Fix #6814 - Notification fadeout
[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     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3593     if (this.registerMenu && this.type != 'treeview')  {
3594         Roo.bootstrap.MenuMgr.register(this);
3595     }
3596     
3597     
3598     this.addEvents({
3599         /**
3600          * @event beforeshow
3601          * Fires before this menu is displayed (return false to block)
3602          * @param {Roo.menu.Menu} this
3603          */
3604         beforeshow : true,
3605         /**
3606          * @event beforehide
3607          * Fires before this menu is hidden (return false to block)
3608          * @param {Roo.menu.Menu} this
3609          */
3610         beforehide : true,
3611         /**
3612          * @event show
3613          * Fires after this menu is displayed
3614          * @param {Roo.menu.Menu} this
3615          */
3616         show : true,
3617         /**
3618          * @event hide
3619          * Fires after this menu is hidden
3620          * @param {Roo.menu.Menu} this
3621          */
3622         hide : true,
3623         /**
3624          * @event click
3625          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3626          * @param {Roo.menu.Menu} this
3627          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3628          * @param {Roo.EventObject} e
3629          */
3630         click : true,
3631         /**
3632          * @event mouseover
3633          * Fires when the mouse is hovering over this menu
3634          * @param {Roo.menu.Menu} this
3635          * @param {Roo.EventObject} e
3636          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3637          */
3638         mouseover : true,
3639         /**
3640          * @event mouseout
3641          * Fires when the mouse exits this menu
3642          * @param {Roo.menu.Menu} this
3643          * @param {Roo.EventObject} e
3644          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3645          */
3646         mouseout : true,
3647         /**
3648          * @event itemclick
3649          * Fires when a menu item contained in this menu is clicked
3650          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3651          * @param {Roo.EventObject} e
3652          */
3653         itemclick: true
3654     });
3655     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3656 };
3657
3658 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3659     
3660    /// html : false,
3661    
3662     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3663     type: false,
3664     /**
3665      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3666      */
3667     registerMenu : true,
3668     
3669     menuItems :false, // stores the menu items..
3670     
3671     hidden:true,
3672         
3673     parentMenu : false,
3674     
3675     stopEvent : true,
3676     
3677     isLink : false,
3678     
3679     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3680     
3681     hideTrigger : false,
3682     
3683     align : 'tl-bl?',
3684     
3685     
3686     getChildContainer : function() {
3687         return this.el;  
3688     },
3689     
3690     getAutoCreate : function(){
3691          
3692         //if (['right'].indexOf(this.align)!==-1) {
3693         //    cfg.cn[1].cls += ' pull-right'
3694         //}
3695          
3696         var cfg = {
3697             tag : 'ul',
3698             cls : 'dropdown-menu shadow' ,
3699             style : 'z-index:1000'
3700             
3701         };
3702         
3703         if (this.type === 'submenu') {
3704             cfg.cls = 'submenu active';
3705         }
3706         if (this.type === 'treeview') {
3707             cfg.cls = 'treeview-menu';
3708         }
3709         
3710         return cfg;
3711     },
3712     initEvents : function() {
3713         
3714        // Roo.log("ADD event");
3715        // Roo.log(this.triggerEl.dom);
3716         
3717         this.triggerEl.on('click', this.onTriggerClick, this);
3718         
3719         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3720         
3721         if (!this.hideTrigger) {
3722             if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3723                 // dropdown toggle on the 'a' in BS4?
3724                 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3725             } else {
3726                 this.triggerEl.addClass('dropdown-toggle');
3727             }
3728         }
3729         if (Roo.isTouch) {
3730             this.el.on('touchstart'  , this.onTouch, this);
3731         }
3732         this.el.on('click' , this.onClick, this);
3733
3734         this.el.on("mouseover", this.onMouseOver, this);
3735         this.el.on("mouseout", this.onMouseOut, this);
3736         
3737     },
3738     
3739     findTargetItem : function(e)
3740     {
3741         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3742         if(!t){
3743             return false;
3744         }
3745         //Roo.log(t);         Roo.log(t.id);
3746         if(t && t.id){
3747             //Roo.log(this.menuitems);
3748             return this.menuitems.get(t.id);
3749             
3750             //return this.items.get(t.menuItemId);
3751         }
3752         
3753         return false;
3754     },
3755     
3756     onTouch : function(e) 
3757     {
3758         Roo.log("menu.onTouch");
3759         //e.stopEvent(); this make the user popdown broken
3760         this.onClick(e);
3761     },
3762     
3763     onClick : function(e)
3764     {
3765         Roo.log("menu.onClick");
3766         
3767         var t = this.findTargetItem(e);
3768         if(!t || t.isContainer){
3769             return;
3770         }
3771         Roo.log(e);
3772         /*
3773         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3774             if(t == this.activeItem && t.shouldDeactivate(e)){
3775                 this.activeItem.deactivate();
3776                 delete this.activeItem;
3777                 return;
3778             }
3779             if(t.canActivate){
3780                 this.setActiveItem(t, true);
3781             }
3782             return;
3783             
3784             
3785         }
3786         */
3787        
3788         Roo.log('pass click event');
3789         
3790         t.onClick(e);
3791         
3792         this.fireEvent("click", this, t, e);
3793         
3794         var _this = this;
3795         
3796         if(!t.href.length || t.href == '#'){
3797             (function() { _this.hide(); }).defer(100);
3798         }
3799         
3800     },
3801     
3802     onMouseOver : function(e){
3803         var t  = this.findTargetItem(e);
3804         //Roo.log(t);
3805         //if(t){
3806         //    if(t.canActivate && !t.disabled){
3807         //        this.setActiveItem(t, true);
3808         //    }
3809         //}
3810         
3811         this.fireEvent("mouseover", this, e, t);
3812     },
3813     isVisible : function(){
3814         return !this.hidden;
3815     },
3816     onMouseOut : function(e){
3817         var t  = this.findTargetItem(e);
3818         
3819         //if(t ){
3820         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3821         //        this.activeItem.deactivate();
3822         //        delete this.activeItem;
3823         //    }
3824         //}
3825         this.fireEvent("mouseout", this, e, t);
3826     },
3827     
3828     
3829     /**
3830      * Displays this menu relative to another element
3831      * @param {String/HTMLElement/Roo.Element} element The element to align to
3832      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3833      * the element (defaults to this.defaultAlign)
3834      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3835      */
3836     show : function(el, pos, parentMenu)
3837     {
3838         if (false === this.fireEvent("beforeshow", this)) {
3839             Roo.log("show canceled");
3840             return;
3841         }
3842         this.parentMenu = parentMenu;
3843         if(!this.el){
3844             this.render();
3845         }
3846         this.el.addClass('show'); // show otherwise we do not know how big we are..
3847          
3848         var xy = this.el.getAlignToXY(el, pos);
3849         
3850         // bl-tl << left align  below
3851         // tl-bl << left align 
3852         
3853         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3854             // if it goes to far to the right.. -> align left.
3855             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3856         }
3857         if(xy[0] < 0){
3858             // was left align - go right?
3859             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3860         }
3861         
3862         // goes down the bottom
3863         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3864            xy[1]  < 0 ){
3865             var a = this.align.replace('?', '').split('-');
3866             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3867             
3868         }
3869         
3870         this.showAt(  xy , parentMenu, false);
3871     },
3872      /**
3873      * Displays this menu at a specific xy position
3874      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3875      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3876      */
3877     showAt : function(xy, parentMenu, /* private: */_e){
3878         this.parentMenu = parentMenu;
3879         if(!this.el){
3880             this.render();
3881         }
3882         if(_e !== false){
3883             this.fireEvent("beforeshow", this);
3884             //xy = this.el.adjustForConstraints(xy);
3885         }
3886         
3887         //this.el.show();
3888         this.hideMenuItems();
3889         this.hidden = false;
3890         this.triggerEl.addClass('open');
3891         this.el.addClass('show');
3892         
3893         
3894         
3895         // reassign x when hitting right
3896         
3897         // reassign y when hitting bottom
3898         
3899         // but the list may align on trigger left or trigger top... should it be a properity?
3900         
3901         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3902             this.el.setXY(xy);
3903         }
3904         
3905         this.focus();
3906         this.fireEvent("show", this);
3907     },
3908     
3909     focus : function(){
3910         return;
3911         if(!this.hidden){
3912             this.doFocus.defer(50, this);
3913         }
3914     },
3915
3916     doFocus : function(){
3917         if(!this.hidden){
3918             this.focusEl.focus();
3919         }
3920     },
3921
3922     /**
3923      * Hides this menu and optionally all parent menus
3924      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3925      */
3926     hide : function(deep)
3927     {
3928         if (false === this.fireEvent("beforehide", this)) {
3929             Roo.log("hide canceled");
3930             return;
3931         }
3932         this.hideMenuItems();
3933         if(this.el && this.isVisible()){
3934            
3935             if(this.activeItem){
3936                 this.activeItem.deactivate();
3937                 this.activeItem = null;
3938             }
3939             this.triggerEl.removeClass('open');;
3940             this.el.removeClass('show');
3941             this.hidden = true;
3942             this.fireEvent("hide", this);
3943         }
3944         if(deep === true && this.parentMenu){
3945             this.parentMenu.hide(true);
3946         }
3947     },
3948     
3949     onTriggerClick : function(e)
3950     {
3951         Roo.log('trigger click');
3952         
3953         var target = e.getTarget();
3954         
3955         Roo.log(target.nodeName.toLowerCase());
3956         
3957         if(target.nodeName.toLowerCase() === 'i'){
3958             e.preventDefault();
3959         }
3960         
3961     },
3962     
3963     onTriggerPress  : function(e)
3964     {
3965         Roo.log('trigger press');
3966         //Roo.log(e.getTarget());
3967        // Roo.log(this.triggerEl.dom);
3968        
3969         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3970         var pel = Roo.get(e.getTarget());
3971         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3972             Roo.log('is treeview or dropdown?');
3973             return;
3974         }
3975         
3976         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3977             return;
3978         }
3979         
3980         if (this.isVisible()) {
3981             Roo.log('hide');
3982             this.hide();
3983         } else {
3984             Roo.log('show');
3985              
3986             this.show(this.triggerEl, this.align, false);
3987         }
3988         
3989         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3990             e.stopEvent();
3991         }
3992         
3993     },
3994        
3995     
3996     hideMenuItems : function()
3997     {
3998         Roo.log("hide Menu Items");
3999         if (!this.el) { 
4000             return;
4001         }
4002         
4003         this.el.select('.open',true).each(function(aa) {
4004             
4005             aa.removeClass('open');
4006          
4007         });
4008     },
4009     addxtypeChild : function (tree, cntr) {
4010         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4011           
4012         this.menuitems.add(comp);
4013         return comp;
4014
4015     },
4016     getEl : function()
4017     {
4018         Roo.log(this.el);
4019         return this.el;
4020     },
4021     
4022     clear : function()
4023     {
4024         this.getEl().dom.innerHTML = '';
4025         this.menuitems.clear();
4026     }
4027 });
4028
4029  
4030  /*
4031  * - LGPL
4032  *
4033  * menu item
4034  * 
4035  */
4036
4037
4038 /**
4039  * @class Roo.bootstrap.MenuItem
4040  * @extends Roo.bootstrap.Component
4041  * Bootstrap MenuItem class
4042  * @cfg {String} html the menu label
4043  * @cfg {String} href the link
4044  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4045  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4046  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4047  * @cfg {String} fa favicon to show on left of menu item.
4048  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4049  * 
4050  * 
4051  * @constructor
4052  * Create a new MenuItem
4053  * @param {Object} config The config object
4054  */
4055
4056
4057 Roo.bootstrap.MenuItem = function(config){
4058     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4059     this.addEvents({
4060         // raw events
4061         /**
4062          * @event click
4063          * The raw click event for the entire grid.
4064          * @param {Roo.bootstrap.MenuItem} this
4065          * @param {Roo.EventObject} e
4066          */
4067         "click" : true
4068     });
4069 };
4070
4071 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4072     
4073     href : false,
4074     html : false,
4075     preventDefault: false,
4076     isContainer : false,
4077     active : false,
4078     fa: false,
4079     
4080     getAutoCreate : function(){
4081         
4082         if(this.isContainer){
4083             return {
4084                 tag: 'li',
4085                 cls: 'dropdown-menu-item '
4086             };
4087         }
4088         var ctag = {
4089             tag: 'span',
4090             html: 'Link'
4091         };
4092         
4093         var anc = {
4094             tag : 'a',
4095             cls : 'dropdown-item',
4096             href : '#',
4097             cn : [  ]
4098         };
4099         
4100         if (this.fa !== false) {
4101             anc.cn.push({
4102                 tag : 'i',
4103                 cls : 'fa fa-' + this.fa
4104             });
4105         }
4106         
4107         anc.cn.push(ctag);
4108         
4109         
4110         var cfg= {
4111             tag: 'li',
4112             cls: 'dropdown-menu-item',
4113             cn: [ anc ]
4114         };
4115         if (this.parent().type == 'treeview') {
4116             cfg.cls = 'treeview-menu';
4117         }
4118         if (this.active) {
4119             cfg.cls += ' active';
4120         }
4121         
4122         
4123         
4124         anc.href = this.href || cfg.cn[0].href ;
4125         ctag.html = this.html || cfg.cn[0].html ;
4126         return cfg;
4127     },
4128     
4129     initEvents: function()
4130     {
4131         if (this.parent().type == 'treeview') {
4132             this.el.select('a').on('click', this.onClick, this);
4133         }
4134         
4135         if (this.menu) {
4136             this.menu.parentType = this.xtype;
4137             this.menu.triggerEl = this.el;
4138             this.menu = this.addxtype(Roo.apply({}, this.menu));
4139         }
4140         
4141     },
4142     onClick : function(e)
4143     {
4144         Roo.log('item on click ');
4145         
4146         if(this.preventDefault){
4147             e.preventDefault();
4148         }
4149         //this.parent().hideMenuItems();
4150         
4151         this.fireEvent('click', this, e);
4152     },
4153     getEl : function()
4154     {
4155         return this.el;
4156     } 
4157 });
4158
4159  
4160
4161  /*
4162  * - LGPL
4163  *
4164  * menu separator
4165  * 
4166  */
4167
4168
4169 /**
4170  * @class Roo.bootstrap.MenuSeparator
4171  * @extends Roo.bootstrap.Component
4172  * Bootstrap MenuSeparator class
4173  * 
4174  * @constructor
4175  * Create a new MenuItem
4176  * @param {Object} config The config object
4177  */
4178
4179
4180 Roo.bootstrap.MenuSeparator = function(config){
4181     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4182 };
4183
4184 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4185     
4186     getAutoCreate : function(){
4187         var cfg = {
4188             cls: 'divider',
4189             tag : 'li'
4190         };
4191         
4192         return cfg;
4193     }
4194    
4195 });
4196
4197  
4198
4199  
4200 /*
4201 * Licence: LGPL
4202 */
4203
4204 /**
4205  * @class Roo.bootstrap.Modal
4206  * @extends Roo.bootstrap.Component
4207  * Bootstrap Modal class
4208  * @cfg {String} title Title of dialog
4209  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4210  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4211  * @cfg {Boolean} specificTitle default false
4212  * @cfg {Array} buttons Array of buttons or standard button set..
4213  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4214  * @cfg {Boolean} animate default true
4215  * @cfg {Boolean} allow_close default true
4216  * @cfg {Boolean} fitwindow default false
4217  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4218  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4219  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4220  * @cfg {String} size (sm|lg|xl) default empty
4221  * @cfg {Number} max_width set the max width of modal
4222  * @cfg {Boolean} editableTitle can the title be edited
4223
4224  *
4225  *
4226  * @constructor
4227  * Create a new Modal Dialog
4228  * @param {Object} config The config object
4229  */
4230
4231 Roo.bootstrap.Modal = function(config){
4232     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4233     this.addEvents({
4234         // raw events
4235         /**
4236          * @event btnclick
4237          * The raw btnclick event for the button
4238          * @param {Roo.EventObject} e
4239          */
4240         "btnclick" : true,
4241         /**
4242          * @event resize
4243          * Fire when dialog resize
4244          * @param {Roo.bootstrap.Modal} this
4245          * @param {Roo.EventObject} e
4246          */
4247         "resize" : true,
4248         /**
4249          * @event titlechanged
4250          * Fire when the editable title has been changed
4251          * @param {Roo.bootstrap.Modal} this
4252          * @param {Roo.EventObject} value
4253          */
4254         "titlechanged" : true 
4255         
4256     });
4257     this.buttons = this.buttons || [];
4258
4259     if (this.tmpl) {
4260         this.tmpl = Roo.factory(this.tmpl);
4261     }
4262
4263 };
4264
4265 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4266
4267     title : 'test dialog',
4268
4269     buttons : false,
4270
4271     // set on load...
4272
4273     html: false,
4274
4275     tmp: false,
4276
4277     specificTitle: false,
4278
4279     buttonPosition: 'right',
4280
4281     allow_close : true,
4282
4283     animate : true,
4284
4285     fitwindow: false,
4286     
4287      // private
4288     dialogEl: false,
4289     bodyEl:  false,
4290     footerEl:  false,
4291     titleEl:  false,
4292     closeEl:  false,
4293
4294     size: '',
4295     
4296     max_width: 0,
4297     
4298     max_height: 0,
4299     
4300     fit_content: false,
4301     editableTitle  : false,
4302
4303     onRender : function(ct, position)
4304     {
4305         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4306
4307         if(!this.el){
4308             var cfg = Roo.apply({},  this.getAutoCreate());
4309             cfg.id = Roo.id();
4310             //if(!cfg.name){
4311             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4312             //}
4313             //if (!cfg.name.length) {
4314             //    delete cfg.name;
4315            // }
4316             if (this.cls) {
4317                 cfg.cls += ' ' + this.cls;
4318             }
4319             if (this.style) {
4320                 cfg.style = this.style;
4321             }
4322             this.el = Roo.get(document.body).createChild(cfg, position);
4323         }
4324         //var type = this.el.dom.type;
4325
4326
4327         if(this.tabIndex !== undefined){
4328             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4329         }
4330
4331         this.dialogEl = this.el.select('.modal-dialog',true).first();
4332         this.bodyEl = this.el.select('.modal-body',true).first();
4333         this.closeEl = this.el.select('.modal-header .close', true).first();
4334         this.headerEl = this.el.select('.modal-header',true).first();
4335         this.titleEl = this.el.select('.modal-title',true).first();
4336         this.footerEl = this.el.select('.modal-footer',true).first();
4337
4338         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4339         
4340         //this.el.addClass("x-dlg-modal");
4341
4342         if (this.buttons.length) {
4343             Roo.each(this.buttons, function(bb) {
4344                 var b = Roo.apply({}, bb);
4345                 b.xns = b.xns || Roo.bootstrap;
4346                 b.xtype = b.xtype || 'Button';
4347                 if (typeof(b.listeners) == 'undefined') {
4348                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4349                 }
4350
4351                 var btn = Roo.factory(b);
4352
4353                 btn.render(this.getButtonContainer());
4354
4355             },this);
4356         }
4357         // render the children.
4358         var nitems = [];
4359
4360         if(typeof(this.items) != 'undefined'){
4361             var items = this.items;
4362             delete this.items;
4363
4364             for(var i =0;i < items.length;i++) {
4365                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4366             }
4367         }
4368
4369         this.items = nitems;
4370
4371         // where are these used - they used to be body/close/footer
4372
4373
4374         this.initEvents();
4375         //this.el.addClass([this.fieldClass, this.cls]);
4376
4377     },
4378
4379     getAutoCreate : function()
4380     {
4381         // we will default to modal-body-overflow - might need to remove or make optional later.
4382         var bdy = {
4383                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4384                 html : this.html || ''
4385         };
4386
4387         var title = {
4388             tag: 'h5',
4389             cls : 'modal-title',
4390             html : this.title
4391         };
4392
4393         if(this.specificTitle){ // WTF is this?
4394             title = this.title;
4395         }
4396
4397         var header = [];
4398         if (this.allow_close && Roo.bootstrap.version == 3) {
4399             header.push({
4400                 tag: 'button',
4401                 cls : 'close',
4402                 html : '&times'
4403             });
4404         }
4405
4406         header.push(title);
4407
4408         if (this.editableTitle) {
4409             header.push({
4410                 cls: 'form-control roo-editable-title d-none',
4411                 tag: 'input',
4412                 type: 'text'
4413             });
4414         }
4415         
4416         if (this.allow_close && Roo.bootstrap.version == 4) {
4417             header.push({
4418                 tag: 'button',
4419                 cls : 'close',
4420                 html : '&times'
4421             });
4422         }
4423         
4424         var size = '';
4425
4426         if(this.size.length){
4427             size = 'modal-' + this.size;
4428         }
4429         
4430         var footer = Roo.bootstrap.version == 3 ?
4431             {
4432                 cls : 'modal-footer',
4433                 cn : [
4434                     {
4435                         tag: 'div',
4436                         cls: 'btn-' + this.buttonPosition
4437                     }
4438                 ]
4439
4440             } :
4441             {  // BS4 uses mr-auto on left buttons....
4442                 cls : 'modal-footer'
4443             };
4444
4445             
4446
4447         
4448         
4449         var modal = {
4450             cls: "modal",
4451              cn : [
4452                 {
4453                     cls: "modal-dialog " + size,
4454                     cn : [
4455                         {
4456                             cls : "modal-content",
4457                             cn : [
4458                                 {
4459                                     cls : 'modal-header',
4460                                     cn : header
4461                                 },
4462                                 bdy,
4463                                 footer
4464                             ]
4465
4466                         }
4467                     ]
4468
4469                 }
4470             ]
4471         };
4472
4473         if(this.animate){
4474             modal.cls += ' fade';
4475         }
4476
4477         return modal;
4478
4479     },
4480     getChildContainer : function() {
4481
4482          return this.bodyEl;
4483
4484     },
4485     getButtonContainer : function() {
4486         
4487          return Roo.bootstrap.version == 4 ?
4488             this.el.select('.modal-footer',true).first()
4489             : this.el.select('.modal-footer div',true).first();
4490
4491     },
4492     initEvents : function()
4493     {
4494         if (this.allow_close) {
4495             this.closeEl.on('click', this.hide, this);
4496         }
4497         Roo.EventManager.onWindowResize(this.resize, this, true);
4498         if (this.editableTitle) {
4499             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4500             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4501             this.headerEditEl.on('keyup', function(e) {
4502                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4503                         this.toggleHeaderInput(false)
4504                     }
4505                 }, this);
4506             this.headerEditEl.on('blur', function(e) {
4507                 this.toggleHeaderInput(false)
4508             },this);
4509         }
4510
4511     },
4512   
4513
4514     resize : function()
4515     {
4516         this.maskEl.setSize(
4517             Roo.lib.Dom.getViewWidth(true),
4518             Roo.lib.Dom.getViewHeight(true)
4519         );
4520         
4521         if (this.fitwindow) {
4522             
4523            this.dialogEl.setStyle( { 'max-width' : '100%' });
4524             this.setSize(
4525                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4526                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4527             );
4528             return;
4529         }
4530         
4531         if(this.max_width !== 0) {
4532             
4533             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4534             
4535             if(this.height) {
4536                 this.setSize(w, this.height);
4537                 return;
4538             }
4539             
4540             if(this.max_height) {
4541                 this.setSize(w,Math.min(
4542                     this.max_height,
4543                     Roo.lib.Dom.getViewportHeight(true) - 60
4544                 ));
4545                 
4546                 return;
4547             }
4548             
4549             if(!this.fit_content) {
4550                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4551                 return;
4552             }
4553             
4554             this.setSize(w, Math.min(
4555                 60 +
4556                 this.headerEl.getHeight() + 
4557                 this.footerEl.getHeight() + 
4558                 this.getChildHeight(this.bodyEl.dom.childNodes),
4559                 Roo.lib.Dom.getViewportHeight(true) - 60)
4560             );
4561         }
4562         
4563     },
4564
4565     setSize : function(w,h)
4566     {
4567         if (!w && !h) {
4568             return;
4569         }
4570         
4571         this.resizeTo(w,h);
4572     },
4573
4574     show : function() {
4575
4576         if (!this.rendered) {
4577             this.render();
4578         }
4579         this.toggleHeaderInput(false);
4580         //this.el.setStyle('display', 'block');
4581         this.el.removeClass('hideing');
4582         this.el.dom.style.display='block';
4583         
4584         Roo.get(document.body).addClass('modal-open');
4585  
4586         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4587             
4588             (function(){
4589                 this.el.addClass('show');
4590                 this.el.addClass('in');
4591             }).defer(50, this);
4592         }else{
4593             this.el.addClass('show');
4594             this.el.addClass('in');
4595         }
4596
4597         // not sure how we can show data in here..
4598         //if (this.tmpl) {
4599         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4600         //}
4601
4602         Roo.get(document.body).addClass("x-body-masked");
4603         
4604         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4605         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4606         this.maskEl.dom.style.display = 'block';
4607         this.maskEl.addClass('show');
4608         
4609         
4610         this.resize();
4611         
4612         this.fireEvent('show', this);
4613
4614         // set zindex here - otherwise it appears to be ignored...
4615         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4616
4617         (function () {
4618             this.items.forEach( function(e) {
4619                 e.layout ? e.layout() : false;
4620
4621             });
4622         }).defer(100,this);
4623
4624     },
4625     hide : function()
4626     {
4627         if(this.fireEvent("beforehide", this) !== false){
4628             
4629             this.maskEl.removeClass('show');
4630             
4631             this.maskEl.dom.style.display = '';
4632             Roo.get(document.body).removeClass("x-body-masked");
4633             this.el.removeClass('in');
4634             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4635
4636             if(this.animate){ // why
4637                 this.el.addClass('hideing');
4638                 this.el.removeClass('show');
4639                 (function(){
4640                     if (!this.el.hasClass('hideing')) {
4641                         return; // it's been shown again...
4642                     }
4643                     
4644                     this.el.dom.style.display='';
4645
4646                     Roo.get(document.body).removeClass('modal-open');
4647                     this.el.removeClass('hideing');
4648                 }).defer(150,this);
4649                 
4650             }else{
4651                 this.el.removeClass('show');
4652                 this.el.dom.style.display='';
4653                 Roo.get(document.body).removeClass('modal-open');
4654
4655             }
4656             this.fireEvent('hide', this);
4657         }
4658     },
4659     isVisible : function()
4660     {
4661         
4662         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4663         
4664     },
4665
4666     addButton : function(str, cb)
4667     {
4668
4669
4670         var b = Roo.apply({}, { html : str } );
4671         b.xns = b.xns || Roo.bootstrap;
4672         b.xtype = b.xtype || 'Button';
4673         if (typeof(b.listeners) == 'undefined') {
4674             b.listeners = { click : cb.createDelegate(this)  };
4675         }
4676
4677         var btn = Roo.factory(b);
4678
4679         btn.render(this.getButtonContainer());
4680
4681         return btn;
4682
4683     },
4684
4685     setDefaultButton : function(btn)
4686     {
4687         //this.el.select('.modal-footer').()
4688     },
4689
4690     resizeTo: function(w,h)
4691     {
4692         this.dialogEl.setWidth(w);
4693         
4694         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4695
4696         this.bodyEl.setHeight(h - diff);
4697         
4698         this.fireEvent('resize', this);
4699     },
4700     
4701     setContentSize  : function(w, h)
4702     {
4703
4704     },
4705     onButtonClick: function(btn,e)
4706     {
4707         //Roo.log([a,b,c]);
4708         this.fireEvent('btnclick', btn.name, e);
4709     },
4710      /**
4711      * Set the title of the Dialog
4712      * @param {String} str new Title
4713      */
4714     setTitle: function(str) {
4715         this.titleEl.dom.innerHTML = str;
4716         this.title = str;
4717     },
4718     /**
4719      * Set the body of the Dialog
4720      * @param {String} str new Title
4721      */
4722     setBody: function(str) {
4723         this.bodyEl.dom.innerHTML = str;
4724     },
4725     /**
4726      * Set the body of the Dialog using the template
4727      * @param {Obj} data - apply this data to the template and replace the body contents.
4728      */
4729     applyBody: function(obj)
4730     {
4731         if (!this.tmpl) {
4732             Roo.log("Error - using apply Body without a template");
4733             //code
4734         }
4735         this.tmpl.overwrite(this.bodyEl, obj);
4736     },
4737     
4738     getChildHeight : function(child_nodes)
4739     {
4740         if(
4741             !child_nodes ||
4742             child_nodes.length == 0
4743         ) {
4744             return 0;
4745         }
4746         
4747         var child_height = 0;
4748         
4749         for(var i = 0; i < child_nodes.length; i++) {
4750             
4751             /*
4752             * for modal with tabs...
4753             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4754                 
4755                 var layout_childs = child_nodes[i].childNodes;
4756                 
4757                 for(var j = 0; j < layout_childs.length; j++) {
4758                     
4759                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4760                         
4761                         var layout_body_childs = layout_childs[j].childNodes;
4762                         
4763                         for(var k = 0; k < layout_body_childs.length; k++) {
4764                             
4765                             if(layout_body_childs[k].classList.contains('navbar')) {
4766                                 child_height += layout_body_childs[k].offsetHeight;
4767                                 continue;
4768                             }
4769                             
4770                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4771                                 
4772                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4773                                 
4774                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4775                                     
4776                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4777                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4778                                         continue;
4779                                     }
4780                                     
4781                                 }
4782                                 
4783                             }
4784                             
4785                         }
4786                     }
4787                 }
4788                 continue;
4789             }
4790             */
4791             
4792             child_height += child_nodes[i].offsetHeight;
4793             // Roo.log(child_nodes[i].offsetHeight);
4794         }
4795         
4796         return child_height;
4797     },
4798     toggleHeaderInput : function(is_edit)
4799     {
4800         if (!this.editableTitle) {
4801             return; // not editable.
4802         }
4803         if (is_edit && this.is_header_editing) {
4804             return; // already editing..
4805         }
4806         if (is_edit) {
4807     
4808             this.headerEditEl.dom.value = this.title;
4809             this.headerEditEl.removeClass('d-none');
4810             this.headerEditEl.dom.focus();
4811             this.titleEl.addClass('d-none');
4812             
4813             this.is_header_editing = true;
4814             return
4815         }
4816         // flip back to not editing.
4817         this.title = this.headerEditEl.dom.value;
4818         this.headerEditEl.addClass('d-none');
4819         this.titleEl.removeClass('d-none');
4820         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4821         this.is_header_editing = false;
4822         this.fireEvent('titlechanged', this, this.title);
4823     
4824             
4825         
4826     }
4827
4828 });
4829
4830
4831 Roo.apply(Roo.bootstrap.Modal,  {
4832     /**
4833          * Button config that displays a single OK button
4834          * @type Object
4835          */
4836         OK :  [{
4837             name : 'ok',
4838             weight : 'primary',
4839             html : 'OK'
4840         }],
4841         /**
4842          * Button config that displays Yes and No buttons
4843          * @type Object
4844          */
4845         YESNO : [
4846             {
4847                 name  : 'no',
4848                 html : 'No'
4849             },
4850             {
4851                 name  :'yes',
4852                 weight : 'primary',
4853                 html : 'Yes'
4854             }
4855         ],
4856
4857         /**
4858          * Button config that displays OK and Cancel buttons
4859          * @type Object
4860          */
4861         OKCANCEL : [
4862             {
4863                name : 'cancel',
4864                 html : 'Cancel'
4865             },
4866             {
4867                 name : 'ok',
4868                 weight : 'primary',
4869                 html : 'OK'
4870             }
4871         ],
4872         /**
4873          * Button config that displays Yes, No and Cancel buttons
4874          * @type Object
4875          */
4876         YESNOCANCEL : [
4877             {
4878                 name : 'yes',
4879                 weight : 'primary',
4880                 html : 'Yes'
4881             },
4882             {
4883                 name : 'no',
4884                 html : 'No'
4885             },
4886             {
4887                 name : 'cancel',
4888                 html : 'Cancel'
4889             }
4890         ],
4891         
4892         zIndex : 10001
4893 });
4894
4895 /*
4896  * - LGPL
4897  *
4898  * messagebox - can be used as a replace
4899  * 
4900  */
4901 /**
4902  * @class Roo.MessageBox
4903  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4904  * Example usage:
4905  *<pre><code>
4906 // Basic alert:
4907 Roo.Msg.alert('Status', 'Changes saved successfully.');
4908
4909 // Prompt for user data:
4910 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4911     if (btn == 'ok'){
4912         // process text value...
4913     }
4914 });
4915
4916 // Show a dialog using config options:
4917 Roo.Msg.show({
4918    title:'Save Changes?',
4919    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4920    buttons: Roo.Msg.YESNOCANCEL,
4921    fn: processResult,
4922    animEl: 'elId'
4923 });
4924 </code></pre>
4925  * @singleton
4926  */
4927 Roo.bootstrap.MessageBox = function(){
4928     var dlg, opt, mask, waitTimer;
4929     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4930     var buttons, activeTextEl, bwidth;
4931
4932     
4933     // private
4934     var handleButton = function(button){
4935         dlg.hide();
4936         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4937     };
4938
4939     // private
4940     var handleHide = function(){
4941         if(opt && opt.cls){
4942             dlg.el.removeClass(opt.cls);
4943         }
4944         //if(waitTimer){
4945         //    Roo.TaskMgr.stop(waitTimer);
4946         //    waitTimer = null;
4947         //}
4948     };
4949
4950     // private
4951     var updateButtons = function(b){
4952         var width = 0;
4953         if(!b){
4954             buttons["ok"].hide();
4955             buttons["cancel"].hide();
4956             buttons["yes"].hide();
4957             buttons["no"].hide();
4958             dlg.footerEl.hide();
4959             
4960             return width;
4961         }
4962         dlg.footerEl.show();
4963         for(var k in buttons){
4964             if(typeof buttons[k] != "function"){
4965                 if(b[k]){
4966                     buttons[k].show();
4967                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4968                     width += buttons[k].el.getWidth()+15;
4969                 }else{
4970                     buttons[k].hide();
4971                 }
4972             }
4973         }
4974         return width;
4975     };
4976
4977     // private
4978     var handleEsc = function(d, k, e){
4979         if(opt && opt.closable !== false){
4980             dlg.hide();
4981         }
4982         if(e){
4983             e.stopEvent();
4984         }
4985     };
4986
4987     return {
4988         /**
4989          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4990          * @return {Roo.BasicDialog} The BasicDialog element
4991          */
4992         getDialog : function(){
4993            if(!dlg){
4994                 dlg = new Roo.bootstrap.Modal( {
4995                     //draggable: true,
4996                     //resizable:false,
4997                     //constraintoviewport:false,
4998                     //fixedcenter:true,
4999                     //collapsible : false,
5000                     //shim:true,
5001                     //modal: true,
5002                 //    width: 'auto',
5003                   //  height:100,
5004                     //buttonAlign:"center",
5005                     closeClick : function(){
5006                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5007                             handleButton("no");
5008                         }else{
5009                             handleButton("cancel");
5010                         }
5011                     }
5012                 });
5013                 dlg.render();
5014                 dlg.on("hide", handleHide);
5015                 mask = dlg.mask;
5016                 //dlg.addKeyListener(27, handleEsc);
5017                 buttons = {};
5018                 this.buttons = buttons;
5019                 var bt = this.buttonText;
5020                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5021                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5022                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5023                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5024                 //Roo.log(buttons);
5025                 bodyEl = dlg.bodyEl.createChild({
5026
5027                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5028                         '<textarea class="roo-mb-textarea"></textarea>' +
5029                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5030                 });
5031                 msgEl = bodyEl.dom.firstChild;
5032                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5033                 textboxEl.enableDisplayMode();
5034                 textboxEl.addKeyListener([10,13], function(){
5035                     if(dlg.isVisible() && opt && opt.buttons){
5036                         if(opt.buttons.ok){
5037                             handleButton("ok");
5038                         }else if(opt.buttons.yes){
5039                             handleButton("yes");
5040                         }
5041                     }
5042                 });
5043                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5044                 textareaEl.enableDisplayMode();
5045                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5046                 progressEl.enableDisplayMode();
5047                 
5048                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5049                 var pf = progressEl.dom.firstChild;
5050                 if (pf) {
5051                     pp = Roo.get(pf.firstChild);
5052                     pp.setHeight(pf.offsetHeight);
5053                 }
5054                 
5055             }
5056             return dlg;
5057         },
5058
5059         /**
5060          * Updates the message box body text
5061          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5062          * the XHTML-compliant non-breaking space character '&amp;#160;')
5063          * @return {Roo.MessageBox} This message box
5064          */
5065         updateText : function(text)
5066         {
5067             if(!dlg.isVisible() && !opt.width){
5068                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5069                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5070             }
5071             msgEl.innerHTML = text || '&#160;';
5072       
5073             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5074             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5075             var w = Math.max(
5076                     Math.min(opt.width || cw , this.maxWidth), 
5077                     Math.max(opt.minWidth || this.minWidth, bwidth)
5078             );
5079             if(opt.prompt){
5080                 activeTextEl.setWidth(w);
5081             }
5082             if(dlg.isVisible()){
5083                 dlg.fixedcenter = false;
5084             }
5085             // to big, make it scroll. = But as usual stupid IE does not support
5086             // !important..
5087             
5088             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5089                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5090                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5091             } else {
5092                 bodyEl.dom.style.height = '';
5093                 bodyEl.dom.style.overflowY = '';
5094             }
5095             if (cw > w) {
5096                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5097             } else {
5098                 bodyEl.dom.style.overflowX = '';
5099             }
5100             
5101             dlg.setContentSize(w, bodyEl.getHeight());
5102             if(dlg.isVisible()){
5103                 dlg.fixedcenter = true;
5104             }
5105             return this;
5106         },
5107
5108         /**
5109          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5110          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5111          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5112          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5113          * @return {Roo.MessageBox} This message box
5114          */
5115         updateProgress : function(value, text){
5116             if(text){
5117                 this.updateText(text);
5118             }
5119             
5120             if (pp) { // weird bug on my firefox - for some reason this is not defined
5121                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5122                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5123             }
5124             return this;
5125         },        
5126
5127         /**
5128          * Returns true if the message box is currently displayed
5129          * @return {Boolean} True if the message box is visible, else false
5130          */
5131         isVisible : function(){
5132             return dlg && dlg.isVisible();  
5133         },
5134
5135         /**
5136          * Hides the message box if it is displayed
5137          */
5138         hide : function(){
5139             if(this.isVisible()){
5140                 dlg.hide();
5141             }  
5142         },
5143
5144         /**
5145          * Displays a new message box, or reinitializes an existing message box, based on the config options
5146          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5147          * The following config object properties are supported:
5148          * <pre>
5149 Property    Type             Description
5150 ----------  ---------------  ------------------------------------------------------------------------------------
5151 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5152                                    closes (defaults to undefined)
5153 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5154                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5155 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5156                                    progress and wait dialogs will ignore this property and always hide the
5157                                    close button as they can only be closed programmatically.
5158 cls               String           A custom CSS class to apply to the message box element
5159 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5160                                    displayed (defaults to 75)
5161 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5162                                    function will be btn (the name of the button that was clicked, if applicable,
5163                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5164                                    Progress and wait dialogs will ignore this option since they do not respond to
5165                                    user actions and can only be closed programmatically, so any required function
5166                                    should be called by the same code after it closes the dialog.
5167 icon              String           A CSS class that provides a background image to be used as an icon for
5168                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5169 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5170 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5171 modal             Boolean          False to allow user interaction with the page while the message box is
5172                                    displayed (defaults to true)
5173 msg               String           A string that will replace the existing message box body text (defaults
5174                                    to the XHTML-compliant non-breaking space character '&#160;')
5175 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5176 progress          Boolean          True to display a progress bar (defaults to false)
5177 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5178 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5179 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5180 title             String           The title text
5181 value             String           The string value to set into the active textbox element if displayed
5182 wait              Boolean          True to display a progress bar (defaults to false)
5183 width             Number           The width of the dialog in pixels
5184 </pre>
5185          *
5186          * Example usage:
5187          * <pre><code>
5188 Roo.Msg.show({
5189    title: 'Address',
5190    msg: 'Please enter your address:',
5191    width: 300,
5192    buttons: Roo.MessageBox.OKCANCEL,
5193    multiline: true,
5194    fn: saveAddress,
5195    animEl: 'addAddressBtn'
5196 });
5197 </code></pre>
5198          * @param {Object} config Configuration options
5199          * @return {Roo.MessageBox} This message box
5200          */
5201         show : function(options)
5202         {
5203             
5204             // this causes nightmares if you show one dialog after another
5205             // especially on callbacks..
5206              
5207             if(this.isVisible()){
5208                 
5209                 this.hide();
5210                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5211                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5212                 Roo.log("New Dialog Message:" +  options.msg )
5213                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5214                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5215                 
5216             }
5217             var d = this.getDialog();
5218             opt = options;
5219             d.setTitle(opt.title || "&#160;");
5220             d.closeEl.setDisplayed(opt.closable !== false);
5221             activeTextEl = textboxEl;
5222             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5223             if(opt.prompt){
5224                 if(opt.multiline){
5225                     textboxEl.hide();
5226                     textareaEl.show();
5227                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5228                         opt.multiline : this.defaultTextHeight);
5229                     activeTextEl = textareaEl;
5230                 }else{
5231                     textboxEl.show();
5232                     textareaEl.hide();
5233                 }
5234             }else{
5235                 textboxEl.hide();
5236                 textareaEl.hide();
5237             }
5238             progressEl.setDisplayed(opt.progress === true);
5239             if (opt.progress) {
5240                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5241             }
5242             this.updateProgress(0);
5243             activeTextEl.dom.value = opt.value || "";
5244             if(opt.prompt){
5245                 dlg.setDefaultButton(activeTextEl);
5246             }else{
5247                 var bs = opt.buttons;
5248                 var db = null;
5249                 if(bs && bs.ok){
5250                     db = buttons["ok"];
5251                 }else if(bs && bs.yes){
5252                     db = buttons["yes"];
5253                 }
5254                 dlg.setDefaultButton(db);
5255             }
5256             bwidth = updateButtons(opt.buttons);
5257             this.updateText(opt.msg);
5258             if(opt.cls){
5259                 d.el.addClass(opt.cls);
5260             }
5261             d.proxyDrag = opt.proxyDrag === true;
5262             d.modal = opt.modal !== false;
5263             d.mask = opt.modal !== false ? mask : false;
5264             if(!d.isVisible()){
5265                 // force it to the end of the z-index stack so it gets a cursor in FF
5266                 document.body.appendChild(dlg.el.dom);
5267                 d.animateTarget = null;
5268                 d.show(options.animEl);
5269             }
5270             return this;
5271         },
5272
5273         /**
5274          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5275          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5276          * and closing the message box when the process is complete.
5277          * @param {String} title The title bar text
5278          * @param {String} msg The message box body text
5279          * @return {Roo.MessageBox} This message box
5280          */
5281         progress : function(title, msg){
5282             this.show({
5283                 title : title,
5284                 msg : msg,
5285                 buttons: false,
5286                 progress:true,
5287                 closable:false,
5288                 minWidth: this.minProgressWidth,
5289                 modal : true
5290             });
5291             return this;
5292         },
5293
5294         /**
5295          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5296          * If a callback function is passed it will be called after the user clicks the button, and the
5297          * id of the button that was clicked will be passed as the only parameter to the callback
5298          * (could also be the top-right close button).
5299          * @param {String} title The title bar text
5300          * @param {String} msg The message box body text
5301          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5302          * @param {Object} scope (optional) The scope of the callback function
5303          * @return {Roo.MessageBox} This message box
5304          */
5305         alert : function(title, msg, fn, scope)
5306         {
5307             this.show({
5308                 title : title,
5309                 msg : msg,
5310                 buttons: this.OK,
5311                 fn: fn,
5312                 closable : false,
5313                 scope : scope,
5314                 modal : true
5315             });
5316             return this;
5317         },
5318
5319         /**
5320          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5321          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5322          * You are responsible for closing the message box when the process is complete.
5323          * @param {String} msg The message box body text
5324          * @param {String} title (optional) The title bar text
5325          * @return {Roo.MessageBox} This message box
5326          */
5327         wait : function(msg, title){
5328             this.show({
5329                 title : title,
5330                 msg : msg,
5331                 buttons: false,
5332                 closable:false,
5333                 progress:true,
5334                 modal:true,
5335                 width:300,
5336                 wait:true
5337             });
5338             waitTimer = Roo.TaskMgr.start({
5339                 run: function(i){
5340                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5341                 },
5342                 interval: 1000
5343             });
5344             return this;
5345         },
5346
5347         /**
5348          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5349          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5350          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5351          * @param {String} title The title bar text
5352          * @param {String} msg The message box body text
5353          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5354          * @param {Object} scope (optional) The scope of the callback function
5355          * @return {Roo.MessageBox} This message box
5356          */
5357         confirm : function(title, msg, fn, scope){
5358             this.show({
5359                 title : title,
5360                 msg : msg,
5361                 buttons: this.YESNO,
5362                 fn: fn,
5363                 scope : scope,
5364                 modal : true
5365             });
5366             return this;
5367         },
5368
5369         /**
5370          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5371          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5372          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5373          * (could also be the top-right close button) and the text that was entered will be passed as the two
5374          * parameters to the callback.
5375          * @param {String} title The title bar text
5376          * @param {String} msg The message box body text
5377          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5378          * @param {Object} scope (optional) The scope of the callback function
5379          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5380          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5381          * @return {Roo.MessageBox} This message box
5382          */
5383         prompt : function(title, msg, fn, scope, multiline){
5384             this.show({
5385                 title : title,
5386                 msg : msg,
5387                 buttons: this.OKCANCEL,
5388                 fn: fn,
5389                 minWidth:250,
5390                 scope : scope,
5391                 prompt:true,
5392                 multiline: multiline,
5393                 modal : true
5394             });
5395             return this;
5396         },
5397
5398         /**
5399          * Button config that displays a single OK button
5400          * @type Object
5401          */
5402         OK : {ok:true},
5403         /**
5404          * Button config that displays Yes and No buttons
5405          * @type Object
5406          */
5407         YESNO : {yes:true, no:true},
5408         /**
5409          * Button config that displays OK and Cancel buttons
5410          * @type Object
5411          */
5412         OKCANCEL : {ok:true, cancel:true},
5413         /**
5414          * Button config that displays Yes, No and Cancel buttons
5415          * @type Object
5416          */
5417         YESNOCANCEL : {yes:true, no:true, cancel:true},
5418
5419         /**
5420          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5421          * @type Number
5422          */
5423         defaultTextHeight : 75,
5424         /**
5425          * The maximum width in pixels of the message box (defaults to 600)
5426          * @type Number
5427          */
5428         maxWidth : 600,
5429         /**
5430          * The minimum width in pixels of the message box (defaults to 100)
5431          * @type Number
5432          */
5433         minWidth : 100,
5434         /**
5435          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5436          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5437          * @type Number
5438          */
5439         minProgressWidth : 250,
5440         /**
5441          * An object containing the default button text strings that can be overriden for localized language support.
5442          * Supported properties are: ok, cancel, yes and no.
5443          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5444          * @type Object
5445          */
5446         buttonText : {
5447             ok : "OK",
5448             cancel : "Cancel",
5449             yes : "Yes",
5450             no : "No"
5451         }
5452     };
5453 }();
5454
5455 /**
5456  * Shorthand for {@link Roo.MessageBox}
5457  */
5458 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5459 Roo.Msg = Roo.Msg || Roo.MessageBox;
5460 /*
5461  * - LGPL
5462  *
5463  * navbar
5464  * 
5465  */
5466
5467 /**
5468  * @class Roo.bootstrap.Navbar
5469  * @extends Roo.bootstrap.Component
5470  * Bootstrap Navbar class
5471
5472  * @constructor
5473  * Create a new Navbar
5474  * @param {Object} config The config object
5475  */
5476
5477
5478 Roo.bootstrap.Navbar = function(config){
5479     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5480     this.addEvents({
5481         // raw events
5482         /**
5483          * @event beforetoggle
5484          * Fire before toggle the menu
5485          * @param {Roo.EventObject} e
5486          */
5487         "beforetoggle" : true
5488     });
5489 };
5490
5491 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5492     
5493     
5494    
5495     // private
5496     navItems : false,
5497     loadMask : false,
5498     
5499     
5500     getAutoCreate : function(){
5501         
5502         
5503         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5504         
5505     },
5506     
5507     initEvents :function ()
5508     {
5509         //Roo.log(this.el.select('.navbar-toggle',true));
5510         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5511         
5512         var mark = {
5513             tag: "div",
5514             cls:"x-dlg-mask"
5515         };
5516         
5517         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5518         
5519         var size = this.el.getSize();
5520         this.maskEl.setSize(size.width, size.height);
5521         this.maskEl.enableDisplayMode("block");
5522         this.maskEl.hide();
5523         
5524         if(this.loadMask){
5525             this.maskEl.show();
5526         }
5527     },
5528     
5529     
5530     getChildContainer : function()
5531     {
5532         if (this.el && this.el.select('.collapse').getCount()) {
5533             return this.el.select('.collapse',true).first();
5534         }
5535         
5536         return this.el;
5537     },
5538     
5539     mask : function()
5540     {
5541         this.maskEl.show();
5542     },
5543     
5544     unmask : function()
5545     {
5546         this.maskEl.hide();
5547     },
5548     onToggle : function()
5549     {
5550         
5551         if(this.fireEvent('beforetoggle', this) === false){
5552             return;
5553         }
5554         var ce = this.el.select('.navbar-collapse',true).first();
5555       
5556         if (!ce.hasClass('show')) {
5557            this.expand();
5558         } else {
5559             this.collapse();
5560         }
5561         
5562         
5563     
5564     },
5565     /**
5566      * Expand the navbar pulldown 
5567      */
5568     expand : function ()
5569     {
5570        
5571         var ce = this.el.select('.navbar-collapse',true).first();
5572         if (ce.hasClass('collapsing')) {
5573             return;
5574         }
5575         ce.dom.style.height = '';
5576                // show it...
5577         ce.addClass('in'); // old...
5578         ce.removeClass('collapse');
5579         ce.addClass('show');
5580         var h = ce.getHeight();
5581         Roo.log(h);
5582         ce.removeClass('show');
5583         // at this point we should be able to see it..
5584         ce.addClass('collapsing');
5585         
5586         ce.setHeight(0); // resize it ...
5587         ce.on('transitionend', function() {
5588             //Roo.log('done transition');
5589             ce.removeClass('collapsing');
5590             ce.addClass('show');
5591             ce.removeClass('collapse');
5592
5593             ce.dom.style.height = '';
5594         }, this, { single: true} );
5595         ce.setHeight(h);
5596         ce.dom.scrollTop = 0;
5597     },
5598     /**
5599      * Collapse the navbar pulldown 
5600      */
5601     collapse : function()
5602     {
5603          var ce = this.el.select('.navbar-collapse',true).first();
5604        
5605         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5606             // it's collapsed or collapsing..
5607             return;
5608         }
5609         ce.removeClass('in'); // old...
5610         ce.setHeight(ce.getHeight());
5611         ce.removeClass('show');
5612         ce.addClass('collapsing');
5613         
5614         ce.on('transitionend', function() {
5615             ce.dom.style.height = '';
5616             ce.removeClass('collapsing');
5617             ce.addClass('collapse');
5618         }, this, { single: true} );
5619         ce.setHeight(0);
5620     }
5621     
5622     
5623     
5624 });
5625
5626
5627
5628  
5629
5630  /*
5631  * - LGPL
5632  *
5633  * navbar
5634  * 
5635  */
5636
5637 /**
5638  * @class Roo.bootstrap.NavSimplebar
5639  * @extends Roo.bootstrap.Navbar
5640  * Bootstrap Sidebar class
5641  *
5642  * @cfg {Boolean} inverse is inverted color
5643  * 
5644  * @cfg {String} type (nav | pills | tabs)
5645  * @cfg {Boolean} arrangement stacked | justified
5646  * @cfg {String} align (left | right) alignment
5647  * 
5648  * @cfg {Boolean} main (true|false) main nav bar? default false
5649  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5650  * 
5651  * @cfg {String} tag (header|footer|nav|div) default is nav 
5652
5653  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5654  * 
5655  * 
5656  * @constructor
5657  * Create a new Sidebar
5658  * @param {Object} config The config object
5659  */
5660
5661
5662 Roo.bootstrap.NavSimplebar = function(config){
5663     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5664 };
5665
5666 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5667     
5668     inverse: false,
5669     
5670     type: false,
5671     arrangement: '',
5672     align : false,
5673     
5674     weight : 'light',
5675     
5676     main : false,
5677     
5678     
5679     tag : false,
5680     
5681     
5682     getAutoCreate : function(){
5683         
5684         
5685         var cfg = {
5686             tag : this.tag || 'div',
5687             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5688         };
5689         if (['light','white'].indexOf(this.weight) > -1) {
5690             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5691         }
5692         cfg.cls += ' bg-' + this.weight;
5693         
5694         if (this.inverse) {
5695             cfg.cls += ' navbar-inverse';
5696             
5697         }
5698         
5699         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5700         
5701         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5702             return cfg;
5703         }
5704         
5705         
5706     
5707         
5708         cfg.cn = [
5709             {
5710                 cls: 'nav nav-' + this.xtype,
5711                 tag : 'ul'
5712             }
5713         ];
5714         
5715          
5716         this.type = this.type || 'nav';
5717         if (['tabs','pills'].indexOf(this.type) != -1) {
5718             cfg.cn[0].cls += ' nav-' + this.type
5719         
5720         
5721         } else {
5722             if (this.type!=='nav') {
5723                 Roo.log('nav type must be nav/tabs/pills')
5724             }
5725             cfg.cn[0].cls += ' navbar-nav'
5726         }
5727         
5728         
5729         
5730         
5731         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5732             cfg.cn[0].cls += ' nav-' + this.arrangement;
5733         }
5734         
5735         
5736         if (this.align === 'right') {
5737             cfg.cn[0].cls += ' navbar-right';
5738         }
5739         
5740         
5741         
5742         
5743         return cfg;
5744     
5745         
5746     }
5747     
5748     
5749     
5750 });
5751
5752
5753
5754  
5755
5756  
5757        /*
5758  * - LGPL
5759  *
5760  * navbar
5761  * navbar-fixed-top
5762  * navbar-expand-md  fixed-top 
5763  */
5764
5765 /**
5766  * @class Roo.bootstrap.NavHeaderbar
5767  * @extends Roo.bootstrap.NavSimplebar
5768  * Bootstrap Sidebar class
5769  *
5770  * @cfg {String} brand what is brand
5771  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5772  * @cfg {String} brand_href href of the brand
5773  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5774  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5775  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5776  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5777  * 
5778  * @constructor
5779  * Create a new Sidebar
5780  * @param {Object} config The config object
5781  */
5782
5783
5784 Roo.bootstrap.NavHeaderbar = function(config){
5785     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5786       
5787 };
5788
5789 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5790     
5791     position: '',
5792     brand: '',
5793     brand_href: false,
5794     srButton : true,
5795     autohide : false,
5796     desktopCenter : false,
5797    
5798     
5799     getAutoCreate : function(){
5800         
5801         var   cfg = {
5802             tag: this.nav || 'nav',
5803             cls: 'navbar navbar-expand-md',
5804             role: 'navigation',
5805             cn: []
5806         };
5807         
5808         var cn = cfg.cn;
5809         if (this.desktopCenter) {
5810             cn.push({cls : 'container', cn : []});
5811             cn = cn[0].cn;
5812         }
5813         
5814         if(this.srButton){
5815             var btn = {
5816                 tag: 'button',
5817                 type: 'button',
5818                 cls: 'navbar-toggle navbar-toggler',
5819                 'data-toggle': 'collapse',
5820                 cn: [
5821                     {
5822                         tag: 'span',
5823                         cls: 'sr-only',
5824                         html: 'Toggle navigation'
5825                     },
5826                     {
5827                         tag: 'span',
5828                         cls: 'icon-bar navbar-toggler-icon'
5829                     },
5830                     {
5831                         tag: 'span',
5832                         cls: 'icon-bar'
5833                     },
5834                     {
5835                         tag: 'span',
5836                         cls: 'icon-bar'
5837                     }
5838                 ]
5839             };
5840             
5841             cn.push( Roo.bootstrap.version == 4 ? btn : {
5842                 tag: 'div',
5843                 cls: 'navbar-header',
5844                 cn: [
5845                     btn
5846                 ]
5847             });
5848         }
5849         
5850         cn.push({
5851             tag: 'div',
5852             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5853             cn : []
5854         });
5855         
5856         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5857         
5858         if (['light','white'].indexOf(this.weight) > -1) {
5859             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5860         }
5861         cfg.cls += ' bg-' + this.weight;
5862         
5863         
5864         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5865             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5866             
5867             // tag can override this..
5868             
5869             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5870         }
5871         
5872         if (this.brand !== '') {
5873             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5874             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5875                 tag: 'a',
5876                 href: this.brand_href ? this.brand_href : '#',
5877                 cls: 'navbar-brand',
5878                 cn: [
5879                 this.brand
5880                 ]
5881             });
5882         }
5883         
5884         if(this.main){
5885             cfg.cls += ' main-nav';
5886         }
5887         
5888         
5889         return cfg;
5890
5891         
5892     },
5893     getHeaderChildContainer : function()
5894     {
5895         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5896             return this.el.select('.navbar-header',true).first();
5897         }
5898         
5899         return this.getChildContainer();
5900     },
5901     
5902     getChildContainer : function()
5903     {
5904          
5905         return this.el.select('.roo-navbar-collapse',true).first();
5906          
5907         
5908     },
5909     
5910     initEvents : function()
5911     {
5912         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5913         
5914         if (this.autohide) {
5915             
5916             var prevScroll = 0;
5917             var ft = this.el;
5918             
5919             Roo.get(document).on('scroll',function(e) {
5920                 var ns = Roo.get(document).getScroll().top;
5921                 var os = prevScroll;
5922                 prevScroll = ns;
5923                 
5924                 if(ns > os){
5925                     ft.removeClass('slideDown');
5926                     ft.addClass('slideUp');
5927                     return;
5928                 }
5929                 ft.removeClass('slideUp');
5930                 ft.addClass('slideDown');
5931                  
5932               
5933           },this);
5934         }
5935     }    
5936     
5937 });
5938
5939
5940
5941  
5942
5943  /*
5944  * - LGPL
5945  *
5946  * navbar
5947  * 
5948  */
5949
5950 /**
5951  * @class Roo.bootstrap.NavSidebar
5952  * @extends Roo.bootstrap.Navbar
5953  * Bootstrap Sidebar class
5954  * 
5955  * @constructor
5956  * Create a new Sidebar
5957  * @param {Object} config The config object
5958  */
5959
5960
5961 Roo.bootstrap.NavSidebar = function(config){
5962     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5963 };
5964
5965 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5966     
5967     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5968     
5969     getAutoCreate : function(){
5970         
5971         
5972         return  {
5973             tag: 'div',
5974             cls: 'sidebar sidebar-nav'
5975         };
5976     
5977         
5978     }
5979     
5980     
5981     
5982 });
5983
5984
5985
5986  
5987
5988  /*
5989  * - LGPL
5990  *
5991  * nav group
5992  * 
5993  */
5994
5995 /**
5996  * @class Roo.bootstrap.NavGroup
5997  * @extends Roo.bootstrap.Component
5998  * Bootstrap NavGroup class
5999  * @cfg {String} align (left|right)
6000  * @cfg {Boolean} inverse
6001  * @cfg {String} type (nav|pills|tab) default nav
6002  * @cfg {String} navId - reference Id for navbar.
6003  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6004  * 
6005  * @constructor
6006  * Create a new nav group
6007  * @param {Object} config The config object
6008  */
6009
6010 Roo.bootstrap.NavGroup = function(config){
6011     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6012     this.navItems = [];
6013    
6014     Roo.bootstrap.NavGroup.register(this);
6015      this.addEvents({
6016         /**
6017              * @event changed
6018              * Fires when the active item changes
6019              * @param {Roo.bootstrap.NavGroup} this
6020              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6021              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6022          */
6023         'changed': true
6024      });
6025     
6026 };
6027
6028 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6029     
6030     align: '',
6031     inverse: false,
6032     form: false,
6033     type: 'nav',
6034     navId : '',
6035     // private
6036     pilltype : true,
6037     
6038     navItems : false, 
6039     
6040     getAutoCreate : function()
6041     {
6042         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6043         
6044         cfg = {
6045             tag : 'ul',
6046             cls: 'nav' 
6047         };
6048         if (Roo.bootstrap.version == 4) {
6049             if (['tabs','pills'].indexOf(this.type) != -1) {
6050                 cfg.cls += ' nav-' + this.type; 
6051             } else {
6052                 // trying to remove so header bar can right align top?
6053                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6054                     // do not use on header bar... 
6055                     cfg.cls += ' navbar-nav';
6056                 }
6057             }
6058             
6059         } else {
6060             if (['tabs','pills'].indexOf(this.type) != -1) {
6061                 cfg.cls += ' nav-' + this.type
6062             } else {
6063                 if (this.type !== 'nav') {
6064                     Roo.log('nav type must be nav/tabs/pills')
6065                 }
6066                 cfg.cls += ' navbar-nav'
6067             }
6068         }
6069         
6070         if (this.parent() && this.parent().sidebar) {
6071             cfg = {
6072                 tag: 'ul',
6073                 cls: 'dashboard-menu sidebar-menu'
6074             };
6075             
6076             return cfg;
6077         }
6078         
6079         if (this.form === true) {
6080             cfg = {
6081                 tag: 'form',
6082                 cls: 'navbar-form form-inline'
6083             };
6084             //nav navbar-right ml-md-auto
6085             if (this.align === 'right') {
6086                 cfg.cls += ' navbar-right ml-md-auto';
6087             } else {
6088                 cfg.cls += ' navbar-left';
6089             }
6090         }
6091         
6092         if (this.align === 'right') {
6093             cfg.cls += ' navbar-right ml-md-auto';
6094         } else {
6095             cfg.cls += ' mr-auto';
6096         }
6097         
6098         if (this.inverse) {
6099             cfg.cls += ' navbar-inverse';
6100             
6101         }
6102         
6103         
6104         return cfg;
6105     },
6106     /**
6107     * sets the active Navigation item
6108     * @param {Roo.bootstrap.NavItem} the new current navitem
6109     */
6110     setActiveItem : function(item)
6111     {
6112         var prev = false;
6113         Roo.each(this.navItems, function(v){
6114             if (v == item) {
6115                 return ;
6116             }
6117             if (v.isActive()) {
6118                 v.setActive(false, true);
6119                 prev = v;
6120                 
6121             }
6122             
6123         });
6124
6125         item.setActive(true, true);
6126         this.fireEvent('changed', this, item, prev);
6127         
6128         
6129     },
6130     /**
6131     * gets the active Navigation item
6132     * @return {Roo.bootstrap.NavItem} the current navitem
6133     */
6134     getActive : function()
6135     {
6136         
6137         var prev = false;
6138         Roo.each(this.navItems, function(v){
6139             
6140             if (v.isActive()) {
6141                 prev = v;
6142                 
6143             }
6144             
6145         });
6146         return prev;
6147     },
6148     
6149     indexOfNav : function()
6150     {
6151         
6152         var prev = false;
6153         Roo.each(this.navItems, function(v,i){
6154             
6155             if (v.isActive()) {
6156                 prev = i;
6157                 
6158             }
6159             
6160         });
6161         return prev;
6162     },
6163     /**
6164     * adds a Navigation item
6165     * @param {Roo.bootstrap.NavItem} the navitem to add
6166     */
6167     addItem : function(cfg)
6168     {
6169         if (this.form && Roo.bootstrap.version == 4) {
6170             cfg.tag = 'div';
6171         }
6172         var cn = new Roo.bootstrap.NavItem(cfg);
6173         this.register(cn);
6174         cn.parentId = this.id;
6175         cn.onRender(this.el, null);
6176         return cn;
6177     },
6178     /**
6179     * register a Navigation item
6180     * @param {Roo.bootstrap.NavItem} the navitem to add
6181     */
6182     register : function(item)
6183     {
6184         this.navItems.push( item);
6185         item.navId = this.navId;
6186     
6187     },
6188     
6189     /**
6190     * clear all the Navigation item
6191     */
6192    
6193     clearAll : function()
6194     {
6195         this.navItems = [];
6196         this.el.dom.innerHTML = '';
6197     },
6198     
6199     getNavItem: function(tabId)
6200     {
6201         var ret = false;
6202         Roo.each(this.navItems, function(e) {
6203             if (e.tabId == tabId) {
6204                ret =  e;
6205                return false;
6206             }
6207             return true;
6208             
6209         });
6210         return ret;
6211     },
6212     
6213     setActiveNext : function()
6214     {
6215         var i = this.indexOfNav(this.getActive());
6216         if (i > this.navItems.length) {
6217             return;
6218         }
6219         this.setActiveItem(this.navItems[i+1]);
6220     },
6221     setActivePrev : function()
6222     {
6223         var i = this.indexOfNav(this.getActive());
6224         if (i  < 1) {
6225             return;
6226         }
6227         this.setActiveItem(this.navItems[i-1]);
6228     },
6229     clearWasActive : function(except) {
6230         Roo.each(this.navItems, function(e) {
6231             if (e.tabId != except.tabId && e.was_active) {
6232                e.was_active = false;
6233                return false;
6234             }
6235             return true;
6236             
6237         });
6238     },
6239     getWasActive : function ()
6240     {
6241         var r = false;
6242         Roo.each(this.navItems, function(e) {
6243             if (e.was_active) {
6244                r = e;
6245                return false;
6246             }
6247             return true;
6248             
6249         });
6250         return r;
6251     }
6252     
6253     
6254 });
6255
6256  
6257 Roo.apply(Roo.bootstrap.NavGroup, {
6258     
6259     groups: {},
6260      /**
6261     * register a Navigation Group
6262     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6263     */
6264     register : function(navgrp)
6265     {
6266         this.groups[navgrp.navId] = navgrp;
6267         
6268     },
6269     /**
6270     * fetch a Navigation Group based on the navigation ID
6271     * @param {string} the navgroup to add
6272     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6273     */
6274     get: function(navId) {
6275         if (typeof(this.groups[navId]) == 'undefined') {
6276             return false;
6277             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6278         }
6279         return this.groups[navId] ;
6280     }
6281     
6282     
6283     
6284 });
6285
6286  /*
6287  * - LGPL
6288  *
6289  * row
6290  * 
6291  */
6292
6293 /**
6294  * @class Roo.bootstrap.NavItem
6295  * @extends Roo.bootstrap.Component
6296  * Bootstrap Navbar.NavItem class
6297  * @cfg {String} href  link to
6298  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6299  * @cfg {Boolean} button_outline show and outlined button
6300  * @cfg {String} html content of button
6301  * @cfg {String} badge text inside badge
6302  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6303  * @cfg {String} glyphicon DEPRICATED - use fa
6304  * @cfg {String} icon DEPRICATED - use fa
6305  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6306  * @cfg {Boolean} active Is item active
6307  * @cfg {Boolean} disabled Is item disabled
6308  * @cfg {String} linkcls  Link Class
6309  * @cfg {Boolean} preventDefault (true | false) default false
6310  * @cfg {String} tabId the tab that this item activates.
6311  * @cfg {String} tagtype (a|span) render as a href or span?
6312  * @cfg {Boolean} animateRef (true|false) link to element default false  
6313   
6314  * @constructor
6315  * Create a new Navbar Item
6316  * @param {Object} config The config object
6317  */
6318 Roo.bootstrap.NavItem = function(config){
6319     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6320     this.addEvents({
6321         // raw events
6322         /**
6323          * @event click
6324          * The raw click event for the entire grid.
6325          * @param {Roo.EventObject} e
6326          */
6327         "click" : true,
6328          /**
6329             * @event changed
6330             * Fires when the active item active state changes
6331             * @param {Roo.bootstrap.NavItem} this
6332             * @param {boolean} state the new state
6333              
6334          */
6335         'changed': true,
6336         /**
6337             * @event scrollto
6338             * Fires when scroll to element
6339             * @param {Roo.bootstrap.NavItem} this
6340             * @param {Object} options
6341             * @param {Roo.EventObject} e
6342              
6343          */
6344         'scrollto': true
6345     });
6346    
6347 };
6348
6349 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6350     
6351     href: false,
6352     html: '',
6353     badge: '',
6354     icon: false,
6355     fa : false,
6356     glyphicon: false,
6357     active: false,
6358     preventDefault : false,
6359     tabId : false,
6360     tagtype : 'a',
6361     tag: 'li',
6362     disabled : false,
6363     animateRef : false,
6364     was_active : false,
6365     button_weight : '',
6366     button_outline : false,
6367     linkcls : '',
6368     navLink: false,
6369     
6370     getAutoCreate : function(){
6371          
6372         var cfg = {
6373             tag: this.tag,
6374             cls: 'nav-item'
6375         };
6376         
6377         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6378         
6379         if (this.active) {
6380             cfg.cls +=  ' active' ;
6381         }
6382         if (this.disabled) {
6383             cfg.cls += ' disabled';
6384         }
6385         
6386         // BS4 only?
6387         if (this.button_weight.length) {
6388             cfg.tag = this.href ? 'a' : 'button';
6389             cfg.html = this.html || '';
6390             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6391             if (this.href) {
6392                 cfg.href = this.href;
6393             }
6394             if (this.fa) {
6395                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6396             } else {
6397                 cfg.cls += " nav-html";
6398             }
6399             
6400             // menu .. should add dropdown-menu class - so no need for carat..
6401             
6402             if (this.badge !== '') {
6403                  
6404                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6405             }
6406             return cfg;
6407         }
6408         
6409         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6410             cfg.cn = [
6411                 {
6412                     tag: this.tagtype,
6413                     href : this.href || "#",
6414                     html: this.html || '',
6415                     cls : ''
6416                 }
6417             ];
6418             if (this.tagtype == 'a') {
6419                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6420         
6421             }
6422             if (this.icon) {
6423                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6424             } else  if (this.fa) {
6425                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6426             } else if(this.glyphicon) {
6427                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6428             } else {
6429                 cfg.cn[0].cls += " nav-html";
6430             }
6431             
6432             if (this.menu) {
6433                 cfg.cn[0].html += " <span class='caret'></span>";
6434              
6435             }
6436             
6437             if (this.badge !== '') {
6438                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6439             }
6440         }
6441         
6442         
6443         
6444         return cfg;
6445     },
6446     onRender : function(ct, position)
6447     {
6448        // Roo.log("Call onRender: " + this.xtype);
6449         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6450             this.tag = 'div';
6451         }
6452         
6453         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6454         this.navLink = this.el.select('.nav-link',true).first();
6455         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6456         return ret;
6457     },
6458       
6459     
6460     initEvents: function() 
6461     {
6462         if (typeof (this.menu) != 'undefined') {
6463             this.menu.parentType = this.xtype;
6464             this.menu.triggerEl = this.el;
6465             this.menu = this.addxtype(Roo.apply({}, this.menu));
6466         }
6467         
6468         this.el.on('click', this.onClick, this);
6469         
6470         //if(this.tagtype == 'span'){
6471         //    this.el.select('span',true).on('click', this.onClick, this);
6472         //}
6473        
6474         // at this point parent should be available..
6475         this.parent().register(this);
6476     },
6477     
6478     onClick : function(e)
6479     {
6480         if (e.getTarget('.dropdown-menu-item')) {
6481             // did you click on a menu itemm.... - then don't trigger onclick..
6482             return;
6483         }
6484         
6485         if(
6486                 this.preventDefault || 
6487                 this.href == '#' 
6488         ){
6489             Roo.log("NavItem - prevent Default?");
6490             e.preventDefault();
6491         }
6492         
6493         if (this.disabled) {
6494             return;
6495         }
6496         
6497         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6498         if (tg && tg.transition) {
6499             Roo.log("waiting for the transitionend");
6500             return;
6501         }
6502         
6503         
6504         
6505         //Roo.log("fire event clicked");
6506         if(this.fireEvent('click', this, e) === false){
6507             return;
6508         };
6509         
6510         if(this.tagtype == 'span'){
6511             return;
6512         }
6513         
6514         //Roo.log(this.href);
6515         var ael = this.el.select('a',true).first();
6516         //Roo.log(ael);
6517         
6518         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6519             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6520             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6521                 return; // ignore... - it's a 'hash' to another page.
6522             }
6523             Roo.log("NavItem - prevent Default?");
6524             e.preventDefault();
6525             this.scrollToElement(e);
6526         }
6527         
6528         
6529         var p =  this.parent();
6530    
6531         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6532             if (typeof(p.setActiveItem) !== 'undefined') {
6533                 p.setActiveItem(this);
6534             }
6535         }
6536         
6537         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6538         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6539             // remove the collapsed menu expand...
6540             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6541         }
6542     },
6543     
6544     isActive: function () {
6545         return this.active
6546     },
6547     setActive : function(state, fire, is_was_active)
6548     {
6549         if (this.active && !state && this.navId) {
6550             this.was_active = true;
6551             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6552             if (nv) {
6553                 nv.clearWasActive(this);
6554             }
6555             
6556         }
6557         this.active = state;
6558         
6559         if (!state ) {
6560             this.el.removeClass('active');
6561             this.navLink ? this.navLink.removeClass('active') : false;
6562         } else if (!this.el.hasClass('active')) {
6563             
6564             this.el.addClass('active');
6565             if (Roo.bootstrap.version == 4 && this.navLink ) {
6566                 this.navLink.addClass('active');
6567             }
6568             
6569         }
6570         if (fire) {
6571             this.fireEvent('changed', this, state);
6572         }
6573         
6574         // show a panel if it's registered and related..
6575         
6576         if (!this.navId || !this.tabId || !state || is_was_active) {
6577             return;
6578         }
6579         
6580         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6581         if (!tg) {
6582             return;
6583         }
6584         var pan = tg.getPanelByName(this.tabId);
6585         if (!pan) {
6586             return;
6587         }
6588         // if we can not flip to new panel - go back to old nav highlight..
6589         if (false == tg.showPanel(pan)) {
6590             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6591             if (nv) {
6592                 var onav = nv.getWasActive();
6593                 if (onav) {
6594                     onav.setActive(true, false, true);
6595                 }
6596             }
6597             
6598         }
6599         
6600         
6601         
6602     },
6603      // this should not be here...
6604     setDisabled : function(state)
6605     {
6606         this.disabled = state;
6607         if (!state ) {
6608             this.el.removeClass('disabled');
6609         } else if (!this.el.hasClass('disabled')) {
6610             this.el.addClass('disabled');
6611         }
6612         
6613     },
6614     
6615     /**
6616      * Fetch the element to display the tooltip on.
6617      * @return {Roo.Element} defaults to this.el
6618      */
6619     tooltipEl : function()
6620     {
6621         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6622     },
6623     
6624     scrollToElement : function(e)
6625     {
6626         var c = document.body;
6627         
6628         /*
6629          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6630          */
6631         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6632             c = document.documentElement;
6633         }
6634         
6635         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6636         
6637         if(!target){
6638             return;
6639         }
6640
6641         var o = target.calcOffsetsTo(c);
6642         
6643         var options = {
6644             target : target,
6645             value : o[1]
6646         };
6647         
6648         this.fireEvent('scrollto', this, options, e);
6649         
6650         Roo.get(c).scrollTo('top', options.value, true);
6651         
6652         return;
6653     },
6654     /**
6655      * Set the HTML (text content) of the item
6656      * @param {string} html  content for the nav item
6657      */
6658     setHtml : function(html)
6659     {
6660         this.html = html;
6661         this.htmlEl.dom.innerHTML = html;
6662         
6663     } 
6664 });
6665  
6666
6667  /*
6668  * - LGPL
6669  *
6670  * sidebar item
6671  *
6672  *  li
6673  *    <span> icon </span>
6674  *    <span> text </span>
6675  *    <span>badge </span>
6676  */
6677
6678 /**
6679  * @class Roo.bootstrap.NavSidebarItem
6680  * @extends Roo.bootstrap.NavItem
6681  * Bootstrap Navbar.NavSidebarItem class
6682  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6683  * {Boolean} open is the menu open
6684  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6685  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6686  * {String} buttonSize (sm|md|lg)the extra classes for the button
6687  * {Boolean} showArrow show arrow next to the text (default true)
6688  * @constructor
6689  * Create a new Navbar Button
6690  * @param {Object} config The config object
6691  */
6692 Roo.bootstrap.NavSidebarItem = function(config){
6693     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6694     this.addEvents({
6695         // raw events
6696         /**
6697          * @event click
6698          * The raw click event for the entire grid.
6699          * @param {Roo.EventObject} e
6700          */
6701         "click" : true,
6702          /**
6703             * @event changed
6704             * Fires when the active item active state changes
6705             * @param {Roo.bootstrap.NavSidebarItem} this
6706             * @param {boolean} state the new state
6707              
6708          */
6709         'changed': true
6710     });
6711    
6712 };
6713
6714 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6715     
6716     badgeWeight : 'default',
6717     
6718     open: false,
6719     
6720     buttonView : false,
6721     
6722     buttonWeight : 'default',
6723     
6724     buttonSize : 'md',
6725     
6726     showArrow : true,
6727     
6728     getAutoCreate : function(){
6729         
6730         
6731         var a = {
6732                 tag: 'a',
6733                 href : this.href || '#',
6734                 cls: '',
6735                 html : '',
6736                 cn : []
6737         };
6738         
6739         if(this.buttonView){
6740             a = {
6741                 tag: 'button',
6742                 href : this.href || '#',
6743                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6744                 html : this.html,
6745                 cn : []
6746             };
6747         }
6748         
6749         var cfg = {
6750             tag: 'li',
6751             cls: '',
6752             cn: [ a ]
6753         };
6754         
6755         if (this.active) {
6756             cfg.cls += ' active';
6757         }
6758         
6759         if (this.disabled) {
6760             cfg.cls += ' disabled';
6761         }
6762         if (this.open) {
6763             cfg.cls += ' open x-open';
6764         }
6765         // left icon..
6766         if (this.glyphicon || this.icon) {
6767             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6768             a.cn.push({ tag : 'i', cls : c }) ;
6769         }
6770         
6771         if(!this.buttonView){
6772             var span = {
6773                 tag: 'span',
6774                 html : this.html || ''
6775             };
6776
6777             a.cn.push(span);
6778             
6779         }
6780         
6781         if (this.badge !== '') {
6782             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6783         }
6784         
6785         if (this.menu) {
6786             
6787             if(this.showArrow){
6788                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6789             }
6790             
6791             a.cls += ' dropdown-toggle treeview' ;
6792         }
6793         
6794         return cfg;
6795     },
6796     
6797     initEvents : function()
6798     { 
6799         if (typeof (this.menu) != 'undefined') {
6800             this.menu.parentType = this.xtype;
6801             this.menu.triggerEl = this.el;
6802             this.menu = this.addxtype(Roo.apply({}, this.menu));
6803         }
6804         
6805         this.el.on('click', this.onClick, this);
6806         
6807         if(this.badge !== ''){
6808             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6809         }
6810         
6811     },
6812     
6813     onClick : function(e)
6814     {
6815         if(this.disabled){
6816             e.preventDefault();
6817             return;
6818         }
6819         
6820         if(this.preventDefault){
6821             e.preventDefault();
6822         }
6823         
6824         this.fireEvent('click', this, e);
6825     },
6826     
6827     disable : function()
6828     {
6829         this.setDisabled(true);
6830     },
6831     
6832     enable : function()
6833     {
6834         this.setDisabled(false);
6835     },
6836     
6837     setDisabled : function(state)
6838     {
6839         if(this.disabled == state){
6840             return;
6841         }
6842         
6843         this.disabled = state;
6844         
6845         if (state) {
6846             this.el.addClass('disabled');
6847             return;
6848         }
6849         
6850         this.el.removeClass('disabled');
6851         
6852         return;
6853     },
6854     
6855     setActive : function(state)
6856     {
6857         if(this.active == state){
6858             return;
6859         }
6860         
6861         this.active = state;
6862         
6863         if (state) {
6864             this.el.addClass('active');
6865             return;
6866         }
6867         
6868         this.el.removeClass('active');
6869         
6870         return;
6871     },
6872     
6873     isActive: function () 
6874     {
6875         return this.active;
6876     },
6877     
6878     setBadge : function(str)
6879     {
6880         if(!this.badgeEl){
6881             return;
6882         }
6883         
6884         this.badgeEl.dom.innerHTML = str;
6885     }
6886     
6887    
6888      
6889  
6890 });
6891  
6892
6893  /*
6894  * - LGPL
6895  *
6896  *  Breadcrumb Nav
6897  * 
6898  */
6899 Roo.namespace('Roo.bootstrap.breadcrumb');
6900
6901
6902 /**
6903  * @class Roo.bootstrap.breadcrumb.Nav
6904  * @extends Roo.bootstrap.Component
6905  * Bootstrap Breadcrumb Nav Class
6906  *  
6907  * @children Roo.bootstrap.breadcrumb.Item
6908  * 
6909  * @constructor
6910  * Create a new breadcrumb.Nav
6911  * @param {Object} config The config object
6912  */
6913
6914
6915 Roo.bootstrap.breadcrumb.Nav = function(config){
6916     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6917     
6918     
6919 };
6920
6921 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6922     
6923     getAutoCreate : function()
6924     {
6925
6926         var cfg = {
6927             tag: 'nav',
6928             cn : [
6929                 {
6930                     tag : 'ol',
6931                     cls : 'breadcrumb'
6932                 }
6933             ]
6934             
6935         };
6936           
6937         return cfg;
6938     },
6939     
6940     initEvents: function()
6941     {
6942         this.olEl = this.el.select('ol',true).first();    
6943     },
6944     getChildContainer : function()
6945     {
6946         return this.olEl;  
6947     }
6948     
6949 });
6950
6951  /*
6952  * - LGPL
6953  *
6954  *  Breadcrumb Item
6955  * 
6956  */
6957
6958
6959 /**
6960  * @class Roo.bootstrap.breadcrumb.Nav
6961  * @extends Roo.bootstrap.Component
6962  * Bootstrap Breadcrumb Nav Class
6963  *  
6964  * @children Roo.bootstrap.breadcrumb.Component
6965  * @cfg {String} html the content of the link.
6966  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6967  * @cfg {Boolean} active is it active
6968
6969  * 
6970  * @constructor
6971  * Create a new breadcrumb.Nav
6972  * @param {Object} config The config object
6973  */
6974
6975 Roo.bootstrap.breadcrumb.Item = function(config){
6976     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6977     this.addEvents({
6978         // img events
6979         /**
6980          * @event click
6981          * The img click event for the img.
6982          * @param {Roo.EventObject} e
6983          */
6984         "click" : true
6985     });
6986     
6987 };
6988
6989 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6990     
6991     href: false,
6992     html : '',
6993     
6994     getAutoCreate : function()
6995     {
6996
6997         var cfg = {
6998             tag: 'li',
6999             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7000         };
7001         if (this.href !== false) {
7002             cfg.cn = [{
7003                 tag : 'a',
7004                 href : this.href,
7005                 html : this.html
7006             }];
7007         } else {
7008             cfg.html = this.html;
7009         }
7010         
7011         return cfg;
7012     },
7013     
7014     initEvents: function()
7015     {
7016         if (this.href) {
7017             this.el.select('a', true).first().on('click',this.onClick, this)
7018         }
7019         
7020     },
7021     onClick : function(e)
7022     {
7023         e.preventDefault();
7024         this.fireEvent('click',this,  e);
7025     }
7026     
7027 });
7028
7029  /*
7030  * - LGPL
7031  *
7032  * row
7033  * 
7034  */
7035
7036 /**
7037  * @class Roo.bootstrap.Row
7038  * @extends Roo.bootstrap.Component
7039  * Bootstrap Row class (contains columns...)
7040  * 
7041  * @constructor
7042  * Create a new Row
7043  * @param {Object} config The config object
7044  */
7045
7046 Roo.bootstrap.Row = function(config){
7047     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7048 };
7049
7050 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7051     
7052     getAutoCreate : function(){
7053        return {
7054             cls: 'row clearfix'
7055        };
7056     }
7057     
7058     
7059 });
7060
7061  
7062
7063  /*
7064  * - LGPL
7065  *
7066  * pagination
7067  * 
7068  */
7069
7070 /**
7071  * @class Roo.bootstrap.Pagination
7072  * @extends Roo.bootstrap.Component
7073  * Bootstrap Pagination class
7074  * @cfg {String} size xs | sm | md | lg
7075  * @cfg {Boolean} inverse false | true
7076  * 
7077  * @constructor
7078  * Create a new Pagination
7079  * @param {Object} config The config object
7080  */
7081
7082 Roo.bootstrap.Pagination = function(config){
7083     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7084 };
7085
7086 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7087     
7088     cls: false,
7089     size: false,
7090     inverse: false,
7091     
7092     getAutoCreate : function(){
7093         var cfg = {
7094             tag: 'ul',
7095                 cls: 'pagination'
7096         };
7097         if (this.inverse) {
7098             cfg.cls += ' inverse';
7099         }
7100         if (this.html) {
7101             cfg.html=this.html;
7102         }
7103         if (this.cls) {
7104             cfg.cls += " " + this.cls;
7105         }
7106         return cfg;
7107     }
7108    
7109 });
7110
7111  
7112
7113  /*
7114  * - LGPL
7115  *
7116  * Pagination item
7117  * 
7118  */
7119
7120
7121 /**
7122  * @class Roo.bootstrap.PaginationItem
7123  * @extends Roo.bootstrap.Component
7124  * Bootstrap PaginationItem class
7125  * @cfg {String} html text
7126  * @cfg {String} href the link
7127  * @cfg {Boolean} preventDefault (true | false) default true
7128  * @cfg {Boolean} active (true | false) default false
7129  * @cfg {Boolean} disabled default false
7130  * 
7131  * 
7132  * @constructor
7133  * Create a new PaginationItem
7134  * @param {Object} config The config object
7135  */
7136
7137
7138 Roo.bootstrap.PaginationItem = function(config){
7139     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7140     this.addEvents({
7141         // raw events
7142         /**
7143          * @event click
7144          * The raw click event for the entire grid.
7145          * @param {Roo.EventObject} e
7146          */
7147         "click" : true
7148     });
7149 };
7150
7151 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7152     
7153     href : false,
7154     html : false,
7155     preventDefault: true,
7156     active : false,
7157     cls : false,
7158     disabled: false,
7159     
7160     getAutoCreate : function(){
7161         var cfg= {
7162             tag: 'li',
7163             cn: [
7164                 {
7165                     tag : 'a',
7166                     href : this.href ? this.href : '#',
7167                     html : this.html ? this.html : ''
7168                 }
7169             ]
7170         };
7171         
7172         if(this.cls){
7173             cfg.cls = this.cls;
7174         }
7175         
7176         if(this.disabled){
7177             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7178         }
7179         
7180         if(this.active){
7181             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7182         }
7183         
7184         return cfg;
7185     },
7186     
7187     initEvents: function() {
7188         
7189         this.el.on('click', this.onClick, this);
7190         
7191     },
7192     onClick : function(e)
7193     {
7194         Roo.log('PaginationItem on click ');
7195         if(this.preventDefault){
7196             e.preventDefault();
7197         }
7198         
7199         if(this.disabled){
7200             return;
7201         }
7202         
7203         this.fireEvent('click', this, e);
7204     }
7205    
7206 });
7207
7208  
7209
7210  /*
7211  * - LGPL
7212  *
7213  * slider
7214  * 
7215  */
7216
7217
7218 /**
7219  * @class Roo.bootstrap.Slider
7220  * @extends Roo.bootstrap.Component
7221  * Bootstrap Slider class
7222  *    
7223  * @constructor
7224  * Create a new Slider
7225  * @param {Object} config The config object
7226  */
7227
7228 Roo.bootstrap.Slider = function(config){
7229     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7230 };
7231
7232 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7233     
7234     getAutoCreate : function(){
7235         
7236         var cfg = {
7237             tag: 'div',
7238             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7239             cn: [
7240                 {
7241                     tag: 'a',
7242                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7243                 }
7244             ]
7245         };
7246         
7247         return cfg;
7248     }
7249    
7250 });
7251
7252  /*
7253  * Based on:
7254  * Ext JS Library 1.1.1
7255  * Copyright(c) 2006-2007, Ext JS, LLC.
7256  *
7257  * Originally Released Under LGPL - original licence link has changed is not relivant.
7258  *
7259  * Fork - LGPL
7260  * <script type="text/javascript">
7261  */
7262  
7263
7264 /**
7265  * @class Roo.grid.ColumnModel
7266  * @extends Roo.util.Observable
7267  * This is the default implementation of a ColumnModel used by the Grid. It defines
7268  * the columns in the grid.
7269  * <br>Usage:<br>
7270  <pre><code>
7271  var colModel = new Roo.grid.ColumnModel([
7272         {header: "Ticker", width: 60, sortable: true, locked: true},
7273         {header: "Company Name", width: 150, sortable: true},
7274         {header: "Market Cap.", width: 100, sortable: true},
7275         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7276         {header: "Employees", width: 100, sortable: true, resizable: false}
7277  ]);
7278  </code></pre>
7279  * <p>
7280  
7281  * The config options listed for this class are options which may appear in each
7282  * individual column definition.
7283  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7284  * @constructor
7285  * @param {Object} config An Array of column config objects. See this class's
7286  * config objects for details.
7287 */
7288 Roo.grid.ColumnModel = function(config){
7289         /**
7290      * The config passed into the constructor
7291      */
7292     this.config = config;
7293     this.lookup = {};
7294
7295     // if no id, create one
7296     // if the column does not have a dataIndex mapping,
7297     // map it to the order it is in the config
7298     for(var i = 0, len = config.length; i < len; i++){
7299         var c = config[i];
7300         if(typeof c.dataIndex == "undefined"){
7301             c.dataIndex = i;
7302         }
7303         if(typeof c.renderer == "string"){
7304             c.renderer = Roo.util.Format[c.renderer];
7305         }
7306         if(typeof c.id == "undefined"){
7307             c.id = Roo.id();
7308         }
7309         if(c.editor && c.editor.xtype){
7310             c.editor  = Roo.factory(c.editor, Roo.grid);
7311         }
7312         if(c.editor && c.editor.isFormField){
7313             c.editor = new Roo.grid.GridEditor(c.editor);
7314         }
7315         this.lookup[c.id] = c;
7316     }
7317
7318     /**
7319      * The width of columns which have no width specified (defaults to 100)
7320      * @type Number
7321      */
7322     this.defaultWidth = 100;
7323
7324     /**
7325      * Default sortable of columns which have no sortable specified (defaults to false)
7326      * @type Boolean
7327      */
7328     this.defaultSortable = false;
7329
7330     this.addEvents({
7331         /**
7332              * @event widthchange
7333              * Fires when the width of a column changes.
7334              * @param {ColumnModel} this
7335              * @param {Number} columnIndex The column index
7336              * @param {Number} newWidth The new width
7337              */
7338             "widthchange": true,
7339         /**
7340              * @event headerchange
7341              * Fires when the text of a header changes.
7342              * @param {ColumnModel} this
7343              * @param {Number} columnIndex The column index
7344              * @param {Number} newText The new header text
7345              */
7346             "headerchange": true,
7347         /**
7348              * @event hiddenchange
7349              * Fires when a column is hidden or "unhidden".
7350              * @param {ColumnModel} this
7351              * @param {Number} columnIndex The column index
7352              * @param {Boolean} hidden true if hidden, false otherwise
7353              */
7354             "hiddenchange": true,
7355             /**
7356          * @event columnmoved
7357          * Fires when a column is moved.
7358          * @param {ColumnModel} this
7359          * @param {Number} oldIndex
7360          * @param {Number} newIndex
7361          */
7362         "columnmoved" : true,
7363         /**
7364          * @event columlockchange
7365          * Fires when a column's locked state is changed
7366          * @param {ColumnModel} this
7367          * @param {Number} colIndex
7368          * @param {Boolean} locked true if locked
7369          */
7370         "columnlockchange" : true
7371     });
7372     Roo.grid.ColumnModel.superclass.constructor.call(this);
7373 };
7374 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7375     /**
7376      * @cfg {String} header The header text to display in the Grid view.
7377      */
7378     /**
7379      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7380      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7381      * specified, the column's index is used as an index into the Record's data Array.
7382      */
7383     /**
7384      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7385      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7386      */
7387     /**
7388      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7389      * Defaults to the value of the {@link #defaultSortable} property.
7390      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7391      */
7392     /**
7393      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7394      */
7395     /**
7396      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7397      */
7398     /**
7399      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7400      */
7401     /**
7402      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7403      */
7404     /**
7405      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7406      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7407      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7408      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7409      */
7410        /**
7411      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7412      */
7413     /**
7414      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7415      */
7416     /**
7417      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7418      */
7419     /**
7420      * @cfg {String} cursor (Optional)
7421      */
7422     /**
7423      * @cfg {String} tooltip (Optional)
7424      */
7425     /**
7426      * @cfg {Number} xs (Optional)
7427      */
7428     /**
7429      * @cfg {Number} sm (Optional)
7430      */
7431     /**
7432      * @cfg {Number} md (Optional)
7433      */
7434     /**
7435      * @cfg {Number} lg (Optional)
7436      */
7437     /**
7438      * Returns the id of the column at the specified index.
7439      * @param {Number} index The column index
7440      * @return {String} the id
7441      */
7442     getColumnId : function(index){
7443         return this.config[index].id;
7444     },
7445
7446     /**
7447      * Returns the column for a specified id.
7448      * @param {String} id The column id
7449      * @return {Object} the column
7450      */
7451     getColumnById : function(id){
7452         return this.lookup[id];
7453     },
7454
7455     
7456     /**
7457      * Returns the column for a specified dataIndex.
7458      * @param {String} dataIndex The column dataIndex
7459      * @return {Object|Boolean} the column or false if not found
7460      */
7461     getColumnByDataIndex: function(dataIndex){
7462         var index = this.findColumnIndex(dataIndex);
7463         return index > -1 ? this.config[index] : false;
7464     },
7465     
7466     /**
7467      * Returns the index for a specified column id.
7468      * @param {String} id The column id
7469      * @return {Number} the index, or -1 if not found
7470      */
7471     getIndexById : function(id){
7472         for(var i = 0, len = this.config.length; i < len; i++){
7473             if(this.config[i].id == id){
7474                 return i;
7475             }
7476         }
7477         return -1;
7478     },
7479     
7480     /**
7481      * Returns the index for a specified column dataIndex.
7482      * @param {String} dataIndex The column dataIndex
7483      * @return {Number} the index, or -1 if not found
7484      */
7485     
7486     findColumnIndex : function(dataIndex){
7487         for(var i = 0, len = this.config.length; i < len; i++){
7488             if(this.config[i].dataIndex == dataIndex){
7489                 return i;
7490             }
7491         }
7492         return -1;
7493     },
7494     
7495     
7496     moveColumn : function(oldIndex, newIndex){
7497         var c = this.config[oldIndex];
7498         this.config.splice(oldIndex, 1);
7499         this.config.splice(newIndex, 0, c);
7500         this.dataMap = null;
7501         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7502     },
7503
7504     isLocked : function(colIndex){
7505         return this.config[colIndex].locked === true;
7506     },
7507
7508     setLocked : function(colIndex, value, suppressEvent){
7509         if(this.isLocked(colIndex) == value){
7510             return;
7511         }
7512         this.config[colIndex].locked = value;
7513         if(!suppressEvent){
7514             this.fireEvent("columnlockchange", this, colIndex, value);
7515         }
7516     },
7517
7518     getTotalLockedWidth : function(){
7519         var totalWidth = 0;
7520         for(var i = 0; i < this.config.length; i++){
7521             if(this.isLocked(i) && !this.isHidden(i)){
7522                 this.totalWidth += this.getColumnWidth(i);
7523             }
7524         }
7525         return totalWidth;
7526     },
7527
7528     getLockedCount : function(){
7529         for(var i = 0, len = this.config.length; i < len; i++){
7530             if(!this.isLocked(i)){
7531                 return i;
7532             }
7533         }
7534         
7535         return this.config.length;
7536     },
7537
7538     /**
7539      * Returns the number of columns.
7540      * @return {Number}
7541      */
7542     getColumnCount : function(visibleOnly){
7543         if(visibleOnly === true){
7544             var c = 0;
7545             for(var i = 0, len = this.config.length; i < len; i++){
7546                 if(!this.isHidden(i)){
7547                     c++;
7548                 }
7549             }
7550             return c;
7551         }
7552         return this.config.length;
7553     },
7554
7555     /**
7556      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7557      * @param {Function} fn
7558      * @param {Object} scope (optional)
7559      * @return {Array} result
7560      */
7561     getColumnsBy : function(fn, scope){
7562         var r = [];
7563         for(var i = 0, len = this.config.length; i < len; i++){
7564             var c = this.config[i];
7565             if(fn.call(scope||this, c, i) === true){
7566                 r[r.length] = c;
7567             }
7568         }
7569         return r;
7570     },
7571
7572     /**
7573      * Returns true if the specified column is sortable.
7574      * @param {Number} col The column index
7575      * @return {Boolean}
7576      */
7577     isSortable : function(col){
7578         if(typeof this.config[col].sortable == "undefined"){
7579             return this.defaultSortable;
7580         }
7581         return this.config[col].sortable;
7582     },
7583
7584     /**
7585      * Returns the rendering (formatting) function defined for the column.
7586      * @param {Number} col The column index.
7587      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7588      */
7589     getRenderer : function(col){
7590         if(!this.config[col].renderer){
7591             return Roo.grid.ColumnModel.defaultRenderer;
7592         }
7593         return this.config[col].renderer;
7594     },
7595
7596     /**
7597      * Sets the rendering (formatting) function for a column.
7598      * @param {Number} col The column index
7599      * @param {Function} fn The function to use to process the cell's raw data
7600      * to return HTML markup for the grid view. The render function is called with
7601      * the following parameters:<ul>
7602      * <li>Data value.</li>
7603      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7604      * <li>css A CSS style string to apply to the table cell.</li>
7605      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7606      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7607      * <li>Row index</li>
7608      * <li>Column index</li>
7609      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7610      */
7611     setRenderer : function(col, fn){
7612         this.config[col].renderer = fn;
7613     },
7614
7615     /**
7616      * Returns the width for the specified column.
7617      * @param {Number} col The column index
7618      * @return {Number}
7619      */
7620     getColumnWidth : function(col){
7621         return this.config[col].width * 1 || this.defaultWidth;
7622     },
7623
7624     /**
7625      * Sets the width for a column.
7626      * @param {Number} col The column index
7627      * @param {Number} width The new width
7628      */
7629     setColumnWidth : function(col, width, suppressEvent){
7630         this.config[col].width = width;
7631         this.totalWidth = null;
7632         if(!suppressEvent){
7633              this.fireEvent("widthchange", this, col, width);
7634         }
7635     },
7636
7637     /**
7638      * Returns the total width of all columns.
7639      * @param {Boolean} includeHidden True to include hidden column widths
7640      * @return {Number}
7641      */
7642     getTotalWidth : function(includeHidden){
7643         if(!this.totalWidth){
7644             this.totalWidth = 0;
7645             for(var i = 0, len = this.config.length; i < len; i++){
7646                 if(includeHidden || !this.isHidden(i)){
7647                     this.totalWidth += this.getColumnWidth(i);
7648                 }
7649             }
7650         }
7651         return this.totalWidth;
7652     },
7653
7654     /**
7655      * Returns the header for the specified column.
7656      * @param {Number} col The column index
7657      * @return {String}
7658      */
7659     getColumnHeader : function(col){
7660         return this.config[col].header;
7661     },
7662
7663     /**
7664      * Sets the header for a column.
7665      * @param {Number} col The column index
7666      * @param {String} header The new header
7667      */
7668     setColumnHeader : function(col, header){
7669         this.config[col].header = header;
7670         this.fireEvent("headerchange", this, col, header);
7671     },
7672
7673     /**
7674      * Returns the tooltip for the specified column.
7675      * @param {Number} col The column index
7676      * @return {String}
7677      */
7678     getColumnTooltip : function(col){
7679             return this.config[col].tooltip;
7680     },
7681     /**
7682      * Sets the tooltip for a column.
7683      * @param {Number} col The column index
7684      * @param {String} tooltip The new tooltip
7685      */
7686     setColumnTooltip : function(col, tooltip){
7687             this.config[col].tooltip = tooltip;
7688     },
7689
7690     /**
7691      * Returns the dataIndex for the specified column.
7692      * @param {Number} col The column index
7693      * @return {Number}
7694      */
7695     getDataIndex : function(col){
7696         return this.config[col].dataIndex;
7697     },
7698
7699     /**
7700      * Sets the dataIndex for a column.
7701      * @param {Number} col The column index
7702      * @param {Number} dataIndex The new dataIndex
7703      */
7704     setDataIndex : function(col, dataIndex){
7705         this.config[col].dataIndex = dataIndex;
7706     },
7707
7708     
7709     
7710     /**
7711      * Returns true if the cell is editable.
7712      * @param {Number} colIndex The column index
7713      * @param {Number} rowIndex The row index - this is nto actually used..?
7714      * @return {Boolean}
7715      */
7716     isCellEditable : function(colIndex, rowIndex){
7717         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7718     },
7719
7720     /**
7721      * Returns the editor defined for the cell/column.
7722      * return false or null to disable editing.
7723      * @param {Number} colIndex The column index
7724      * @param {Number} rowIndex The row index
7725      * @return {Object}
7726      */
7727     getCellEditor : function(colIndex, rowIndex){
7728         return this.config[colIndex].editor;
7729     },
7730
7731     /**
7732      * Sets if a column is editable.
7733      * @param {Number} col The column index
7734      * @param {Boolean} editable True if the column is editable
7735      */
7736     setEditable : function(col, editable){
7737         this.config[col].editable = editable;
7738     },
7739
7740
7741     /**
7742      * Returns true if the column is hidden.
7743      * @param {Number} colIndex The column index
7744      * @return {Boolean}
7745      */
7746     isHidden : function(colIndex){
7747         return this.config[colIndex].hidden;
7748     },
7749
7750
7751     /**
7752      * Returns true if the column width cannot be changed
7753      */
7754     isFixed : function(colIndex){
7755         return this.config[colIndex].fixed;
7756     },
7757
7758     /**
7759      * Returns true if the column can be resized
7760      * @return {Boolean}
7761      */
7762     isResizable : function(colIndex){
7763         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7764     },
7765     /**
7766      * Sets if a column is hidden.
7767      * @param {Number} colIndex The column index
7768      * @param {Boolean} hidden True if the column is hidden
7769      */
7770     setHidden : function(colIndex, hidden){
7771         this.config[colIndex].hidden = hidden;
7772         this.totalWidth = null;
7773         this.fireEvent("hiddenchange", this, colIndex, hidden);
7774     },
7775
7776     /**
7777      * Sets the editor for a column.
7778      * @param {Number} col The column index
7779      * @param {Object} editor The editor object
7780      */
7781     setEditor : function(col, editor){
7782         this.config[col].editor = editor;
7783     }
7784 });
7785
7786 Roo.grid.ColumnModel.defaultRenderer = function(value)
7787 {
7788     if(typeof value == "object") {
7789         return value;
7790     }
7791         if(typeof value == "string" && value.length < 1){
7792             return "&#160;";
7793         }
7794     
7795         return String.format("{0}", value);
7796 };
7797
7798 // Alias for backwards compatibility
7799 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7800 /*
7801  * Based on:
7802  * Ext JS Library 1.1.1
7803  * Copyright(c) 2006-2007, Ext JS, LLC.
7804  *
7805  * Originally Released Under LGPL - original licence link has changed is not relivant.
7806  *
7807  * Fork - LGPL
7808  * <script type="text/javascript">
7809  */
7810  
7811 /**
7812  * @class Roo.LoadMask
7813  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7814  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7815  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7816  * element's UpdateManager load indicator and will be destroyed after the initial load.
7817  * @constructor
7818  * Create a new LoadMask
7819  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7820  * @param {Object} config The config object
7821  */
7822 Roo.LoadMask = function(el, config){
7823     this.el = Roo.get(el);
7824     Roo.apply(this, config);
7825     if(this.store){
7826         this.store.on('beforeload', this.onBeforeLoad, this);
7827         this.store.on('load', this.onLoad, this);
7828         this.store.on('loadexception', this.onLoadException, this);
7829         this.removeMask = false;
7830     }else{
7831         var um = this.el.getUpdateManager();
7832         um.showLoadIndicator = false; // disable the default indicator
7833         um.on('beforeupdate', this.onBeforeLoad, this);
7834         um.on('update', this.onLoad, this);
7835         um.on('failure', this.onLoad, this);
7836         this.removeMask = true;
7837     }
7838 };
7839
7840 Roo.LoadMask.prototype = {
7841     /**
7842      * @cfg {Boolean} removeMask
7843      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7844      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7845      */
7846     /**
7847      * @cfg {String} msg
7848      * The text to display in a centered loading message box (defaults to 'Loading...')
7849      */
7850     msg : 'Loading...',
7851     /**
7852      * @cfg {String} msgCls
7853      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7854      */
7855     msgCls : 'x-mask-loading',
7856
7857     /**
7858      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7859      * @type Boolean
7860      */
7861     disabled: false,
7862
7863     /**
7864      * Disables the mask to prevent it from being displayed
7865      */
7866     disable : function(){
7867        this.disabled = true;
7868     },
7869
7870     /**
7871      * Enables the mask so that it can be displayed
7872      */
7873     enable : function(){
7874         this.disabled = false;
7875     },
7876     
7877     onLoadException : function()
7878     {
7879         Roo.log(arguments);
7880         
7881         if (typeof(arguments[3]) != 'undefined') {
7882             Roo.MessageBox.alert("Error loading",arguments[3]);
7883         } 
7884         /*
7885         try {
7886             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7887                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7888             }   
7889         } catch(e) {
7890             
7891         }
7892         */
7893     
7894         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7895     },
7896     // private
7897     onLoad : function()
7898     {
7899         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7900     },
7901
7902     // private
7903     onBeforeLoad : function(){
7904         if(!this.disabled){
7905             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7906         }
7907     },
7908
7909     // private
7910     destroy : function(){
7911         if(this.store){
7912             this.store.un('beforeload', this.onBeforeLoad, this);
7913             this.store.un('load', this.onLoad, this);
7914             this.store.un('loadexception', this.onLoadException, this);
7915         }else{
7916             var um = this.el.getUpdateManager();
7917             um.un('beforeupdate', this.onBeforeLoad, this);
7918             um.un('update', this.onLoad, this);
7919             um.un('failure', this.onLoad, this);
7920         }
7921     }
7922 };/*
7923  * - LGPL
7924  *
7925  * table
7926  * 
7927  */
7928
7929 /**
7930  * @class Roo.bootstrap.Table
7931  * @extends Roo.bootstrap.Component
7932  * Bootstrap Table class
7933  * @cfg {String} cls table class
7934  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7935  * @cfg {String} bgcolor Specifies the background color for a table
7936  * @cfg {Number} border Specifies whether the table cells should have borders or not
7937  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7938  * @cfg {Number} cellspacing Specifies the space between cells
7939  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7940  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7941  * @cfg {String} sortable Specifies that the table should be sortable
7942  * @cfg {String} summary Specifies a summary of the content of a table
7943  * @cfg {Number} width Specifies the width of a table
7944  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7945  * 
7946  * @cfg {boolean} striped Should the rows be alternative striped
7947  * @cfg {boolean} bordered Add borders to the table
7948  * @cfg {boolean} hover Add hover highlighting
7949  * @cfg {boolean} condensed Format condensed
7950  * @cfg {boolean} responsive Format condensed
7951  * @cfg {Boolean} loadMask (true|false) default false
7952  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7953  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7954  * @cfg {Boolean} rowSelection (true|false) default false
7955  * @cfg {Boolean} cellSelection (true|false) default false
7956  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7957  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7958  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7959  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7960  
7961  * 
7962  * @constructor
7963  * Create a new Table
7964  * @param {Object} config The config object
7965  */
7966
7967 Roo.bootstrap.Table = function(config){
7968     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7969     
7970   
7971     
7972     // BC...
7973     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7974     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7975     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7976     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7977     
7978     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7979     if (this.sm) {
7980         this.sm.grid = this;
7981         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7982         this.sm = this.selModel;
7983         this.sm.xmodule = this.xmodule || false;
7984     }
7985     
7986     if (this.cm && typeof(this.cm.config) == 'undefined') {
7987         this.colModel = new Roo.grid.ColumnModel(this.cm);
7988         this.cm = this.colModel;
7989         this.cm.xmodule = this.xmodule || false;
7990     }
7991     if (this.store) {
7992         this.store= Roo.factory(this.store, Roo.data);
7993         this.ds = this.store;
7994         this.ds.xmodule = this.xmodule || false;
7995          
7996     }
7997     if (this.footer && this.store) {
7998         this.footer.dataSource = this.ds;
7999         this.footer = Roo.factory(this.footer);
8000     }
8001     
8002     /** @private */
8003     this.addEvents({
8004         /**
8005          * @event cellclick
8006          * Fires when a cell is clicked
8007          * @param {Roo.bootstrap.Table} this
8008          * @param {Roo.Element} el
8009          * @param {Number} rowIndex
8010          * @param {Number} columnIndex
8011          * @param {Roo.EventObject} e
8012          */
8013         "cellclick" : true,
8014         /**
8015          * @event celldblclick
8016          * Fires when a cell is double clicked
8017          * @param {Roo.bootstrap.Table} this
8018          * @param {Roo.Element} el
8019          * @param {Number} rowIndex
8020          * @param {Number} columnIndex
8021          * @param {Roo.EventObject} e
8022          */
8023         "celldblclick" : true,
8024         /**
8025          * @event rowclick
8026          * Fires when a row is clicked
8027          * @param {Roo.bootstrap.Table} this
8028          * @param {Roo.Element} el
8029          * @param {Number} rowIndex
8030          * @param {Roo.EventObject} e
8031          */
8032         "rowclick" : true,
8033         /**
8034          * @event rowdblclick
8035          * Fires when a row is double clicked
8036          * @param {Roo.bootstrap.Table} this
8037          * @param {Roo.Element} el
8038          * @param {Number} rowIndex
8039          * @param {Roo.EventObject} e
8040          */
8041         "rowdblclick" : true,
8042         /**
8043          * @event mouseover
8044          * Fires when a mouseover occur
8045          * @param {Roo.bootstrap.Table} this
8046          * @param {Roo.Element} el
8047          * @param {Number} rowIndex
8048          * @param {Number} columnIndex
8049          * @param {Roo.EventObject} e
8050          */
8051         "mouseover" : true,
8052         /**
8053          * @event mouseout
8054          * Fires when a mouseout occur
8055          * @param {Roo.bootstrap.Table} this
8056          * @param {Roo.Element} el
8057          * @param {Number} rowIndex
8058          * @param {Number} columnIndex
8059          * @param {Roo.EventObject} e
8060          */
8061         "mouseout" : true,
8062         /**
8063          * @event rowclass
8064          * Fires when a row is rendered, so you can change add a style to it.
8065          * @param {Roo.bootstrap.Table} this
8066          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8067          */
8068         'rowclass' : true,
8069           /**
8070          * @event rowsrendered
8071          * Fires when all the  rows have been rendered
8072          * @param {Roo.bootstrap.Table} this
8073          */
8074         'rowsrendered' : true,
8075         /**
8076          * @event contextmenu
8077          * The raw contextmenu event for the entire grid.
8078          * @param {Roo.EventObject} e
8079          */
8080         "contextmenu" : true,
8081         /**
8082          * @event rowcontextmenu
8083          * Fires when a row is right clicked
8084          * @param {Roo.bootstrap.Table} this
8085          * @param {Number} rowIndex
8086          * @param {Roo.EventObject} e
8087          */
8088         "rowcontextmenu" : true,
8089         /**
8090          * @event cellcontextmenu
8091          * Fires when a cell is right clicked
8092          * @param {Roo.bootstrap.Table} this
8093          * @param {Number} rowIndex
8094          * @param {Number} cellIndex
8095          * @param {Roo.EventObject} e
8096          */
8097          "cellcontextmenu" : true,
8098          /**
8099          * @event headercontextmenu
8100          * Fires when a header is right clicked
8101          * @param {Roo.bootstrap.Table} this
8102          * @param {Number} columnIndex
8103          * @param {Roo.EventObject} e
8104          */
8105         "headercontextmenu" : true
8106     });
8107 };
8108
8109 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8110     
8111     cls: false,
8112     align: false,
8113     bgcolor: false,
8114     border: false,
8115     cellpadding: false,
8116     cellspacing: false,
8117     frame: false,
8118     rules: false,
8119     sortable: false,
8120     summary: false,
8121     width: false,
8122     striped : false,
8123     scrollBody : false,
8124     bordered: false,
8125     hover:  false,
8126     condensed : false,
8127     responsive : false,
8128     sm : false,
8129     cm : false,
8130     store : false,
8131     loadMask : false,
8132     footerShow : true,
8133     headerShow : true,
8134   
8135     rowSelection : false,
8136     cellSelection : false,
8137     layout : false,
8138     
8139     // Roo.Element - the tbody
8140     mainBody: false,
8141     // Roo.Element - thead element
8142     mainHead: false,
8143     
8144     container: false, // used by gridpanel...
8145     
8146     lazyLoad : false,
8147     
8148     CSS : Roo.util.CSS,
8149     
8150     auto_hide_footer : false,
8151     
8152     getAutoCreate : function()
8153     {
8154         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8155         
8156         cfg = {
8157             tag: 'table',
8158             cls : 'table',
8159             cn : []
8160         };
8161         if (this.scrollBody) {
8162             cfg.cls += ' table-body-fixed';
8163         }    
8164         if (this.striped) {
8165             cfg.cls += ' table-striped';
8166         }
8167         
8168         if (this.hover) {
8169             cfg.cls += ' table-hover';
8170         }
8171         if (this.bordered) {
8172             cfg.cls += ' table-bordered';
8173         }
8174         if (this.condensed) {
8175             cfg.cls += ' table-condensed';
8176         }
8177         if (this.responsive) {
8178             cfg.cls += ' table-responsive';
8179         }
8180         
8181         if (this.cls) {
8182             cfg.cls+=  ' ' +this.cls;
8183         }
8184         
8185         // this lot should be simplifed...
8186         var _t = this;
8187         var cp = [
8188             'align',
8189             'bgcolor',
8190             'border',
8191             'cellpadding',
8192             'cellspacing',
8193             'frame',
8194             'rules',
8195             'sortable',
8196             'summary',
8197             'width'
8198         ].forEach(function(k) {
8199             if (_t[k]) {
8200                 cfg[k] = _t[k];
8201             }
8202         });
8203         
8204         
8205         if (this.layout) {
8206             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8207         }
8208         
8209         if(this.store || this.cm){
8210             if(this.headerShow){
8211                 cfg.cn.push(this.renderHeader());
8212             }
8213             
8214             cfg.cn.push(this.renderBody());
8215             
8216             if(this.footerShow){
8217                 cfg.cn.push(this.renderFooter());
8218             }
8219             // where does this come from?
8220             //cfg.cls+=  ' TableGrid';
8221         }
8222         
8223         return { cn : [ cfg ] };
8224     },
8225     
8226     initEvents : function()
8227     {   
8228         if(!this.store || !this.cm){
8229             return;
8230         }
8231         if (this.selModel) {
8232             this.selModel.initEvents();
8233         }
8234         
8235         
8236         //Roo.log('initEvents with ds!!!!');
8237         
8238         this.mainBody = this.el.select('tbody', true).first();
8239         this.mainHead = this.el.select('thead', true).first();
8240         this.mainFoot = this.el.select('tfoot', true).first();
8241         
8242         
8243         
8244         var _this = this;
8245         
8246         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8247             e.on('click', _this.sort, _this);
8248         });
8249         
8250         this.mainBody.on("click", this.onClick, this);
8251         this.mainBody.on("dblclick", this.onDblClick, this);
8252         
8253         // why is this done????? = it breaks dialogs??
8254         //this.parent().el.setStyle('position', 'relative');
8255         
8256         
8257         if (this.footer) {
8258             this.footer.parentId = this.id;
8259             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8260             
8261             if(this.lazyLoad){
8262                 this.el.select('tfoot tr td').first().addClass('hide');
8263             }
8264         } 
8265         
8266         if(this.loadMask) {
8267             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8268         }
8269         
8270         this.store.on('load', this.onLoad, this);
8271         this.store.on('beforeload', this.onBeforeLoad, this);
8272         this.store.on('update', this.onUpdate, this);
8273         this.store.on('add', this.onAdd, this);
8274         this.store.on("clear", this.clear, this);
8275         
8276         this.el.on("contextmenu", this.onContextMenu, this);
8277         
8278         this.mainBody.on('scroll', this.onBodyScroll, this);
8279         
8280         this.cm.on("headerchange", this.onHeaderChange, this);
8281         
8282         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8283         
8284     },
8285     
8286     onContextMenu : function(e, t)
8287     {
8288         this.processEvent("contextmenu", e);
8289     },
8290     
8291     processEvent : function(name, e)
8292     {
8293         if (name != 'touchstart' ) {
8294             this.fireEvent(name, e);    
8295         }
8296         
8297         var t = e.getTarget();
8298         
8299         var cell = Roo.get(t);
8300         
8301         if(!cell){
8302             return;
8303         }
8304         
8305         if(cell.findParent('tfoot', false, true)){
8306             return;
8307         }
8308         
8309         if(cell.findParent('thead', false, true)){
8310             
8311             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8312                 cell = Roo.get(t).findParent('th', false, true);
8313                 if (!cell) {
8314                     Roo.log("failed to find th in thead?");
8315                     Roo.log(e.getTarget());
8316                     return;
8317                 }
8318             }
8319             
8320             var cellIndex = cell.dom.cellIndex;
8321             
8322             var ename = name == 'touchstart' ? 'click' : name;
8323             this.fireEvent("header" + ename, this, cellIndex, e);
8324             
8325             return;
8326         }
8327         
8328         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8329             cell = Roo.get(t).findParent('td', false, true);
8330             if (!cell) {
8331                 Roo.log("failed to find th in tbody?");
8332                 Roo.log(e.getTarget());
8333                 return;
8334             }
8335         }
8336         
8337         var row = cell.findParent('tr', false, true);
8338         var cellIndex = cell.dom.cellIndex;
8339         var rowIndex = row.dom.rowIndex - 1;
8340         
8341         if(row !== false){
8342             
8343             this.fireEvent("row" + name, this, rowIndex, e);
8344             
8345             if(cell !== false){
8346             
8347                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8348             }
8349         }
8350         
8351     },
8352     
8353     onMouseover : function(e, el)
8354     {
8355         var cell = Roo.get(el);
8356         
8357         if(!cell){
8358             return;
8359         }
8360         
8361         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8362             cell = cell.findParent('td', false, true);
8363         }
8364         
8365         var row = cell.findParent('tr', false, true);
8366         var cellIndex = cell.dom.cellIndex;
8367         var rowIndex = row.dom.rowIndex - 1; // start from 0
8368         
8369         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8370         
8371     },
8372     
8373     onMouseout : function(e, el)
8374     {
8375         var cell = Roo.get(el);
8376         
8377         if(!cell){
8378             return;
8379         }
8380         
8381         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8382             cell = cell.findParent('td', false, true);
8383         }
8384         
8385         var row = cell.findParent('tr', false, true);
8386         var cellIndex = cell.dom.cellIndex;
8387         var rowIndex = row.dom.rowIndex - 1; // start from 0
8388         
8389         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8390         
8391     },
8392     
8393     onClick : function(e, el)
8394     {
8395         var cell = Roo.get(el);
8396         
8397         if(!cell || (!this.cellSelection && !this.rowSelection)){
8398             return;
8399         }
8400         
8401         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8402             cell = cell.findParent('td', false, true);
8403         }
8404         
8405         if(!cell || typeof(cell) == 'undefined'){
8406             return;
8407         }
8408         
8409         var row = cell.findParent('tr', false, true);
8410         
8411         if(!row || typeof(row) == 'undefined'){
8412             return;
8413         }
8414         
8415         var cellIndex = cell.dom.cellIndex;
8416         var rowIndex = this.getRowIndex(row);
8417         
8418         // why??? - should these not be based on SelectionModel?
8419         if(this.cellSelection){
8420             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8421         }
8422         
8423         if(this.rowSelection){
8424             this.fireEvent('rowclick', this, row, rowIndex, e);
8425         }
8426         
8427         
8428     },
8429         
8430     onDblClick : function(e,el)
8431     {
8432         var cell = Roo.get(el);
8433         
8434         if(!cell || (!this.cellSelection && !this.rowSelection)){
8435             return;
8436         }
8437         
8438         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8439             cell = cell.findParent('td', false, true);
8440         }
8441         
8442         if(!cell || typeof(cell) == 'undefined'){
8443             return;
8444         }
8445         
8446         var row = cell.findParent('tr', false, true);
8447         
8448         if(!row || typeof(row) == 'undefined'){
8449             return;
8450         }
8451         
8452         var cellIndex = cell.dom.cellIndex;
8453         var rowIndex = this.getRowIndex(row);
8454         
8455         if(this.cellSelection){
8456             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8457         }
8458         
8459         if(this.rowSelection){
8460             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8461         }
8462     },
8463     
8464     sort : function(e,el)
8465     {
8466         var col = Roo.get(el);
8467         
8468         if(!col.hasClass('sortable')){
8469             return;
8470         }
8471         
8472         var sort = col.attr('sort');
8473         var dir = 'ASC';
8474         
8475         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8476             dir = 'DESC';
8477         }
8478         
8479         this.store.sortInfo = {field : sort, direction : dir};
8480         
8481         if (this.footer) {
8482             Roo.log("calling footer first");
8483             this.footer.onClick('first');
8484         } else {
8485         
8486             this.store.load({ params : { start : 0 } });
8487         }
8488     },
8489     
8490     renderHeader : function()
8491     {
8492         var header = {
8493             tag: 'thead',
8494             cn : []
8495         };
8496         
8497         var cm = this.cm;
8498         this.totalWidth = 0;
8499         
8500         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8501             
8502             var config = cm.config[i];
8503             
8504             var c = {
8505                 tag: 'th',
8506                 cls : 'x-hcol-' + i,
8507                 style : '',
8508                 html: cm.getColumnHeader(i)
8509             };
8510             
8511             var hh = '';
8512             
8513             if(typeof(config.sortable) != 'undefined' && config.sortable){
8514                 c.cls = 'sortable';
8515                 c.html = '<i class="glyphicon"></i>' + c.html;
8516             }
8517             
8518             // could use BS4 hidden-..-down 
8519             
8520             if(typeof(config.lgHeader) != 'undefined'){
8521                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8522             }
8523             
8524             if(typeof(config.mdHeader) != 'undefined'){
8525                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8526             }
8527             
8528             if(typeof(config.smHeader) != 'undefined'){
8529                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8530             }
8531             
8532             if(typeof(config.xsHeader) != 'undefined'){
8533                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8534             }
8535             
8536             if(hh.length){
8537                 c.html = hh;
8538             }
8539             
8540             if(typeof(config.tooltip) != 'undefined'){
8541                 c.tooltip = config.tooltip;
8542             }
8543             
8544             if(typeof(config.colspan) != 'undefined'){
8545                 c.colspan = config.colspan;
8546             }
8547             
8548             if(typeof(config.hidden) != 'undefined' && config.hidden){
8549                 c.style += ' display:none;';
8550             }
8551             
8552             if(typeof(config.dataIndex) != 'undefined'){
8553                 c.sort = config.dataIndex;
8554             }
8555             
8556            
8557             
8558             if(typeof(config.align) != 'undefined' && config.align.length){
8559                 c.style += ' text-align:' + config.align + ';';
8560             }
8561             
8562             if(typeof(config.width) != 'undefined'){
8563                 c.style += ' width:' + config.width + 'px;';
8564                 this.totalWidth += config.width;
8565             } else {
8566                 this.totalWidth += 100; // assume minimum of 100 per column?
8567             }
8568             
8569             if(typeof(config.cls) != 'undefined'){
8570                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8571             }
8572             
8573             ['xs','sm','md','lg'].map(function(size){
8574                 
8575                 if(typeof(config[size]) == 'undefined'){
8576                     return;
8577                 }
8578                  
8579                 if (!config[size]) { // 0 = hidden
8580                     // BS 4 '0' is treated as hide that column and below.
8581                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8582                     return;
8583                 }
8584                 
8585                 c.cls += ' col-' + size + '-' + config[size] + (
8586                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8587                 );
8588                 
8589                 
8590             });
8591             
8592             header.cn.push(c)
8593         }
8594         
8595         return header;
8596     },
8597     
8598     renderBody : function()
8599     {
8600         var body = {
8601             tag: 'tbody',
8602             cn : [
8603                 {
8604                     tag: 'tr',
8605                     cn : [
8606                         {
8607                             tag : 'td',
8608                             colspan :  this.cm.getColumnCount()
8609                         }
8610                     ]
8611                 }
8612             ]
8613         };
8614         
8615         return body;
8616     },
8617     
8618     renderFooter : function()
8619     {
8620         var footer = {
8621             tag: 'tfoot',
8622             cn : [
8623                 {
8624                     tag: 'tr',
8625                     cn : [
8626                         {
8627                             tag : 'td',
8628                             colspan :  this.cm.getColumnCount()
8629                         }
8630                     ]
8631                 }
8632             ]
8633         };
8634         
8635         return footer;
8636     },
8637     
8638     
8639     
8640     onLoad : function()
8641     {
8642 //        Roo.log('ds onload');
8643         this.clear();
8644         
8645         var _this = this;
8646         var cm = this.cm;
8647         var ds = this.store;
8648         
8649         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8650             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8651             if (_this.store.sortInfo) {
8652                     
8653                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8654                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8655                 }
8656                 
8657                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8658                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8659                 }
8660             }
8661         });
8662         
8663         var tbody =  this.mainBody;
8664               
8665         if(ds.getCount() > 0){
8666             ds.data.each(function(d,rowIndex){
8667                 var row =  this.renderRow(cm, ds, rowIndex);
8668                 
8669                 tbody.createChild(row);
8670                 
8671                 var _this = this;
8672                 
8673                 if(row.cellObjects.length){
8674                     Roo.each(row.cellObjects, function(r){
8675                         _this.renderCellObject(r);
8676                     })
8677                 }
8678                 
8679             }, this);
8680         }
8681         
8682         var tfoot = this.el.select('tfoot', true).first();
8683         
8684         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8685             
8686             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8687             
8688             var total = this.ds.getTotalCount();
8689             
8690             if(this.footer.pageSize < total){
8691                 this.mainFoot.show();
8692             }
8693         }
8694         
8695         Roo.each(this.el.select('tbody td', true).elements, function(e){
8696             e.on('mouseover', _this.onMouseover, _this);
8697         });
8698         
8699         Roo.each(this.el.select('tbody td', true).elements, function(e){
8700             e.on('mouseout', _this.onMouseout, _this);
8701         });
8702         this.fireEvent('rowsrendered', this);
8703         
8704         this.autoSize();
8705     },
8706     
8707     
8708     onUpdate : function(ds,record)
8709     {
8710         this.refreshRow(record);
8711         this.autoSize();
8712     },
8713     
8714     onRemove : function(ds, record, index, isUpdate){
8715         if(isUpdate !== true){
8716             this.fireEvent("beforerowremoved", this, index, record);
8717         }
8718         var bt = this.mainBody.dom;
8719         
8720         var rows = this.el.select('tbody > tr', true).elements;
8721         
8722         if(typeof(rows[index]) != 'undefined'){
8723             bt.removeChild(rows[index].dom);
8724         }
8725         
8726 //        if(bt.rows[index]){
8727 //            bt.removeChild(bt.rows[index]);
8728 //        }
8729         
8730         if(isUpdate !== true){
8731             //this.stripeRows(index);
8732             //this.syncRowHeights(index, index);
8733             //this.layout();
8734             this.fireEvent("rowremoved", this, index, record);
8735         }
8736     },
8737     
8738     onAdd : function(ds, records, rowIndex)
8739     {
8740         //Roo.log('on Add called');
8741         // - note this does not handle multiple adding very well..
8742         var bt = this.mainBody.dom;
8743         for (var i =0 ; i < records.length;i++) {
8744             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8745             //Roo.log(records[i]);
8746             //Roo.log(this.store.getAt(rowIndex+i));
8747             this.insertRow(this.store, rowIndex + i, false);
8748             return;
8749         }
8750         
8751     },
8752     
8753     
8754     refreshRow : function(record){
8755         var ds = this.store, index;
8756         if(typeof record == 'number'){
8757             index = record;
8758             record = ds.getAt(index);
8759         }else{
8760             index = ds.indexOf(record);
8761             if (index < 0) {
8762                 return; // should not happen - but seems to 
8763             }
8764         }
8765         this.insertRow(ds, index, true);
8766         this.autoSize();
8767         this.onRemove(ds, record, index+1, true);
8768         this.autoSize();
8769         //this.syncRowHeights(index, index);
8770         //this.layout();
8771         this.fireEvent("rowupdated", this, index, record);
8772     },
8773     
8774     insertRow : function(dm, rowIndex, isUpdate){
8775         
8776         if(!isUpdate){
8777             this.fireEvent("beforerowsinserted", this, rowIndex);
8778         }
8779             //var s = this.getScrollState();
8780         var row = this.renderRow(this.cm, this.store, rowIndex);
8781         // insert before rowIndex..
8782         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8783         
8784         var _this = this;
8785                 
8786         if(row.cellObjects.length){
8787             Roo.each(row.cellObjects, function(r){
8788                 _this.renderCellObject(r);
8789             })
8790         }
8791             
8792         if(!isUpdate){
8793             this.fireEvent("rowsinserted", this, rowIndex);
8794             //this.syncRowHeights(firstRow, lastRow);
8795             //this.stripeRows(firstRow);
8796             //this.layout();
8797         }
8798         
8799     },
8800     
8801     
8802     getRowDom : function(rowIndex)
8803     {
8804         var rows = this.el.select('tbody > tr', true).elements;
8805         
8806         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8807         
8808     },
8809     // returns the object tree for a tr..
8810   
8811     
8812     renderRow : function(cm, ds, rowIndex) 
8813     {
8814         var d = ds.getAt(rowIndex);
8815         
8816         var row = {
8817             tag : 'tr',
8818             cls : 'x-row-' + rowIndex,
8819             cn : []
8820         };
8821             
8822         var cellObjects = [];
8823         
8824         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8825             var config = cm.config[i];
8826             
8827             var renderer = cm.getRenderer(i);
8828             var value = '';
8829             var id = false;
8830             
8831             if(typeof(renderer) !== 'undefined'){
8832                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8833             }
8834             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8835             // and are rendered into the cells after the row is rendered - using the id for the element.
8836             
8837             if(typeof(value) === 'object'){
8838                 id = Roo.id();
8839                 cellObjects.push({
8840                     container : id,
8841                     cfg : value 
8842                 })
8843             }
8844             
8845             var rowcfg = {
8846                 record: d,
8847                 rowIndex : rowIndex,
8848                 colIndex : i,
8849                 rowClass : ''
8850             };
8851
8852             this.fireEvent('rowclass', this, rowcfg);
8853             
8854             var td = {
8855                 tag: 'td',
8856                 cls : rowcfg.rowClass + ' x-col-' + i,
8857                 style: '',
8858                 html: (typeof(value) === 'object') ? '' : value
8859             };
8860             
8861             if (id) {
8862                 td.id = id;
8863             }
8864             
8865             if(typeof(config.colspan) != 'undefined'){
8866                 td.colspan = config.colspan;
8867             }
8868             
8869             if(typeof(config.hidden) != 'undefined' && config.hidden){
8870                 td.style += ' display:none;';
8871             }
8872             
8873             if(typeof(config.align) != 'undefined' && config.align.length){
8874                 td.style += ' text-align:' + config.align + ';';
8875             }
8876             if(typeof(config.valign) != 'undefined' && config.valign.length){
8877                 td.style += ' vertical-align:' + config.valign + ';';
8878             }
8879             
8880             if(typeof(config.width) != 'undefined'){
8881                 td.style += ' width:' +  config.width + 'px;';
8882             }
8883             
8884             if(typeof(config.cursor) != 'undefined'){
8885                 td.style += ' cursor:' +  config.cursor + ';';
8886             }
8887             
8888             if(typeof(config.cls) != 'undefined'){
8889                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8890             }
8891             
8892             ['xs','sm','md','lg'].map(function(size){
8893                 
8894                 if(typeof(config[size]) == 'undefined'){
8895                     return;
8896                 }
8897                 
8898                 
8899                   
8900                 if (!config[size]) { // 0 = hidden
8901                     // BS 4 '0' is treated as hide that column and below.
8902                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8903                     return;
8904                 }
8905                 
8906                 td.cls += ' col-' + size + '-' + config[size] + (
8907                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8908                 );
8909                  
8910
8911             });
8912             
8913             row.cn.push(td);
8914            
8915         }
8916         
8917         row.cellObjects = cellObjects;
8918         
8919         return row;
8920           
8921     },
8922     
8923     
8924     
8925     onBeforeLoad : function()
8926     {
8927         
8928     },
8929      /**
8930      * Remove all rows
8931      */
8932     clear : function()
8933     {
8934         this.el.select('tbody', true).first().dom.innerHTML = '';
8935     },
8936     /**
8937      * Show or hide a row.
8938      * @param {Number} rowIndex to show or hide
8939      * @param {Boolean} state hide
8940      */
8941     setRowVisibility : function(rowIndex, state)
8942     {
8943         var bt = this.mainBody.dom;
8944         
8945         var rows = this.el.select('tbody > tr', true).elements;
8946         
8947         if(typeof(rows[rowIndex]) == 'undefined'){
8948             return;
8949         }
8950         rows[rowIndex].dom.style.display = state ? '' : 'none';
8951     },
8952     
8953     
8954     getSelectionModel : function(){
8955         if(!this.selModel){
8956             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8957         }
8958         return this.selModel;
8959     },
8960     /*
8961      * Render the Roo.bootstrap object from renderder
8962      */
8963     renderCellObject : function(r)
8964     {
8965         var _this = this;
8966         
8967         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8968         
8969         var t = r.cfg.render(r.container);
8970         
8971         if(r.cfg.cn){
8972             Roo.each(r.cfg.cn, function(c){
8973                 var child = {
8974                     container: t.getChildContainer(),
8975                     cfg: c
8976                 };
8977                 _this.renderCellObject(child);
8978             })
8979         }
8980     },
8981     
8982     getRowIndex : function(row)
8983     {
8984         var rowIndex = -1;
8985         
8986         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8987             if(el != row){
8988                 return;
8989             }
8990             
8991             rowIndex = index;
8992         });
8993         
8994         return rowIndex;
8995     },
8996      /**
8997      * Returns the grid's underlying element = used by panel.Grid
8998      * @return {Element} The element
8999      */
9000     getGridEl : function(){
9001         return this.el;
9002     },
9003      /**
9004      * Forces a resize - used by panel.Grid
9005      * @return {Element} The element
9006      */
9007     autoSize : function()
9008     {
9009         //var ctr = Roo.get(this.container.dom.parentElement);
9010         var ctr = Roo.get(this.el.dom);
9011         
9012         var thd = this.getGridEl().select('thead',true).first();
9013         var tbd = this.getGridEl().select('tbody', true).first();
9014         var tfd = this.getGridEl().select('tfoot', true).first();
9015         
9016         var cw = ctr.getWidth();
9017         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9018         
9019         if (tbd) {
9020             
9021             tbd.setWidth(ctr.getWidth());
9022             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9023             // this needs fixing for various usage - currently only hydra job advers I think..
9024             //tdb.setHeight(
9025             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9026             //); 
9027             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9028             cw -= barsize;
9029         }
9030         cw = Math.max(cw, this.totalWidth);
9031         this.getGridEl().select('tbody tr',true).setWidth(cw);
9032         
9033         // resize 'expandable coloumn?
9034         
9035         return; // we doe not have a view in this design..
9036         
9037     },
9038     onBodyScroll: function()
9039     {
9040         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9041         if(this.mainHead){
9042             this.mainHead.setStyle({
9043                 'position' : 'relative',
9044                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9045             });
9046         }
9047         
9048         if(this.lazyLoad){
9049             
9050             var scrollHeight = this.mainBody.dom.scrollHeight;
9051             
9052             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9053             
9054             var height = this.mainBody.getHeight();
9055             
9056             if(scrollHeight - height == scrollTop) {
9057                 
9058                 var total = this.ds.getTotalCount();
9059                 
9060                 if(this.footer.cursor + this.footer.pageSize < total){
9061                     
9062                     this.footer.ds.load({
9063                         params : {
9064                             start : this.footer.cursor + this.footer.pageSize,
9065                             limit : this.footer.pageSize
9066                         },
9067                         add : true
9068                     });
9069                 }
9070             }
9071             
9072         }
9073     },
9074     
9075     onHeaderChange : function()
9076     {
9077         var header = this.renderHeader();
9078         var table = this.el.select('table', true).first();
9079         
9080         this.mainHead.remove();
9081         this.mainHead = table.createChild(header, this.mainBody, false);
9082     },
9083     
9084     onHiddenChange : function(colModel, colIndex, hidden)
9085     {
9086         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9087         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9088         
9089         this.CSS.updateRule(thSelector, "display", "");
9090         this.CSS.updateRule(tdSelector, "display", "");
9091         
9092         if(hidden){
9093             this.CSS.updateRule(thSelector, "display", "none");
9094             this.CSS.updateRule(tdSelector, "display", "none");
9095         }
9096         
9097         this.onHeaderChange();
9098         this.onLoad();
9099     },
9100     
9101     setColumnWidth: function(col_index, width)
9102     {
9103         // width = "md-2 xs-2..."
9104         if(!this.colModel.config[col_index]) {
9105             return;
9106         }
9107         
9108         var w = width.split(" ");
9109         
9110         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9111         
9112         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9113         
9114         
9115         for(var j = 0; j < w.length; j++) {
9116             
9117             if(!w[j]) {
9118                 continue;
9119             }
9120             
9121             var size_cls = w[j].split("-");
9122             
9123             if(!Number.isInteger(size_cls[1] * 1)) {
9124                 continue;
9125             }
9126             
9127             if(!this.colModel.config[col_index][size_cls[0]]) {
9128                 continue;
9129             }
9130             
9131             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9132                 continue;
9133             }
9134             
9135             h_row[0].classList.replace(
9136                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9137                 "col-"+size_cls[0]+"-"+size_cls[1]
9138             );
9139             
9140             for(var i = 0; i < rows.length; i++) {
9141                 
9142                 var size_cls = w[j].split("-");
9143                 
9144                 if(!Number.isInteger(size_cls[1] * 1)) {
9145                     continue;
9146                 }
9147                 
9148                 if(!this.colModel.config[col_index][size_cls[0]]) {
9149                     continue;
9150                 }
9151                 
9152                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9153                     continue;
9154                 }
9155                 
9156                 rows[i].classList.replace(
9157                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9158                     "col-"+size_cls[0]+"-"+size_cls[1]
9159                 );
9160             }
9161             
9162             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9163         }
9164     }
9165 });
9166
9167  
9168
9169  /*
9170  * - LGPL
9171  *
9172  * table cell
9173  * 
9174  */
9175
9176 /**
9177  * @class Roo.bootstrap.TableCell
9178  * @extends Roo.bootstrap.Component
9179  * Bootstrap TableCell class
9180  * @cfg {String} html cell contain text
9181  * @cfg {String} cls cell class
9182  * @cfg {String} tag cell tag (td|th) default td
9183  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9184  * @cfg {String} align Aligns the content in a cell
9185  * @cfg {String} axis Categorizes cells
9186  * @cfg {String} bgcolor Specifies the background color of a cell
9187  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9188  * @cfg {Number} colspan Specifies the number of columns a cell should span
9189  * @cfg {String} headers Specifies one or more header cells a cell is related to
9190  * @cfg {Number} height Sets the height of a cell
9191  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9192  * @cfg {Number} rowspan Sets the number of rows a cell should span
9193  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9194  * @cfg {String} valign Vertical aligns the content in a cell
9195  * @cfg {Number} width Specifies the width of a cell
9196  * 
9197  * @constructor
9198  * Create a new TableCell
9199  * @param {Object} config The config object
9200  */
9201
9202 Roo.bootstrap.TableCell = function(config){
9203     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9204 };
9205
9206 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9207     
9208     html: false,
9209     cls: false,
9210     tag: false,
9211     abbr: false,
9212     align: false,
9213     axis: false,
9214     bgcolor: false,
9215     charoff: false,
9216     colspan: false,
9217     headers: false,
9218     height: false,
9219     nowrap: false,
9220     rowspan: false,
9221     scope: false,
9222     valign: false,
9223     width: false,
9224     
9225     
9226     getAutoCreate : function(){
9227         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9228         
9229         cfg = {
9230             tag: 'td'
9231         };
9232         
9233         if(this.tag){
9234             cfg.tag = this.tag;
9235         }
9236         
9237         if (this.html) {
9238             cfg.html=this.html
9239         }
9240         if (this.cls) {
9241             cfg.cls=this.cls
9242         }
9243         if (this.abbr) {
9244             cfg.abbr=this.abbr
9245         }
9246         if (this.align) {
9247             cfg.align=this.align
9248         }
9249         if (this.axis) {
9250             cfg.axis=this.axis
9251         }
9252         if (this.bgcolor) {
9253             cfg.bgcolor=this.bgcolor
9254         }
9255         if (this.charoff) {
9256             cfg.charoff=this.charoff
9257         }
9258         if (this.colspan) {
9259             cfg.colspan=this.colspan
9260         }
9261         if (this.headers) {
9262             cfg.headers=this.headers
9263         }
9264         if (this.height) {
9265             cfg.height=this.height
9266         }
9267         if (this.nowrap) {
9268             cfg.nowrap=this.nowrap
9269         }
9270         if (this.rowspan) {
9271             cfg.rowspan=this.rowspan
9272         }
9273         if (this.scope) {
9274             cfg.scope=this.scope
9275         }
9276         if (this.valign) {
9277             cfg.valign=this.valign
9278         }
9279         if (this.width) {
9280             cfg.width=this.width
9281         }
9282         
9283         
9284         return cfg;
9285     }
9286    
9287 });
9288
9289  
9290
9291  /*
9292  * - LGPL
9293  *
9294  * table row
9295  * 
9296  */
9297
9298 /**
9299  * @class Roo.bootstrap.TableRow
9300  * @extends Roo.bootstrap.Component
9301  * Bootstrap TableRow class
9302  * @cfg {String} cls row class
9303  * @cfg {String} align Aligns the content in a table row
9304  * @cfg {String} bgcolor Specifies a background color for a table row
9305  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9306  * @cfg {String} valign Vertical aligns the content in a table row
9307  * 
9308  * @constructor
9309  * Create a new TableRow
9310  * @param {Object} config The config object
9311  */
9312
9313 Roo.bootstrap.TableRow = function(config){
9314     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9315 };
9316
9317 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9318     
9319     cls: false,
9320     align: false,
9321     bgcolor: false,
9322     charoff: false,
9323     valign: false,
9324     
9325     getAutoCreate : function(){
9326         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9327         
9328         cfg = {
9329             tag: 'tr'
9330         };
9331             
9332         if(this.cls){
9333             cfg.cls = this.cls;
9334         }
9335         if(this.align){
9336             cfg.align = this.align;
9337         }
9338         if(this.bgcolor){
9339             cfg.bgcolor = this.bgcolor;
9340         }
9341         if(this.charoff){
9342             cfg.charoff = this.charoff;
9343         }
9344         if(this.valign){
9345             cfg.valign = this.valign;
9346         }
9347         
9348         return cfg;
9349     }
9350    
9351 });
9352
9353  
9354
9355  /*
9356  * - LGPL
9357  *
9358  * table body
9359  * 
9360  */
9361
9362 /**
9363  * @class Roo.bootstrap.TableBody
9364  * @extends Roo.bootstrap.Component
9365  * Bootstrap TableBody class
9366  * @cfg {String} cls element class
9367  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9368  * @cfg {String} align Aligns the content inside the element
9369  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9370  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9371  * 
9372  * @constructor
9373  * Create a new TableBody
9374  * @param {Object} config The config object
9375  */
9376
9377 Roo.bootstrap.TableBody = function(config){
9378     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9379 };
9380
9381 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9382     
9383     cls: false,
9384     tag: false,
9385     align: false,
9386     charoff: false,
9387     valign: false,
9388     
9389     getAutoCreate : function(){
9390         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9391         
9392         cfg = {
9393             tag: 'tbody'
9394         };
9395             
9396         if (this.cls) {
9397             cfg.cls=this.cls
9398         }
9399         if(this.tag){
9400             cfg.tag = this.tag;
9401         }
9402         
9403         if(this.align){
9404             cfg.align = this.align;
9405         }
9406         if(this.charoff){
9407             cfg.charoff = this.charoff;
9408         }
9409         if(this.valign){
9410             cfg.valign = this.valign;
9411         }
9412         
9413         return cfg;
9414     }
9415     
9416     
9417 //    initEvents : function()
9418 //    {
9419 //        
9420 //        if(!this.store){
9421 //            return;
9422 //        }
9423 //        
9424 //        this.store = Roo.factory(this.store, Roo.data);
9425 //        this.store.on('load', this.onLoad, this);
9426 //        
9427 //        this.store.load();
9428 //        
9429 //    },
9430 //    
9431 //    onLoad: function () 
9432 //    {   
9433 //        this.fireEvent('load', this);
9434 //    }
9435 //    
9436 //   
9437 });
9438
9439  
9440
9441  /*
9442  * Based on:
9443  * Ext JS Library 1.1.1
9444  * Copyright(c) 2006-2007, Ext JS, LLC.
9445  *
9446  * Originally Released Under LGPL - original licence link has changed is not relivant.
9447  *
9448  * Fork - LGPL
9449  * <script type="text/javascript">
9450  */
9451
9452 // as we use this in bootstrap.
9453 Roo.namespace('Roo.form');
9454  /**
9455  * @class Roo.form.Action
9456  * Internal Class used to handle form actions
9457  * @constructor
9458  * @param {Roo.form.BasicForm} el The form element or its id
9459  * @param {Object} config Configuration options
9460  */
9461
9462  
9463  
9464 // define the action interface
9465 Roo.form.Action = function(form, options){
9466     this.form = form;
9467     this.options = options || {};
9468 };
9469 /**
9470  * Client Validation Failed
9471  * @const 
9472  */
9473 Roo.form.Action.CLIENT_INVALID = 'client';
9474 /**
9475  * Server Validation Failed
9476  * @const 
9477  */
9478 Roo.form.Action.SERVER_INVALID = 'server';
9479  /**
9480  * Connect to Server Failed
9481  * @const 
9482  */
9483 Roo.form.Action.CONNECT_FAILURE = 'connect';
9484 /**
9485  * Reading Data from Server Failed
9486  * @const 
9487  */
9488 Roo.form.Action.LOAD_FAILURE = 'load';
9489
9490 Roo.form.Action.prototype = {
9491     type : 'default',
9492     failureType : undefined,
9493     response : undefined,
9494     result : undefined,
9495
9496     // interface method
9497     run : function(options){
9498
9499     },
9500
9501     // interface method
9502     success : function(response){
9503
9504     },
9505
9506     // interface method
9507     handleResponse : function(response){
9508
9509     },
9510
9511     // default connection failure
9512     failure : function(response){
9513         
9514         this.response = response;
9515         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9516         this.form.afterAction(this, false);
9517     },
9518
9519     processResponse : function(response){
9520         this.response = response;
9521         if(!response.responseText){
9522             return true;
9523         }
9524         this.result = this.handleResponse(response);
9525         return this.result;
9526     },
9527
9528     // utility functions used internally
9529     getUrl : function(appendParams){
9530         var url = this.options.url || this.form.url || this.form.el.dom.action;
9531         if(appendParams){
9532             var p = this.getParams();
9533             if(p){
9534                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9535             }
9536         }
9537         return url;
9538     },
9539
9540     getMethod : function(){
9541         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9542     },
9543
9544     getParams : function(){
9545         var bp = this.form.baseParams;
9546         var p = this.options.params;
9547         if(p){
9548             if(typeof p == "object"){
9549                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9550             }else if(typeof p == 'string' && bp){
9551                 p += '&' + Roo.urlEncode(bp);
9552             }
9553         }else if(bp){
9554             p = Roo.urlEncode(bp);
9555         }
9556         return p;
9557     },
9558
9559     createCallback : function(){
9560         return {
9561             success: this.success,
9562             failure: this.failure,
9563             scope: this,
9564             timeout: (this.form.timeout*1000),
9565             upload: this.form.fileUpload ? this.success : undefined
9566         };
9567     }
9568 };
9569
9570 Roo.form.Action.Submit = function(form, options){
9571     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9572 };
9573
9574 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9575     type : 'submit',
9576
9577     haveProgress : false,
9578     uploadComplete : false,
9579     
9580     // uploadProgress indicator.
9581     uploadProgress : function()
9582     {
9583         if (!this.form.progressUrl) {
9584             return;
9585         }
9586         
9587         if (!this.haveProgress) {
9588             Roo.MessageBox.progress("Uploading", "Uploading");
9589         }
9590         if (this.uploadComplete) {
9591            Roo.MessageBox.hide();
9592            return;
9593         }
9594         
9595         this.haveProgress = true;
9596    
9597         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9598         
9599         var c = new Roo.data.Connection();
9600         c.request({
9601             url : this.form.progressUrl,
9602             params: {
9603                 id : uid
9604             },
9605             method: 'GET',
9606             success : function(req){
9607                //console.log(data);
9608                 var rdata = false;
9609                 var edata;
9610                 try  {
9611                    rdata = Roo.decode(req.responseText)
9612                 } catch (e) {
9613                     Roo.log("Invalid data from server..");
9614                     Roo.log(edata);
9615                     return;
9616                 }
9617                 if (!rdata || !rdata.success) {
9618                     Roo.log(rdata);
9619                     Roo.MessageBox.alert(Roo.encode(rdata));
9620                     return;
9621                 }
9622                 var data = rdata.data;
9623                 
9624                 if (this.uploadComplete) {
9625                    Roo.MessageBox.hide();
9626                    return;
9627                 }
9628                    
9629                 if (data){
9630                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9631                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9632                     );
9633                 }
9634                 this.uploadProgress.defer(2000,this);
9635             },
9636        
9637             failure: function(data) {
9638                 Roo.log('progress url failed ');
9639                 Roo.log(data);
9640             },
9641             scope : this
9642         });
9643            
9644     },
9645     
9646     
9647     run : function()
9648     {
9649         // run get Values on the form, so it syncs any secondary forms.
9650         this.form.getValues();
9651         
9652         var o = this.options;
9653         var method = this.getMethod();
9654         var isPost = method == 'POST';
9655         if(o.clientValidation === false || this.form.isValid()){
9656             
9657             if (this.form.progressUrl) {
9658                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9659                     (new Date() * 1) + '' + Math.random());
9660                     
9661             } 
9662             
9663             
9664             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9665                 form:this.form.el.dom,
9666                 url:this.getUrl(!isPost),
9667                 method: method,
9668                 params:isPost ? this.getParams() : null,
9669                 isUpload: this.form.fileUpload,
9670                 formData : this.form.formData
9671             }));
9672             
9673             this.uploadProgress();
9674
9675         }else if (o.clientValidation !== false){ // client validation failed
9676             this.failureType = Roo.form.Action.CLIENT_INVALID;
9677             this.form.afterAction(this, false);
9678         }
9679     },
9680
9681     success : function(response)
9682     {
9683         this.uploadComplete= true;
9684         if (this.haveProgress) {
9685             Roo.MessageBox.hide();
9686         }
9687         
9688         
9689         var result = this.processResponse(response);
9690         if(result === true || result.success){
9691             this.form.afterAction(this, true);
9692             return;
9693         }
9694         if(result.errors){
9695             this.form.markInvalid(result.errors);
9696             this.failureType = Roo.form.Action.SERVER_INVALID;
9697         }
9698         this.form.afterAction(this, false);
9699     },
9700     failure : function(response)
9701     {
9702         this.uploadComplete= true;
9703         if (this.haveProgress) {
9704             Roo.MessageBox.hide();
9705         }
9706         
9707         this.response = response;
9708         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9709         this.form.afterAction(this, false);
9710     },
9711     
9712     handleResponse : function(response){
9713         if(this.form.errorReader){
9714             var rs = this.form.errorReader.read(response);
9715             var errors = [];
9716             if(rs.records){
9717                 for(var i = 0, len = rs.records.length; i < len; i++) {
9718                     var r = rs.records[i];
9719                     errors[i] = r.data;
9720                 }
9721             }
9722             if(errors.length < 1){
9723                 errors = null;
9724             }
9725             return {
9726                 success : rs.success,
9727                 errors : errors
9728             };
9729         }
9730         var ret = false;
9731         try {
9732             ret = Roo.decode(response.responseText);
9733         } catch (e) {
9734             ret = {
9735                 success: false,
9736                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9737                 errors : []
9738             };
9739         }
9740         return ret;
9741         
9742     }
9743 });
9744
9745
9746 Roo.form.Action.Load = function(form, options){
9747     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9748     this.reader = this.form.reader;
9749 };
9750
9751 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9752     type : 'load',
9753
9754     run : function(){
9755         
9756         Roo.Ajax.request(Roo.apply(
9757                 this.createCallback(), {
9758                     method:this.getMethod(),
9759                     url:this.getUrl(false),
9760                     params:this.getParams()
9761         }));
9762     },
9763
9764     success : function(response){
9765         
9766         var result = this.processResponse(response);
9767         if(result === true || !result.success || !result.data){
9768             this.failureType = Roo.form.Action.LOAD_FAILURE;
9769             this.form.afterAction(this, false);
9770             return;
9771         }
9772         this.form.clearInvalid();
9773         this.form.setValues(result.data);
9774         this.form.afterAction(this, true);
9775     },
9776
9777     handleResponse : function(response){
9778         if(this.form.reader){
9779             var rs = this.form.reader.read(response);
9780             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9781             return {
9782                 success : rs.success,
9783                 data : data
9784             };
9785         }
9786         return Roo.decode(response.responseText);
9787     }
9788 });
9789
9790 Roo.form.Action.ACTION_TYPES = {
9791     'load' : Roo.form.Action.Load,
9792     'submit' : Roo.form.Action.Submit
9793 };/*
9794  * - LGPL
9795  *
9796  * form
9797  *
9798  */
9799
9800 /**
9801  * @class Roo.bootstrap.Form
9802  * @extends Roo.bootstrap.Component
9803  * Bootstrap Form class
9804  * @cfg {String} method  GET | POST (default POST)
9805  * @cfg {String} labelAlign top | left (default top)
9806  * @cfg {String} align left  | right - for navbars
9807  * @cfg {Boolean} loadMask load mask when submit (default true)
9808
9809  *
9810  * @constructor
9811  * Create a new Form
9812  * @param {Object} config The config object
9813  */
9814
9815
9816 Roo.bootstrap.Form = function(config){
9817     
9818     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9819     
9820     Roo.bootstrap.Form.popover.apply();
9821     
9822     this.addEvents({
9823         /**
9824          * @event clientvalidation
9825          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9826          * @param {Form} this
9827          * @param {Boolean} valid true if the form has passed client-side validation
9828          */
9829         clientvalidation: true,
9830         /**
9831          * @event beforeaction
9832          * Fires before any action is performed. Return false to cancel the action.
9833          * @param {Form} this
9834          * @param {Action} action The action to be performed
9835          */
9836         beforeaction: true,
9837         /**
9838          * @event actionfailed
9839          * Fires when an action fails.
9840          * @param {Form} this
9841          * @param {Action} action The action that failed
9842          */
9843         actionfailed : true,
9844         /**
9845          * @event actioncomplete
9846          * Fires when an action is completed.
9847          * @param {Form} this
9848          * @param {Action} action The action that completed
9849          */
9850         actioncomplete : true
9851     });
9852 };
9853
9854 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9855
9856      /**
9857      * @cfg {String} method
9858      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9859      */
9860     method : 'POST',
9861     /**
9862      * @cfg {String} url
9863      * The URL to use for form actions if one isn't supplied in the action options.
9864      */
9865     /**
9866      * @cfg {Boolean} fileUpload
9867      * Set to true if this form is a file upload.
9868      */
9869
9870     /**
9871      * @cfg {Object} baseParams
9872      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9873      */
9874
9875     /**
9876      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9877      */
9878     timeout: 30,
9879     /**
9880      * @cfg {Sting} align (left|right) for navbar forms
9881      */
9882     align : 'left',
9883
9884     // private
9885     activeAction : null,
9886
9887     /**
9888      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9889      * element by passing it or its id or mask the form itself by passing in true.
9890      * @type Mixed
9891      */
9892     waitMsgTarget : false,
9893
9894     loadMask : true,
9895     
9896     /**
9897      * @cfg {Boolean} errorMask (true|false) default false
9898      */
9899     errorMask : false,
9900     
9901     /**
9902      * @cfg {Number} maskOffset Default 100
9903      */
9904     maskOffset : 100,
9905     
9906     /**
9907      * @cfg {Boolean} maskBody
9908      */
9909     maskBody : false,
9910
9911     getAutoCreate : function(){
9912
9913         var cfg = {
9914             tag: 'form',
9915             method : this.method || 'POST',
9916             id : this.id || Roo.id(),
9917             cls : ''
9918         };
9919         if (this.parent().xtype.match(/^Nav/)) {
9920             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9921
9922         }
9923
9924         if (this.labelAlign == 'left' ) {
9925             cfg.cls += ' form-horizontal';
9926         }
9927
9928
9929         return cfg;
9930     },
9931     initEvents : function()
9932     {
9933         this.el.on('submit', this.onSubmit, this);
9934         // this was added as random key presses on the form where triggering form submit.
9935         this.el.on('keypress', function(e) {
9936             if (e.getCharCode() != 13) {
9937                 return true;
9938             }
9939             // we might need to allow it for textareas.. and some other items.
9940             // check e.getTarget().
9941
9942             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9943                 return true;
9944             }
9945
9946             Roo.log("keypress blocked");
9947
9948             e.preventDefault();
9949             return false;
9950         });
9951         
9952     },
9953     // private
9954     onSubmit : function(e){
9955         e.stopEvent();
9956     },
9957
9958      /**
9959      * Returns true if client-side validation on the form is successful.
9960      * @return Boolean
9961      */
9962     isValid : function(){
9963         var items = this.getItems();
9964         var valid = true;
9965         var target = false;
9966         
9967         items.each(function(f){
9968             
9969             if(f.validate()){
9970                 return;
9971             }
9972             
9973             Roo.log('invalid field: ' + f.name);
9974             
9975             valid = false;
9976
9977             if(!target && f.el.isVisible(true)){
9978                 target = f;
9979             }
9980            
9981         });
9982         
9983         if(this.errorMask && !valid){
9984             Roo.bootstrap.Form.popover.mask(this, target);
9985         }
9986         
9987         return valid;
9988     },
9989     
9990     /**
9991      * Returns true if any fields in this form have changed since their original load.
9992      * @return Boolean
9993      */
9994     isDirty : function(){
9995         var dirty = false;
9996         var items = this.getItems();
9997         items.each(function(f){
9998            if(f.isDirty()){
9999                dirty = true;
10000                return false;
10001            }
10002            return true;
10003         });
10004         return dirty;
10005     },
10006      /**
10007      * Performs a predefined action (submit or load) or custom actions you define on this form.
10008      * @param {String} actionName The name of the action type
10009      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10010      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10011      * accept other config options):
10012      * <pre>
10013 Property          Type             Description
10014 ----------------  ---------------  ----------------------------------------------------------------------------------
10015 url               String           The url for the action (defaults to the form's url)
10016 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10017 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10018 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10019                                    validate the form on the client (defaults to false)
10020      * </pre>
10021      * @return {BasicForm} this
10022      */
10023     doAction : function(action, options){
10024         if(typeof action == 'string'){
10025             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10026         }
10027         if(this.fireEvent('beforeaction', this, action) !== false){
10028             this.beforeAction(action);
10029             action.run.defer(100, action);
10030         }
10031         return this;
10032     },
10033
10034     // private
10035     beforeAction : function(action){
10036         var o = action.options;
10037         
10038         if(this.loadMask){
10039             
10040             if(this.maskBody){
10041                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10042             } else {
10043                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10044             }
10045         }
10046         // not really supported yet.. ??
10047
10048         //if(this.waitMsgTarget === true){
10049         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10050         //}else if(this.waitMsgTarget){
10051         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10052         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10053         //}else {
10054         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10055        // }
10056
10057     },
10058
10059     // private
10060     afterAction : function(action, success){
10061         this.activeAction = null;
10062         var o = action.options;
10063
10064         if(this.loadMask){
10065             
10066             if(this.maskBody){
10067                 Roo.get(document.body).unmask();
10068             } else {
10069                 this.el.unmask();
10070             }
10071         }
10072         
10073         //if(this.waitMsgTarget === true){
10074 //            this.el.unmask();
10075         //}else if(this.waitMsgTarget){
10076         //    this.waitMsgTarget.unmask();
10077         //}else{
10078         //    Roo.MessageBox.updateProgress(1);
10079         //    Roo.MessageBox.hide();
10080        // }
10081         //
10082         if(success){
10083             if(o.reset){
10084                 this.reset();
10085             }
10086             Roo.callback(o.success, o.scope, [this, action]);
10087             this.fireEvent('actioncomplete', this, action);
10088
10089         }else{
10090
10091             // failure condition..
10092             // we have a scenario where updates need confirming.
10093             // eg. if a locking scenario exists..
10094             // we look for { errors : { needs_confirm : true }} in the response.
10095             if (
10096                 (typeof(action.result) != 'undefined')  &&
10097                 (typeof(action.result.errors) != 'undefined')  &&
10098                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10099            ){
10100                 var _t = this;
10101                 Roo.log("not supported yet");
10102                  /*
10103
10104                 Roo.MessageBox.confirm(
10105                     "Change requires confirmation",
10106                     action.result.errorMsg,
10107                     function(r) {
10108                         if (r != 'yes') {
10109                             return;
10110                         }
10111                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10112                     }
10113
10114                 );
10115                 */
10116
10117
10118                 return;
10119             }
10120
10121             Roo.callback(o.failure, o.scope, [this, action]);
10122             // show an error message if no failed handler is set..
10123             if (!this.hasListener('actionfailed')) {
10124                 Roo.log("need to add dialog support");
10125                 /*
10126                 Roo.MessageBox.alert("Error",
10127                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10128                         action.result.errorMsg :
10129                         "Saving Failed, please check your entries or try again"
10130                 );
10131                 */
10132             }
10133
10134             this.fireEvent('actionfailed', this, action);
10135         }
10136
10137     },
10138     /**
10139      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10140      * @param {String} id The value to search for
10141      * @return Field
10142      */
10143     findField : function(id){
10144         var items = this.getItems();
10145         var field = items.get(id);
10146         if(!field){
10147              items.each(function(f){
10148                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10149                     field = f;
10150                     return false;
10151                 }
10152                 return true;
10153             });
10154         }
10155         return field || null;
10156     },
10157      /**
10158      * Mark fields in this form invalid in bulk.
10159      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10160      * @return {BasicForm} this
10161      */
10162     markInvalid : function(errors){
10163         if(errors instanceof Array){
10164             for(var i = 0, len = errors.length; i < len; i++){
10165                 var fieldError = errors[i];
10166                 var f = this.findField(fieldError.id);
10167                 if(f){
10168                     f.markInvalid(fieldError.msg);
10169                 }
10170             }
10171         }else{
10172             var field, id;
10173             for(id in errors){
10174                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10175                     field.markInvalid(errors[id]);
10176                 }
10177             }
10178         }
10179         //Roo.each(this.childForms || [], function (f) {
10180         //    f.markInvalid(errors);
10181         //});
10182
10183         return this;
10184     },
10185
10186     /**
10187      * Set values for fields in this form in bulk.
10188      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10189      * @return {BasicForm} this
10190      */
10191     setValues : function(values){
10192         if(values instanceof Array){ // array of objects
10193             for(var i = 0, len = values.length; i < len; i++){
10194                 var v = values[i];
10195                 var f = this.findField(v.id);
10196                 if(f){
10197                     f.setValue(v.value);
10198                     if(this.trackResetOnLoad){
10199                         f.originalValue = f.getValue();
10200                     }
10201                 }
10202             }
10203         }else{ // object hash
10204             var field, id;
10205             for(id in values){
10206                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10207
10208                     if (field.setFromData &&
10209                         field.valueField &&
10210                         field.displayField &&
10211                         // combos' with local stores can
10212                         // be queried via setValue()
10213                         // to set their value..
10214                         (field.store && !field.store.isLocal)
10215                         ) {
10216                         // it's a combo
10217                         var sd = { };
10218                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10219                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10220                         field.setFromData(sd);
10221
10222                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10223                         
10224                         field.setFromData(values);
10225                         
10226                     } else {
10227                         field.setValue(values[id]);
10228                     }
10229
10230
10231                     if(this.trackResetOnLoad){
10232                         field.originalValue = field.getValue();
10233                     }
10234                 }
10235             }
10236         }
10237
10238         //Roo.each(this.childForms || [], function (f) {
10239         //    f.setValues(values);
10240         //});
10241
10242         return this;
10243     },
10244
10245     /**
10246      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10247      * they are returned as an array.
10248      * @param {Boolean} asString
10249      * @return {Object}
10250      */
10251     getValues : function(asString){
10252         //if (this.childForms) {
10253             // copy values from the child forms
10254         //    Roo.each(this.childForms, function (f) {
10255         //        this.setValues(f.getValues());
10256         //    }, this);
10257         //}
10258
10259
10260
10261         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10262         if(asString === true){
10263             return fs;
10264         }
10265         return Roo.urlDecode(fs);
10266     },
10267
10268     /**
10269      * Returns the fields in this form as an object with key/value pairs.
10270      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10271      * @return {Object}
10272      */
10273     getFieldValues : function(with_hidden)
10274     {
10275         var items = this.getItems();
10276         var ret = {};
10277         items.each(function(f){
10278             
10279             if (!f.getName()) {
10280                 return;
10281             }
10282             
10283             var v = f.getValue();
10284             
10285             if (f.inputType =='radio') {
10286                 if (typeof(ret[f.getName()]) == 'undefined') {
10287                     ret[f.getName()] = ''; // empty..
10288                 }
10289
10290                 if (!f.el.dom.checked) {
10291                     return;
10292
10293                 }
10294                 v = f.el.dom.value;
10295
10296             }
10297             
10298             if(f.xtype == 'MoneyField'){
10299                 ret[f.currencyName] = f.getCurrency();
10300             }
10301
10302             // not sure if this supported any more..
10303             if ((typeof(v) == 'object') && f.getRawValue) {
10304                 v = f.getRawValue() ; // dates..
10305             }
10306             // combo boxes where name != hiddenName...
10307             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10308                 ret[f.name] = f.getRawValue();
10309             }
10310             ret[f.getName()] = v;
10311         });
10312
10313         return ret;
10314     },
10315
10316     /**
10317      * Clears all invalid messages in this form.
10318      * @return {BasicForm} this
10319      */
10320     clearInvalid : function(){
10321         var items = this.getItems();
10322
10323         items.each(function(f){
10324            f.clearInvalid();
10325         });
10326
10327         return this;
10328     },
10329
10330     /**
10331      * Resets this form.
10332      * @return {BasicForm} this
10333      */
10334     reset : function(){
10335         var items = this.getItems();
10336         items.each(function(f){
10337             f.reset();
10338         });
10339
10340         Roo.each(this.childForms || [], function (f) {
10341             f.reset();
10342         });
10343
10344
10345         return this;
10346     },
10347     
10348     getItems : function()
10349     {
10350         var r=new Roo.util.MixedCollection(false, function(o){
10351             return o.id || (o.id = Roo.id());
10352         });
10353         var iter = function(el) {
10354             if (el.inputEl) {
10355                 r.add(el);
10356             }
10357             if (!el.items) {
10358                 return;
10359             }
10360             Roo.each(el.items,function(e) {
10361                 iter(e);
10362             });
10363         };
10364
10365         iter(this);
10366         return r;
10367     },
10368     
10369     hideFields : function(items)
10370     {
10371         Roo.each(items, function(i){
10372             
10373             var f = this.findField(i);
10374             
10375             if(!f){
10376                 return;
10377             }
10378             
10379             f.hide();
10380             
10381         }, this);
10382     },
10383     
10384     showFields : function(items)
10385     {
10386         Roo.each(items, function(i){
10387             
10388             var f = this.findField(i);
10389             
10390             if(!f){
10391                 return;
10392             }
10393             
10394             f.show();
10395             
10396         }, this);
10397     }
10398
10399 });
10400
10401 Roo.apply(Roo.bootstrap.Form, {
10402     
10403     popover : {
10404         
10405         padding : 5,
10406         
10407         isApplied : false,
10408         
10409         isMasked : false,
10410         
10411         form : false,
10412         
10413         target : false,
10414         
10415         toolTip : false,
10416         
10417         intervalID : false,
10418         
10419         maskEl : false,
10420         
10421         apply : function()
10422         {
10423             if(this.isApplied){
10424                 return;
10425             }
10426             
10427             this.maskEl = {
10428                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10429                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10430                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10431                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10432             };
10433             
10434             this.maskEl.top.enableDisplayMode("block");
10435             this.maskEl.left.enableDisplayMode("block");
10436             this.maskEl.bottom.enableDisplayMode("block");
10437             this.maskEl.right.enableDisplayMode("block");
10438             
10439             this.toolTip = new Roo.bootstrap.Tooltip({
10440                 cls : 'roo-form-error-popover',
10441                 alignment : {
10442                     'left' : ['r-l', [-2,0], 'right'],
10443                     'right' : ['l-r', [2,0], 'left'],
10444                     'bottom' : ['tl-bl', [0,2], 'top'],
10445                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10446                 }
10447             });
10448             
10449             this.toolTip.render(Roo.get(document.body));
10450
10451             this.toolTip.el.enableDisplayMode("block");
10452             
10453             Roo.get(document.body).on('click', function(){
10454                 this.unmask();
10455             }, this);
10456             
10457             Roo.get(document.body).on('touchstart', function(){
10458                 this.unmask();
10459             }, this);
10460             
10461             this.isApplied = true
10462         },
10463         
10464         mask : function(form, target)
10465         {
10466             this.form = form;
10467             
10468             this.target = target;
10469             
10470             if(!this.form.errorMask || !target.el){
10471                 return;
10472             }
10473             
10474             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10475             
10476             Roo.log(scrollable);
10477             
10478             var ot = this.target.el.calcOffsetsTo(scrollable);
10479             
10480             var scrollTo = ot[1] - this.form.maskOffset;
10481             
10482             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10483             
10484             scrollable.scrollTo('top', scrollTo);
10485             
10486             var box = this.target.el.getBox();
10487             Roo.log(box);
10488             var zIndex = Roo.bootstrap.Modal.zIndex++;
10489
10490             
10491             this.maskEl.top.setStyle('position', 'absolute');
10492             this.maskEl.top.setStyle('z-index', zIndex);
10493             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10494             this.maskEl.top.setLeft(0);
10495             this.maskEl.top.setTop(0);
10496             this.maskEl.top.show();
10497             
10498             this.maskEl.left.setStyle('position', 'absolute');
10499             this.maskEl.left.setStyle('z-index', zIndex);
10500             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10501             this.maskEl.left.setLeft(0);
10502             this.maskEl.left.setTop(box.y - this.padding);
10503             this.maskEl.left.show();
10504
10505             this.maskEl.bottom.setStyle('position', 'absolute');
10506             this.maskEl.bottom.setStyle('z-index', zIndex);
10507             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10508             this.maskEl.bottom.setLeft(0);
10509             this.maskEl.bottom.setTop(box.bottom + this.padding);
10510             this.maskEl.bottom.show();
10511
10512             this.maskEl.right.setStyle('position', 'absolute');
10513             this.maskEl.right.setStyle('z-index', zIndex);
10514             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10515             this.maskEl.right.setLeft(box.right + this.padding);
10516             this.maskEl.right.setTop(box.y - this.padding);
10517             this.maskEl.right.show();
10518
10519             this.toolTip.bindEl = this.target.el;
10520
10521             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10522
10523             var tip = this.target.blankText;
10524
10525             if(this.target.getValue() !== '' ) {
10526                 
10527                 if (this.target.invalidText.length) {
10528                     tip = this.target.invalidText;
10529                 } else if (this.target.regexText.length){
10530                     tip = this.target.regexText;
10531                 }
10532             }
10533
10534             this.toolTip.show(tip);
10535
10536             this.intervalID = window.setInterval(function() {
10537                 Roo.bootstrap.Form.popover.unmask();
10538             }, 10000);
10539
10540             window.onwheel = function(){ return false;};
10541             
10542             (function(){ this.isMasked = true; }).defer(500, this);
10543             
10544         },
10545         
10546         unmask : function()
10547         {
10548             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10549                 return;
10550             }
10551             
10552             this.maskEl.top.setStyle('position', 'absolute');
10553             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10554             this.maskEl.top.hide();
10555
10556             this.maskEl.left.setStyle('position', 'absolute');
10557             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10558             this.maskEl.left.hide();
10559
10560             this.maskEl.bottom.setStyle('position', 'absolute');
10561             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10562             this.maskEl.bottom.hide();
10563
10564             this.maskEl.right.setStyle('position', 'absolute');
10565             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10566             this.maskEl.right.hide();
10567             
10568             this.toolTip.hide();
10569             
10570             this.toolTip.el.hide();
10571             
10572             window.onwheel = function(){ return true;};
10573             
10574             if(this.intervalID){
10575                 window.clearInterval(this.intervalID);
10576                 this.intervalID = false;
10577             }
10578             
10579             this.isMasked = false;
10580             
10581         }
10582         
10583     }
10584     
10585 });
10586
10587 /*
10588  * Based on:
10589  * Ext JS Library 1.1.1
10590  * Copyright(c) 2006-2007, Ext JS, LLC.
10591  *
10592  * Originally Released Under LGPL - original licence link has changed is not relivant.
10593  *
10594  * Fork - LGPL
10595  * <script type="text/javascript">
10596  */
10597 /**
10598  * @class Roo.form.VTypes
10599  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10600  * @singleton
10601  */
10602 Roo.form.VTypes = function(){
10603     // closure these in so they are only created once.
10604     var alpha = /^[a-zA-Z_]+$/;
10605     var alphanum = /^[a-zA-Z0-9_]+$/;
10606     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10607     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10608
10609     // All these messages and functions are configurable
10610     return {
10611         /**
10612          * The function used to validate email addresses
10613          * @param {String} value The email address
10614          */
10615         'email' : function(v){
10616             return email.test(v);
10617         },
10618         /**
10619          * The error text to display when the email validation function returns false
10620          * @type String
10621          */
10622         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10623         /**
10624          * The keystroke filter mask to be applied on email input
10625          * @type RegExp
10626          */
10627         'emailMask' : /[a-z0-9_\.\-@]/i,
10628
10629         /**
10630          * The function used to validate URLs
10631          * @param {String} value The URL
10632          */
10633         'url' : function(v){
10634             return url.test(v);
10635         },
10636         /**
10637          * The error text to display when the url validation function returns false
10638          * @type String
10639          */
10640         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10641         
10642         /**
10643          * The function used to validate alpha values
10644          * @param {String} value The value
10645          */
10646         'alpha' : function(v){
10647             return alpha.test(v);
10648         },
10649         /**
10650          * The error text to display when the alpha validation function returns false
10651          * @type String
10652          */
10653         'alphaText' : 'This field should only contain letters and _',
10654         /**
10655          * The keystroke filter mask to be applied on alpha input
10656          * @type RegExp
10657          */
10658         'alphaMask' : /[a-z_]/i,
10659
10660         /**
10661          * The function used to validate alphanumeric values
10662          * @param {String} value The value
10663          */
10664         'alphanum' : function(v){
10665             return alphanum.test(v);
10666         },
10667         /**
10668          * The error text to display when the alphanumeric validation function returns false
10669          * @type String
10670          */
10671         'alphanumText' : 'This field should only contain letters, numbers and _',
10672         /**
10673          * The keystroke filter mask to be applied on alphanumeric input
10674          * @type RegExp
10675          */
10676         'alphanumMask' : /[a-z0-9_]/i
10677     };
10678 }();/*
10679  * - LGPL
10680  *
10681  * Input
10682  * 
10683  */
10684
10685 /**
10686  * @class Roo.bootstrap.Input
10687  * @extends Roo.bootstrap.Component
10688  * Bootstrap Input class
10689  * @cfg {Boolean} disabled is it disabled
10690  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10691  * @cfg {String} name name of the input
10692  * @cfg {string} fieldLabel - the label associated
10693  * @cfg {string} placeholder - placeholder to put in text.
10694  * @cfg {string}  before - input group add on before
10695  * @cfg {string} after - input group add on after
10696  * @cfg {string} size - (lg|sm) or leave empty..
10697  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10698  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10699  * @cfg {Number} md colspan out of 12 for computer-sized screens
10700  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10701  * @cfg {string} value default value of the input
10702  * @cfg {Number} labelWidth set the width of label 
10703  * @cfg {Number} labellg set the width of label (1-12)
10704  * @cfg {Number} labelmd set the width of label (1-12)
10705  * @cfg {Number} labelsm set the width of label (1-12)
10706  * @cfg {Number} labelxs set the width of label (1-12)
10707  * @cfg {String} labelAlign (top|left)
10708  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10709  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10710  * @cfg {String} indicatorpos (left|right) default left
10711  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10712  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10713  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10714
10715  * @cfg {String} align (left|center|right) Default left
10716  * @cfg {Boolean} forceFeedback (true|false) Default false
10717  * 
10718  * @constructor
10719  * Create a new Input
10720  * @param {Object} config The config object
10721  */
10722
10723 Roo.bootstrap.Input = function(config){
10724     
10725     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10726     
10727     this.addEvents({
10728         /**
10729          * @event focus
10730          * Fires when this field receives input focus.
10731          * @param {Roo.form.Field} this
10732          */
10733         focus : true,
10734         /**
10735          * @event blur
10736          * Fires when this field loses input focus.
10737          * @param {Roo.form.Field} this
10738          */
10739         blur : true,
10740         /**
10741          * @event specialkey
10742          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10743          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10744          * @param {Roo.form.Field} this
10745          * @param {Roo.EventObject} e The event object
10746          */
10747         specialkey : true,
10748         /**
10749          * @event change
10750          * Fires just before the field blurs if the field value has changed.
10751          * @param {Roo.form.Field} this
10752          * @param {Mixed} newValue The new value
10753          * @param {Mixed} oldValue The original value
10754          */
10755         change : true,
10756         /**
10757          * @event invalid
10758          * Fires after the field has been marked as invalid.
10759          * @param {Roo.form.Field} this
10760          * @param {String} msg The validation message
10761          */
10762         invalid : true,
10763         /**
10764          * @event valid
10765          * Fires after the field has been validated with no errors.
10766          * @param {Roo.form.Field} this
10767          */
10768         valid : true,
10769          /**
10770          * @event keyup
10771          * Fires after the key up
10772          * @param {Roo.form.Field} this
10773          * @param {Roo.EventObject}  e The event Object
10774          */
10775         keyup : true,
10776         /**
10777          * @event paste
10778          * Fires after the user pastes into input
10779          * @param {Roo.form.Field} this
10780          * @param {Roo.EventObject}  e The event Object
10781          */
10782         paste : true
10783     });
10784 };
10785
10786 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10787      /**
10788      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10789       automatic validation (defaults to "keyup").
10790      */
10791     validationEvent : "keyup",
10792      /**
10793      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10794      */
10795     validateOnBlur : true,
10796     /**
10797      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10798      */
10799     validationDelay : 250,
10800      /**
10801      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10802      */
10803     focusClass : "x-form-focus",  // not needed???
10804     
10805        
10806     /**
10807      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10808      */
10809     invalidClass : "has-warning",
10810     
10811     /**
10812      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10813      */
10814     validClass : "has-success",
10815     
10816     /**
10817      * @cfg {Boolean} hasFeedback (true|false) default true
10818      */
10819     hasFeedback : true,
10820     
10821     /**
10822      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10823      */
10824     invalidFeedbackClass : "glyphicon-warning-sign",
10825     
10826     /**
10827      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10828      */
10829     validFeedbackClass : "glyphicon-ok",
10830     
10831     /**
10832      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10833      */
10834     selectOnFocus : false,
10835     
10836      /**
10837      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10838      */
10839     maskRe : null,
10840        /**
10841      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10842      */
10843     vtype : null,
10844     
10845       /**
10846      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10847      */
10848     disableKeyFilter : false,
10849     
10850        /**
10851      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10852      */
10853     disabled : false,
10854      /**
10855      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10856      */
10857     allowBlank : true,
10858     /**
10859      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10860      */
10861     blankText : "Please complete this mandatory field",
10862     
10863      /**
10864      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10865      */
10866     minLength : 0,
10867     /**
10868      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10869      */
10870     maxLength : Number.MAX_VALUE,
10871     /**
10872      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10873      */
10874     minLengthText : "The minimum length for this field is {0}",
10875     /**
10876      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10877      */
10878     maxLengthText : "The maximum length for this field is {0}",
10879   
10880     
10881     /**
10882      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10883      * If available, this function will be called only after the basic validators all return true, and will be passed the
10884      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10885      */
10886     validator : null,
10887     /**
10888      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10889      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10890      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10891      */
10892     regex : null,
10893     /**
10894      * @cfg {String} regexText -- Depricated - use Invalid Text
10895      */
10896     regexText : "",
10897     
10898     /**
10899      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10900      */
10901     invalidText : "",
10902     
10903     
10904     
10905     autocomplete: false,
10906     
10907     
10908     fieldLabel : '',
10909     inputType : 'text',
10910     
10911     name : false,
10912     placeholder: false,
10913     before : false,
10914     after : false,
10915     size : false,
10916     hasFocus : false,
10917     preventMark: false,
10918     isFormField : true,
10919     value : '',
10920     labelWidth : 2,
10921     labelAlign : false,
10922     readOnly : false,
10923     align : false,
10924     formatedValue : false,
10925     forceFeedback : false,
10926     
10927     indicatorpos : 'left',
10928     
10929     labellg : 0,
10930     labelmd : 0,
10931     labelsm : 0,
10932     labelxs : 0,
10933     
10934     capture : '',
10935     accept : '',
10936     
10937     parentLabelAlign : function()
10938     {
10939         var parent = this;
10940         while (parent.parent()) {
10941             parent = parent.parent();
10942             if (typeof(parent.labelAlign) !='undefined') {
10943                 return parent.labelAlign;
10944             }
10945         }
10946         return 'left';
10947         
10948     },
10949     
10950     getAutoCreate : function()
10951     {
10952         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10953         
10954         var id = Roo.id();
10955         
10956         var cfg = {};
10957         
10958         if(this.inputType != 'hidden'){
10959             cfg.cls = 'form-group' //input-group
10960         }
10961         
10962         var input =  {
10963             tag: 'input',
10964             id : id,
10965             type : this.inputType,
10966             value : this.value,
10967             cls : 'form-control',
10968             placeholder : this.placeholder || '',
10969             autocomplete : this.autocomplete || 'new-password'
10970         };
10971         if (this.inputType == 'file') {
10972             input.style = 'overflow:hidden'; // why not in CSS?
10973         }
10974         
10975         if(this.capture.length){
10976             input.capture = this.capture;
10977         }
10978         
10979         if(this.accept.length){
10980             input.accept = this.accept + "/*";
10981         }
10982         
10983         if(this.align){
10984             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10985         }
10986         
10987         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10988             input.maxLength = this.maxLength;
10989         }
10990         
10991         if (this.disabled) {
10992             input.disabled=true;
10993         }
10994         
10995         if (this.readOnly) {
10996             input.readonly=true;
10997         }
10998         
10999         if (this.name) {
11000             input.name = this.name;
11001         }
11002         
11003         if (this.size) {
11004             input.cls += ' input-' + this.size;
11005         }
11006         
11007         var settings=this;
11008         ['xs','sm','md','lg'].map(function(size){
11009             if (settings[size]) {
11010                 cfg.cls += ' col-' + size + '-' + settings[size];
11011             }
11012         });
11013         
11014         var inputblock = input;
11015         
11016         var feedback = {
11017             tag: 'span',
11018             cls: 'glyphicon form-control-feedback'
11019         };
11020             
11021         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11022             
11023             inputblock = {
11024                 cls : 'has-feedback',
11025                 cn :  [
11026                     input,
11027                     feedback
11028                 ] 
11029             };  
11030         }
11031         
11032         if (this.before || this.after) {
11033             
11034             inputblock = {
11035                 cls : 'input-group',
11036                 cn :  [] 
11037             };
11038             
11039             if (this.before && typeof(this.before) == 'string') {
11040                 
11041                 inputblock.cn.push({
11042                     tag :'span',
11043                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11044                     html : this.before
11045                 });
11046             }
11047             if (this.before && typeof(this.before) == 'object') {
11048                 this.before = Roo.factory(this.before);
11049                 
11050                 inputblock.cn.push({
11051                     tag :'span',
11052                     cls : 'roo-input-before input-group-prepend   input-group-' +
11053                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11054                 });
11055             }
11056             
11057             inputblock.cn.push(input);
11058             
11059             if (this.after && typeof(this.after) == 'string') {
11060                 inputblock.cn.push({
11061                     tag :'span',
11062                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11063                     html : this.after
11064                 });
11065             }
11066             if (this.after && typeof(this.after) == 'object') {
11067                 this.after = Roo.factory(this.after);
11068                 
11069                 inputblock.cn.push({
11070                     tag :'span',
11071                     cls : 'roo-input-after input-group-append  input-group-' +
11072                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11073                 });
11074             }
11075             
11076             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11077                 inputblock.cls += ' has-feedback';
11078                 inputblock.cn.push(feedback);
11079             }
11080         };
11081         var indicator = {
11082             tag : 'i',
11083             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11084             tooltip : 'This field is required'
11085         };
11086         if (this.allowBlank ) {
11087             indicator.style = this.allowBlank ? ' display:none' : '';
11088         }
11089         if (align ==='left' && this.fieldLabel.length) {
11090             
11091             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11092             
11093             cfg.cn = [
11094                 indicator,
11095                 {
11096                     tag: 'label',
11097                     'for' :  id,
11098                     cls : 'control-label col-form-label',
11099                     html : this.fieldLabel
11100
11101                 },
11102                 {
11103                     cls : "", 
11104                     cn: [
11105                         inputblock
11106                     ]
11107                 }
11108             ];
11109             
11110             var labelCfg = cfg.cn[1];
11111             var contentCfg = cfg.cn[2];
11112             
11113             if(this.indicatorpos == 'right'){
11114                 cfg.cn = [
11115                     {
11116                         tag: 'label',
11117                         'for' :  id,
11118                         cls : 'control-label col-form-label',
11119                         cn : [
11120                             {
11121                                 tag : 'span',
11122                                 html : this.fieldLabel
11123                             },
11124                             indicator
11125                         ]
11126                     },
11127                     {
11128                         cls : "",
11129                         cn: [
11130                             inputblock
11131                         ]
11132                     }
11133
11134                 ];
11135                 
11136                 labelCfg = cfg.cn[0];
11137                 contentCfg = cfg.cn[1];
11138             
11139             }
11140             
11141             if(this.labelWidth > 12){
11142                 labelCfg.style = "width: " + this.labelWidth + 'px';
11143             }
11144             
11145             if(this.labelWidth < 13 && this.labelmd == 0){
11146                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11147             }
11148             
11149             if(this.labellg > 0){
11150                 labelCfg.cls += ' col-lg-' + this.labellg;
11151                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11152             }
11153             
11154             if(this.labelmd > 0){
11155                 labelCfg.cls += ' col-md-' + this.labelmd;
11156                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11157             }
11158             
11159             if(this.labelsm > 0){
11160                 labelCfg.cls += ' col-sm-' + this.labelsm;
11161                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11162             }
11163             
11164             if(this.labelxs > 0){
11165                 labelCfg.cls += ' col-xs-' + this.labelxs;
11166                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11167             }
11168             
11169             
11170         } else if ( this.fieldLabel.length) {
11171                 
11172             
11173             
11174             cfg.cn = [
11175                 {
11176                     tag : 'i',
11177                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11178                     tooltip : 'This field is required',
11179                     style : this.allowBlank ? ' display:none' : '' 
11180                 },
11181                 {
11182                     tag: 'label',
11183                    //cls : 'input-group-addon',
11184                     html : this.fieldLabel
11185
11186                 },
11187
11188                inputblock
11189
11190            ];
11191            
11192            if(this.indicatorpos == 'right'){
11193        
11194                 cfg.cn = [
11195                     {
11196                         tag: 'label',
11197                        //cls : 'input-group-addon',
11198                         html : this.fieldLabel
11199
11200                     },
11201                     {
11202                         tag : 'i',
11203                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11204                         tooltip : 'This field is required',
11205                         style : this.allowBlank ? ' display:none' : '' 
11206                     },
11207
11208                    inputblock
11209
11210                ];
11211
11212             }
11213
11214         } else {
11215             
11216             cfg.cn = [
11217
11218                     inputblock
11219
11220             ];
11221                 
11222                 
11223         };
11224         
11225         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11226            cfg.cls += ' navbar-form';
11227         }
11228         
11229         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11230             // on BS4 we do this only if not form 
11231             cfg.cls += ' navbar-form';
11232             cfg.tag = 'li';
11233         }
11234         
11235         return cfg;
11236         
11237     },
11238     /**
11239      * return the real input element.
11240      */
11241     inputEl: function ()
11242     {
11243         return this.el.select('input.form-control',true).first();
11244     },
11245     
11246     tooltipEl : function()
11247     {
11248         return this.inputEl();
11249     },
11250     
11251     indicatorEl : function()
11252     {
11253         if (Roo.bootstrap.version == 4) {
11254             return false; // not enabled in v4 yet.
11255         }
11256         
11257         var indicator = this.el.select('i.roo-required-indicator',true).first();
11258         
11259         if(!indicator){
11260             return false;
11261         }
11262         
11263         return indicator;
11264         
11265     },
11266     
11267     setDisabled : function(v)
11268     {
11269         var i  = this.inputEl().dom;
11270         if (!v) {
11271             i.removeAttribute('disabled');
11272             return;
11273             
11274         }
11275         i.setAttribute('disabled','true');
11276     },
11277     initEvents : function()
11278     {
11279           
11280         this.inputEl().on("keydown" , this.fireKey,  this);
11281         this.inputEl().on("focus", this.onFocus,  this);
11282         this.inputEl().on("blur", this.onBlur,  this);
11283         
11284         this.inputEl().relayEvent('keyup', this);
11285         this.inputEl().relayEvent('paste', this);
11286         
11287         this.indicator = this.indicatorEl();
11288         
11289         if(this.indicator){
11290             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11291         }
11292  
11293         // reference to original value for reset
11294         this.originalValue = this.getValue();
11295         //Roo.form.TextField.superclass.initEvents.call(this);
11296         if(this.validationEvent == 'keyup'){
11297             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11298             this.inputEl().on('keyup', this.filterValidation, this);
11299         }
11300         else if(this.validationEvent !== false){
11301             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11302         }
11303         
11304         if(this.selectOnFocus){
11305             this.on("focus", this.preFocus, this);
11306             
11307         }
11308         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11309             this.inputEl().on("keypress", this.filterKeys, this);
11310         } else {
11311             this.inputEl().relayEvent('keypress', this);
11312         }
11313        /* if(this.grow){
11314             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11315             this.el.on("click", this.autoSize,  this);
11316         }
11317         */
11318         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11319             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11320         }
11321         
11322         if (typeof(this.before) == 'object') {
11323             this.before.render(this.el.select('.roo-input-before',true).first());
11324         }
11325         if (typeof(this.after) == 'object') {
11326             this.after.render(this.el.select('.roo-input-after',true).first());
11327         }
11328         
11329         this.inputEl().on('change', this.onChange, this);
11330         
11331     },
11332     filterValidation : function(e){
11333         if(!e.isNavKeyPress()){
11334             this.validationTask.delay(this.validationDelay);
11335         }
11336     },
11337      /**
11338      * Validates the field value
11339      * @return {Boolean} True if the value is valid, else false
11340      */
11341     validate : function(){
11342         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11343         if(this.disabled || this.validateValue(this.getRawValue())){
11344             this.markValid();
11345             return true;
11346         }
11347         
11348         this.markInvalid();
11349         return false;
11350     },
11351     
11352     
11353     /**
11354      * Validates a value according to the field's validation rules and marks the field as invalid
11355      * if the validation fails
11356      * @param {Mixed} value The value to validate
11357      * @return {Boolean} True if the value is valid, else false
11358      */
11359     validateValue : function(value)
11360     {
11361         if(this.getVisibilityEl().hasClass('hidden')){
11362             return true;
11363         }
11364         
11365         if(value.length < 1)  { // if it's blank
11366             if(this.allowBlank){
11367                 return true;
11368             }
11369             return false;
11370         }
11371         
11372         if(value.length < this.minLength){
11373             return false;
11374         }
11375         if(value.length > this.maxLength){
11376             return false;
11377         }
11378         if(this.vtype){
11379             var vt = Roo.form.VTypes;
11380             if(!vt[this.vtype](value, this)){
11381                 return false;
11382             }
11383         }
11384         if(typeof this.validator == "function"){
11385             var msg = this.validator(value);
11386             if(msg !== true){
11387                 return false;
11388             }
11389             if (typeof(msg) == 'string') {
11390                 this.invalidText = msg;
11391             }
11392         }
11393         
11394         if(this.regex && !this.regex.test(value)){
11395             return false;
11396         }
11397         
11398         return true;
11399     },
11400     
11401      // private
11402     fireKey : function(e){
11403         //Roo.log('field ' + e.getKey());
11404         if(e.isNavKeyPress()){
11405             this.fireEvent("specialkey", this, e);
11406         }
11407     },
11408     focus : function (selectText){
11409         if(this.rendered){
11410             this.inputEl().focus();
11411             if(selectText === true){
11412                 this.inputEl().dom.select();
11413             }
11414         }
11415         return this;
11416     } ,
11417     
11418     onFocus : function(){
11419         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11420            // this.el.addClass(this.focusClass);
11421         }
11422         if(!this.hasFocus){
11423             this.hasFocus = true;
11424             this.startValue = this.getValue();
11425             this.fireEvent("focus", this);
11426         }
11427     },
11428     
11429     beforeBlur : Roo.emptyFn,
11430
11431     
11432     // private
11433     onBlur : function(){
11434         this.beforeBlur();
11435         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11436             //this.el.removeClass(this.focusClass);
11437         }
11438         this.hasFocus = false;
11439         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11440             this.validate();
11441         }
11442         var v = this.getValue();
11443         if(String(v) !== String(this.startValue)){
11444             this.fireEvent('change', this, v, this.startValue);
11445         }
11446         this.fireEvent("blur", this);
11447     },
11448     
11449     onChange : function(e)
11450     {
11451         var v = this.getValue();
11452         if(String(v) !== String(this.startValue)){
11453             this.fireEvent('change', this, v, this.startValue);
11454         }
11455         
11456     },
11457     
11458     /**
11459      * Resets the current field value to the originally loaded value and clears any validation messages
11460      */
11461     reset : function(){
11462         this.setValue(this.originalValue);
11463         this.validate();
11464     },
11465      /**
11466      * Returns the name of the field
11467      * @return {Mixed} name The name field
11468      */
11469     getName: function(){
11470         return this.name;
11471     },
11472      /**
11473      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11474      * @return {Mixed} value The field value
11475      */
11476     getValue : function(){
11477         
11478         var v = this.inputEl().getValue();
11479         
11480         return v;
11481     },
11482     /**
11483      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11484      * @return {Mixed} value The field value
11485      */
11486     getRawValue : function(){
11487         var v = this.inputEl().getValue();
11488         
11489         return v;
11490     },
11491     
11492     /**
11493      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11494      * @param {Mixed} value The value to set
11495      */
11496     setRawValue : function(v){
11497         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11498     },
11499     
11500     selectText : function(start, end){
11501         var v = this.getRawValue();
11502         if(v.length > 0){
11503             start = start === undefined ? 0 : start;
11504             end = end === undefined ? v.length : end;
11505             var d = this.inputEl().dom;
11506             if(d.setSelectionRange){
11507                 d.setSelectionRange(start, end);
11508             }else if(d.createTextRange){
11509                 var range = d.createTextRange();
11510                 range.moveStart("character", start);
11511                 range.moveEnd("character", v.length-end);
11512                 range.select();
11513             }
11514         }
11515     },
11516     
11517     /**
11518      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11519      * @param {Mixed} value The value to set
11520      */
11521     setValue : function(v){
11522         this.value = v;
11523         if(this.rendered){
11524             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11525             this.validate();
11526         }
11527     },
11528     
11529     /*
11530     processValue : function(value){
11531         if(this.stripCharsRe){
11532             var newValue = value.replace(this.stripCharsRe, '');
11533             if(newValue !== value){
11534                 this.setRawValue(newValue);
11535                 return newValue;
11536             }
11537         }
11538         return value;
11539     },
11540   */
11541     preFocus : function(){
11542         
11543         if(this.selectOnFocus){
11544             this.inputEl().dom.select();
11545         }
11546     },
11547     filterKeys : function(e){
11548         var k = e.getKey();
11549         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11550             return;
11551         }
11552         var c = e.getCharCode(), cc = String.fromCharCode(c);
11553         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11554             return;
11555         }
11556         if(!this.maskRe.test(cc)){
11557             e.stopEvent();
11558         }
11559     },
11560      /**
11561      * Clear any invalid styles/messages for this field
11562      */
11563     clearInvalid : function(){
11564         
11565         if(!this.el || this.preventMark){ // not rendered
11566             return;
11567         }
11568         
11569         
11570         this.el.removeClass([this.invalidClass, 'is-invalid']);
11571         
11572         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11573             
11574             var feedback = this.el.select('.form-control-feedback', true).first();
11575             
11576             if(feedback){
11577                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11578             }
11579             
11580         }
11581         
11582         if(this.indicator){
11583             this.indicator.removeClass('visible');
11584             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11585         }
11586         
11587         this.fireEvent('valid', this);
11588     },
11589     
11590      /**
11591      * Mark this field as valid
11592      */
11593     markValid : function()
11594     {
11595         if(!this.el  || this.preventMark){ // not rendered...
11596             return;
11597         }
11598         
11599         this.el.removeClass([this.invalidClass, this.validClass]);
11600         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11601
11602         var feedback = this.el.select('.form-control-feedback', true).first();
11603             
11604         if(feedback){
11605             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11606         }
11607         
11608         if(this.indicator){
11609             this.indicator.removeClass('visible');
11610             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11611         }
11612         
11613         if(this.disabled){
11614             return;
11615         }
11616         
11617            
11618         if(this.allowBlank && !this.getRawValue().length){
11619             return;
11620         }
11621         if (Roo.bootstrap.version == 3) {
11622             this.el.addClass(this.validClass);
11623         } else {
11624             this.inputEl().addClass('is-valid');
11625         }
11626
11627         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11628             
11629             var feedback = this.el.select('.form-control-feedback', true).first();
11630             
11631             if(feedback){
11632                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11633                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11634             }
11635             
11636         }
11637         
11638         this.fireEvent('valid', this);
11639     },
11640     
11641      /**
11642      * Mark this field as invalid
11643      * @param {String} msg The validation message
11644      */
11645     markInvalid : function(msg)
11646     {
11647         if(!this.el  || this.preventMark){ // not rendered
11648             return;
11649         }
11650         
11651         this.el.removeClass([this.invalidClass, this.validClass]);
11652         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11653         
11654         var feedback = this.el.select('.form-control-feedback', true).first();
11655             
11656         if(feedback){
11657             this.el.select('.form-control-feedback', true).first().removeClass(
11658                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11659         }
11660
11661         if(this.disabled){
11662             return;
11663         }
11664         
11665         if(this.allowBlank && !this.getRawValue().length){
11666             return;
11667         }
11668         
11669         if(this.indicator){
11670             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11671             this.indicator.addClass('visible');
11672         }
11673         if (Roo.bootstrap.version == 3) {
11674             this.el.addClass(this.invalidClass);
11675         } else {
11676             this.inputEl().addClass('is-invalid');
11677         }
11678         
11679         
11680         
11681         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11682             
11683             var feedback = this.el.select('.form-control-feedback', true).first();
11684             
11685             if(feedback){
11686                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11687                 
11688                 if(this.getValue().length || this.forceFeedback){
11689                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11690                 }
11691                 
11692             }
11693             
11694         }
11695         
11696         this.fireEvent('invalid', this, msg);
11697     },
11698     // private
11699     SafariOnKeyDown : function(event)
11700     {
11701         // this is a workaround for a password hang bug on chrome/ webkit.
11702         if (this.inputEl().dom.type != 'password') {
11703             return;
11704         }
11705         
11706         var isSelectAll = false;
11707         
11708         if(this.inputEl().dom.selectionEnd > 0){
11709             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11710         }
11711         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11712             event.preventDefault();
11713             this.setValue('');
11714             return;
11715         }
11716         
11717         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11718             
11719             event.preventDefault();
11720             // this is very hacky as keydown always get's upper case.
11721             //
11722             var cc = String.fromCharCode(event.getCharCode());
11723             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11724             
11725         }
11726     },
11727     adjustWidth : function(tag, w){
11728         tag = tag.toLowerCase();
11729         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11730             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11731                 if(tag == 'input'){
11732                     return w + 2;
11733                 }
11734                 if(tag == 'textarea'){
11735                     return w-2;
11736                 }
11737             }else if(Roo.isOpera){
11738                 if(tag == 'input'){
11739                     return w + 2;
11740                 }
11741                 if(tag == 'textarea'){
11742                     return w-2;
11743                 }
11744             }
11745         }
11746         return w;
11747     },
11748     
11749     setFieldLabel : function(v)
11750     {
11751         if(!this.rendered){
11752             return;
11753         }
11754         
11755         if(this.indicatorEl()){
11756             var ar = this.el.select('label > span',true);
11757             
11758             if (ar.elements.length) {
11759                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11760                 this.fieldLabel = v;
11761                 return;
11762             }
11763             
11764             var br = this.el.select('label',true);
11765             
11766             if(br.elements.length) {
11767                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11768                 this.fieldLabel = v;
11769                 return;
11770             }
11771             
11772             Roo.log('Cannot Found any of label > span || label in input');
11773             return;
11774         }
11775         
11776         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11777         this.fieldLabel = v;
11778         
11779         
11780     }
11781 });
11782
11783  
11784 /*
11785  * - LGPL
11786  *
11787  * Input
11788  * 
11789  */
11790
11791 /**
11792  * @class Roo.bootstrap.TextArea
11793  * @extends Roo.bootstrap.Input
11794  * Bootstrap TextArea class
11795  * @cfg {Number} cols Specifies the visible width of a text area
11796  * @cfg {Number} rows Specifies the visible number of lines in a text area
11797  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11798  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11799  * @cfg {string} html text
11800  * 
11801  * @constructor
11802  * Create a new TextArea
11803  * @param {Object} config The config object
11804  */
11805
11806 Roo.bootstrap.TextArea = function(config){
11807     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11808    
11809 };
11810
11811 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11812      
11813     cols : false,
11814     rows : 5,
11815     readOnly : false,
11816     warp : 'soft',
11817     resize : false,
11818     value: false,
11819     html: false,
11820     
11821     getAutoCreate : function(){
11822         
11823         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11824         
11825         var id = Roo.id();
11826         
11827         var cfg = {};
11828         
11829         if(this.inputType != 'hidden'){
11830             cfg.cls = 'form-group' //input-group
11831         }
11832         
11833         var input =  {
11834             tag: 'textarea',
11835             id : id,
11836             warp : this.warp,
11837             rows : this.rows,
11838             value : this.value || '',
11839             html: this.html || '',
11840             cls : 'form-control',
11841             placeholder : this.placeholder || '' 
11842             
11843         };
11844         
11845         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11846             input.maxLength = this.maxLength;
11847         }
11848         
11849         if(this.resize){
11850             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11851         }
11852         
11853         if(this.cols){
11854             input.cols = this.cols;
11855         }
11856         
11857         if (this.readOnly) {
11858             input.readonly = true;
11859         }
11860         
11861         if (this.name) {
11862             input.name = this.name;
11863         }
11864         
11865         if (this.size) {
11866             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11867         }
11868         
11869         var settings=this;
11870         ['xs','sm','md','lg'].map(function(size){
11871             if (settings[size]) {
11872                 cfg.cls += ' col-' + size + '-' + settings[size];
11873             }
11874         });
11875         
11876         var inputblock = input;
11877         
11878         if(this.hasFeedback && !this.allowBlank){
11879             
11880             var feedback = {
11881                 tag: 'span',
11882                 cls: 'glyphicon form-control-feedback'
11883             };
11884
11885             inputblock = {
11886                 cls : 'has-feedback',
11887                 cn :  [
11888                     input,
11889                     feedback
11890                 ] 
11891             };  
11892         }
11893         
11894         
11895         if (this.before || this.after) {
11896             
11897             inputblock = {
11898                 cls : 'input-group',
11899                 cn :  [] 
11900             };
11901             if (this.before) {
11902                 inputblock.cn.push({
11903                     tag :'span',
11904                     cls : 'input-group-addon',
11905                     html : this.before
11906                 });
11907             }
11908             
11909             inputblock.cn.push(input);
11910             
11911             if(this.hasFeedback && !this.allowBlank){
11912                 inputblock.cls += ' has-feedback';
11913                 inputblock.cn.push(feedback);
11914             }
11915             
11916             if (this.after) {
11917                 inputblock.cn.push({
11918                     tag :'span',
11919                     cls : 'input-group-addon',
11920                     html : this.after
11921                 });
11922             }
11923             
11924         }
11925         
11926         if (align ==='left' && this.fieldLabel.length) {
11927             cfg.cn = [
11928                 {
11929                     tag: 'label',
11930                     'for' :  id,
11931                     cls : 'control-label',
11932                     html : this.fieldLabel
11933                 },
11934                 {
11935                     cls : "",
11936                     cn: [
11937                         inputblock
11938                     ]
11939                 }
11940
11941             ];
11942             
11943             if(this.labelWidth > 12){
11944                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11945             }
11946
11947             if(this.labelWidth < 13 && this.labelmd == 0){
11948                 this.labelmd = this.labelWidth;
11949             }
11950
11951             if(this.labellg > 0){
11952                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11953                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11954             }
11955
11956             if(this.labelmd > 0){
11957                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11958                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11959             }
11960
11961             if(this.labelsm > 0){
11962                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11963                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11964             }
11965
11966             if(this.labelxs > 0){
11967                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11968                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11969             }
11970             
11971         } else if ( this.fieldLabel.length) {
11972             cfg.cn = [
11973
11974                {
11975                    tag: 'label',
11976                    //cls : 'input-group-addon',
11977                    html : this.fieldLabel
11978
11979                },
11980
11981                inputblock
11982
11983            ];
11984
11985         } else {
11986
11987             cfg.cn = [
11988
11989                 inputblock
11990
11991             ];
11992                 
11993         }
11994         
11995         if (this.disabled) {
11996             input.disabled=true;
11997         }
11998         
11999         return cfg;
12000         
12001     },
12002     /**
12003      * return the real textarea element.
12004      */
12005     inputEl: function ()
12006     {
12007         return this.el.select('textarea.form-control',true).first();
12008     },
12009     
12010     /**
12011      * Clear any invalid styles/messages for this field
12012      */
12013     clearInvalid : function()
12014     {
12015         
12016         if(!this.el || this.preventMark){ // not rendered
12017             return;
12018         }
12019         
12020         var label = this.el.select('label', true).first();
12021         var icon = this.el.select('i.fa-star', true).first();
12022         
12023         if(label && icon){
12024             icon.remove();
12025         }
12026         this.el.removeClass( this.validClass);
12027         this.inputEl().removeClass('is-invalid');
12028          
12029         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12030             
12031             var feedback = this.el.select('.form-control-feedback', true).first();
12032             
12033             if(feedback){
12034                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12035             }
12036             
12037         }
12038         
12039         this.fireEvent('valid', this);
12040     },
12041     
12042      /**
12043      * Mark this field as valid
12044      */
12045     markValid : function()
12046     {
12047         if(!this.el  || this.preventMark){ // not rendered
12048             return;
12049         }
12050         
12051         this.el.removeClass([this.invalidClass, this.validClass]);
12052         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12053         
12054         var feedback = this.el.select('.form-control-feedback', true).first();
12055             
12056         if(feedback){
12057             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12058         }
12059
12060         if(this.disabled || this.allowBlank){
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         if (Roo.bootstrap.version == 3) {
12071             this.el.addClass(this.validClass);
12072         } else {
12073             this.inputEl().addClass('is-valid');
12074         }
12075         
12076         
12077         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12078             
12079             var feedback = this.el.select('.form-control-feedback', true).first();
12080             
12081             if(feedback){
12082                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12083                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12084             }
12085             
12086         }
12087         
12088         this.fireEvent('valid', this);
12089     },
12090     
12091      /**
12092      * Mark this field as invalid
12093      * @param {String} msg The validation message
12094      */
12095     markInvalid : function(msg)
12096     {
12097         if(!this.el  || this.preventMark){ // not rendered
12098             return;
12099         }
12100         
12101         this.el.removeClass([this.invalidClass, this.validClass]);
12102         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12103         
12104         var feedback = this.el.select('.form-control-feedback', true).first();
12105             
12106         if(feedback){
12107             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12108         }
12109
12110         if(this.disabled || this.allowBlank){
12111             return;
12112         }
12113         
12114         var label = this.el.select('label', true).first();
12115         var icon = this.el.select('i.fa-star', true).first();
12116         
12117         if(!this.getValue().length && label && !icon){
12118             this.el.createChild({
12119                 tag : 'i',
12120                 cls : 'text-danger fa fa-lg fa-star',
12121                 tooltip : 'This field is required',
12122                 style : 'margin-right:5px;'
12123             }, label, true);
12124         }
12125         
12126         if (Roo.bootstrap.version == 3) {
12127             this.el.addClass(this.invalidClass);
12128         } else {
12129             this.inputEl().addClass('is-invalid');
12130         }
12131         
12132         // fixme ... this may be depricated need to test..
12133         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12134             
12135             var feedback = this.el.select('.form-control-feedback', true).first();
12136             
12137             if(feedback){
12138                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12139                 
12140                 if(this.getValue().length || this.forceFeedback){
12141                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12142                 }
12143                 
12144             }
12145             
12146         }
12147         
12148         this.fireEvent('invalid', this, msg);
12149     }
12150 });
12151
12152  
12153 /*
12154  * - LGPL
12155  *
12156  * trigger field - base class for combo..
12157  * 
12158  */
12159  
12160 /**
12161  * @class Roo.bootstrap.TriggerField
12162  * @extends Roo.bootstrap.Input
12163  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12164  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12165  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12166  * for which you can provide a custom implementation.  For example:
12167  * <pre><code>
12168 var trigger = new Roo.bootstrap.TriggerField();
12169 trigger.onTriggerClick = myTriggerFn;
12170 trigger.applyTo('my-field');
12171 </code></pre>
12172  *
12173  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12174  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12175  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12176  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12177  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12178
12179  * @constructor
12180  * Create a new TriggerField.
12181  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12182  * to the base TextField)
12183  */
12184 Roo.bootstrap.TriggerField = function(config){
12185     this.mimicing = false;
12186     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12187 };
12188
12189 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12190     /**
12191      * @cfg {String} triggerClass A CSS class to apply to the trigger
12192      */
12193      /**
12194      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12195      */
12196     hideTrigger:false,
12197
12198     /**
12199      * @cfg {Boolean} removable (true|false) special filter default false
12200      */
12201     removable : false,
12202     
12203     /** @cfg {Boolean} grow @hide */
12204     /** @cfg {Number} growMin @hide */
12205     /** @cfg {Number} growMax @hide */
12206
12207     /**
12208      * @hide 
12209      * @method
12210      */
12211     autoSize: Roo.emptyFn,
12212     // private
12213     monitorTab : true,
12214     // private
12215     deferHeight : true,
12216
12217     
12218     actionMode : 'wrap',
12219     
12220     caret : false,
12221     
12222     
12223     getAutoCreate : function(){
12224        
12225         var align = this.labelAlign || this.parentLabelAlign();
12226         
12227         var id = Roo.id();
12228         
12229         var cfg = {
12230             cls: 'form-group' //input-group
12231         };
12232         
12233         
12234         var input =  {
12235             tag: 'input',
12236             id : id,
12237             type : this.inputType,
12238             cls : 'form-control',
12239             autocomplete: 'new-password',
12240             placeholder : this.placeholder || '' 
12241             
12242         };
12243         if (this.name) {
12244             input.name = this.name;
12245         }
12246         if (this.size) {
12247             input.cls += ' input-' + this.size;
12248         }
12249         
12250         if (this.disabled) {
12251             input.disabled=true;
12252         }
12253         
12254         var inputblock = input;
12255         
12256         if(this.hasFeedback && !this.allowBlank){
12257             
12258             var feedback = {
12259                 tag: 'span',
12260                 cls: 'glyphicon form-control-feedback'
12261             };
12262             
12263             if(this.removable && !this.editable  ){
12264                 inputblock = {
12265                     cls : 'has-feedback',
12266                     cn :  [
12267                         inputblock,
12268                         {
12269                             tag: 'button',
12270                             html : 'x',
12271                             cls : 'roo-combo-removable-btn close'
12272                         },
12273                         feedback
12274                     ] 
12275                 };
12276             } else {
12277                 inputblock = {
12278                     cls : 'has-feedback',
12279                     cn :  [
12280                         inputblock,
12281                         feedback
12282                     ] 
12283                 };
12284             }
12285
12286         } else {
12287             if(this.removable && !this.editable ){
12288                 inputblock = {
12289                     cls : 'roo-removable',
12290                     cn :  [
12291                         inputblock,
12292                         {
12293                             tag: 'button',
12294                             html : 'x',
12295                             cls : 'roo-combo-removable-btn close'
12296                         }
12297                     ] 
12298                 };
12299             }
12300         }
12301         
12302         if (this.before || this.after) {
12303             
12304             inputblock = {
12305                 cls : 'input-group',
12306                 cn :  [] 
12307             };
12308             if (this.before) {
12309                 inputblock.cn.push({
12310                     tag :'span',
12311                     cls : 'input-group-addon input-group-prepend input-group-text',
12312                     html : this.before
12313                 });
12314             }
12315             
12316             inputblock.cn.push(input);
12317             
12318             if(this.hasFeedback && !this.allowBlank){
12319                 inputblock.cls += ' has-feedback';
12320                 inputblock.cn.push(feedback);
12321             }
12322             
12323             if (this.after) {
12324                 inputblock.cn.push({
12325                     tag :'span',
12326                     cls : 'input-group-addon input-group-append input-group-text',
12327                     html : this.after
12328                 });
12329             }
12330             
12331         };
12332         
12333       
12334         
12335         var ibwrap = inputblock;
12336         
12337         if(this.multiple){
12338             ibwrap = {
12339                 tag: 'ul',
12340                 cls: 'roo-select2-choices',
12341                 cn:[
12342                     {
12343                         tag: 'li',
12344                         cls: 'roo-select2-search-field',
12345                         cn: [
12346
12347                             inputblock
12348                         ]
12349                     }
12350                 ]
12351             };
12352                 
12353         }
12354         
12355         var combobox = {
12356             cls: 'roo-select2-container input-group',
12357             cn: [
12358                  {
12359                     tag: 'input',
12360                     type : 'hidden',
12361                     cls: 'form-hidden-field'
12362                 },
12363                 ibwrap
12364             ]
12365         };
12366         
12367         if(!this.multiple && this.showToggleBtn){
12368             
12369             var caret = {
12370                         tag: 'span',
12371                         cls: 'caret'
12372              };
12373             if (this.caret != false) {
12374                 caret = {
12375                      tag: 'i',
12376                      cls: 'fa fa-' + this.caret
12377                 };
12378                 
12379             }
12380             
12381             combobox.cn.push({
12382                 tag :'span',
12383                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12384                 cn : [
12385                     Roo.bootstrap.version == 3 ? caret : '',
12386                     {
12387                         tag: 'span',
12388                         cls: 'combobox-clear',
12389                         cn  : [
12390                             {
12391                                 tag : 'i',
12392                                 cls: 'icon-remove'
12393                             }
12394                         ]
12395                     }
12396                 ]
12397
12398             })
12399         }
12400         
12401         if(this.multiple){
12402             combobox.cls += ' roo-select2-container-multi';
12403         }
12404          var indicator = {
12405             tag : 'i',
12406             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12407             tooltip : 'This field is required'
12408         };
12409         if (Roo.bootstrap.version == 4) {
12410             indicator = {
12411                 tag : 'i',
12412                 style : 'display:none'
12413             };
12414         }
12415         
12416         
12417         if (align ==='left' && this.fieldLabel.length) {
12418             
12419             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12420
12421             cfg.cn = [
12422                 indicator,
12423                 {
12424                     tag: 'label',
12425                     'for' :  id,
12426                     cls : 'control-label',
12427                     html : this.fieldLabel
12428
12429                 },
12430                 {
12431                     cls : "", 
12432                     cn: [
12433                         combobox
12434                     ]
12435                 }
12436
12437             ];
12438             
12439             var labelCfg = cfg.cn[1];
12440             var contentCfg = cfg.cn[2];
12441             
12442             if(this.indicatorpos == 'right'){
12443                 cfg.cn = [
12444                     {
12445                         tag: 'label',
12446                         'for' :  id,
12447                         cls : 'control-label',
12448                         cn : [
12449                             {
12450                                 tag : 'span',
12451                                 html : this.fieldLabel
12452                             },
12453                             indicator
12454                         ]
12455                     },
12456                     {
12457                         cls : "", 
12458                         cn: [
12459                             combobox
12460                         ]
12461                     }
12462
12463                 ];
12464                 
12465                 labelCfg = cfg.cn[0];
12466                 contentCfg = cfg.cn[1];
12467             }
12468             
12469             if(this.labelWidth > 12){
12470                 labelCfg.style = "width: " + this.labelWidth + 'px';
12471             }
12472             
12473             if(this.labelWidth < 13 && this.labelmd == 0){
12474                 this.labelmd = this.labelWidth;
12475             }
12476             
12477             if(this.labellg > 0){
12478                 labelCfg.cls += ' col-lg-' + this.labellg;
12479                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12480             }
12481             
12482             if(this.labelmd > 0){
12483                 labelCfg.cls += ' col-md-' + this.labelmd;
12484                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12485             }
12486             
12487             if(this.labelsm > 0){
12488                 labelCfg.cls += ' col-sm-' + this.labelsm;
12489                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12490             }
12491             
12492             if(this.labelxs > 0){
12493                 labelCfg.cls += ' col-xs-' + this.labelxs;
12494                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12495             }
12496             
12497         } else if ( this.fieldLabel.length) {
12498 //                Roo.log(" label");
12499             cfg.cn = [
12500                 indicator,
12501                {
12502                    tag: 'label',
12503                    //cls : 'input-group-addon',
12504                    html : this.fieldLabel
12505
12506                },
12507
12508                combobox
12509
12510             ];
12511             
12512             if(this.indicatorpos == 'right'){
12513                 
12514                 cfg.cn = [
12515                     {
12516                        tag: 'label',
12517                        cn : [
12518                            {
12519                                tag : 'span',
12520                                html : this.fieldLabel
12521                            },
12522                            indicator
12523                        ]
12524
12525                     },
12526                     combobox
12527
12528                 ];
12529
12530             }
12531
12532         } else {
12533             
12534 //                Roo.log(" no label && no align");
12535                 cfg = combobox
12536                      
12537                 
12538         }
12539         
12540         var settings=this;
12541         ['xs','sm','md','lg'].map(function(size){
12542             if (settings[size]) {
12543                 cfg.cls += ' col-' + size + '-' + settings[size];
12544             }
12545         });
12546         
12547         return cfg;
12548         
12549     },
12550     
12551     
12552     
12553     // private
12554     onResize : function(w, h){
12555 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12556 //        if(typeof w == 'number'){
12557 //            var x = w - this.trigger.getWidth();
12558 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12559 //            this.trigger.setStyle('left', x+'px');
12560 //        }
12561     },
12562
12563     // private
12564     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12565
12566     // private
12567     getResizeEl : function(){
12568         return this.inputEl();
12569     },
12570
12571     // private
12572     getPositionEl : function(){
12573         return this.inputEl();
12574     },
12575
12576     // private
12577     alignErrorIcon : function(){
12578         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12579     },
12580
12581     // private
12582     initEvents : function(){
12583         
12584         this.createList();
12585         
12586         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12587         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12588         if(!this.multiple && this.showToggleBtn){
12589             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12590             if(this.hideTrigger){
12591                 this.trigger.setDisplayed(false);
12592             }
12593             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12594         }
12595         
12596         if(this.multiple){
12597             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12598         }
12599         
12600         if(this.removable && !this.editable && !this.tickable){
12601             var close = this.closeTriggerEl();
12602             
12603             if(close){
12604                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12605                 close.on('click', this.removeBtnClick, this, close);
12606             }
12607         }
12608         
12609         //this.trigger.addClassOnOver('x-form-trigger-over');
12610         //this.trigger.addClassOnClick('x-form-trigger-click');
12611         
12612         //if(!this.width){
12613         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12614         //}
12615     },
12616     
12617     closeTriggerEl : function()
12618     {
12619         var close = this.el.select('.roo-combo-removable-btn', true).first();
12620         return close ? close : false;
12621     },
12622     
12623     removeBtnClick : function(e, h, el)
12624     {
12625         e.preventDefault();
12626         
12627         if(this.fireEvent("remove", this) !== false){
12628             this.reset();
12629             this.fireEvent("afterremove", this)
12630         }
12631     },
12632     
12633     createList : function()
12634     {
12635         this.list = Roo.get(document.body).createChild({
12636             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12637             cls: 'typeahead typeahead-long dropdown-menu shadow',
12638             style: 'display:none'
12639         });
12640         
12641         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12642         
12643     },
12644
12645     // private
12646     initTrigger : function(){
12647        
12648     },
12649
12650     // private
12651     onDestroy : function(){
12652         if(this.trigger){
12653             this.trigger.removeAllListeners();
12654           //  this.trigger.remove();
12655         }
12656         //if(this.wrap){
12657         //    this.wrap.remove();
12658         //}
12659         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12660     },
12661
12662     // private
12663     onFocus : function(){
12664         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12665         /*
12666         if(!this.mimicing){
12667             this.wrap.addClass('x-trigger-wrap-focus');
12668             this.mimicing = true;
12669             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12670             if(this.monitorTab){
12671                 this.el.on("keydown", this.checkTab, this);
12672             }
12673         }
12674         */
12675     },
12676
12677     // private
12678     checkTab : function(e){
12679         if(e.getKey() == e.TAB){
12680             this.triggerBlur();
12681         }
12682     },
12683
12684     // private
12685     onBlur : function(){
12686         // do nothing
12687     },
12688
12689     // private
12690     mimicBlur : function(e, t){
12691         /*
12692         if(!this.wrap.contains(t) && this.validateBlur()){
12693             this.triggerBlur();
12694         }
12695         */
12696     },
12697
12698     // private
12699     triggerBlur : function(){
12700         this.mimicing = false;
12701         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12702         if(this.monitorTab){
12703             this.el.un("keydown", this.checkTab, this);
12704         }
12705         //this.wrap.removeClass('x-trigger-wrap-focus');
12706         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12707     },
12708
12709     // private
12710     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12711     validateBlur : function(e, t){
12712         return true;
12713     },
12714
12715     // private
12716     onDisable : function(){
12717         this.inputEl().dom.disabled = true;
12718         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12719         //if(this.wrap){
12720         //    this.wrap.addClass('x-item-disabled');
12721         //}
12722     },
12723
12724     // private
12725     onEnable : function(){
12726         this.inputEl().dom.disabled = false;
12727         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12728         //if(this.wrap){
12729         //    this.el.removeClass('x-item-disabled');
12730         //}
12731     },
12732
12733     // private
12734     onShow : function(){
12735         var ae = this.getActionEl();
12736         
12737         if(ae){
12738             ae.dom.style.display = '';
12739             ae.dom.style.visibility = 'visible';
12740         }
12741     },
12742
12743     // private
12744     
12745     onHide : function(){
12746         var ae = this.getActionEl();
12747         ae.dom.style.display = 'none';
12748     },
12749
12750     /**
12751      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12752      * by an implementing function.
12753      * @method
12754      * @param {EventObject} e
12755      */
12756     onTriggerClick : Roo.emptyFn
12757 });
12758  
12759 /*
12760 * Licence: LGPL
12761 */
12762
12763 /**
12764  * @class Roo.bootstrap.CardUploader
12765  * @extends Roo.bootstrap.Button
12766  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12767  * @cfg {Number} errorTimeout default 3000
12768  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12769  * @cfg {Array}  html The button text.
12770
12771  *
12772  * @constructor
12773  * Create a new CardUploader
12774  * @param {Object} config The config object
12775  */
12776
12777 Roo.bootstrap.CardUploader = function(config){
12778     
12779  
12780     
12781     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12782     
12783     
12784     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12785         return r.data.id
12786      });
12787     
12788      this.addEvents({
12789          // raw events
12790         /**
12791          * @event preview
12792          * When a image is clicked on - and needs to display a slideshow or similar..
12793          * @param {Roo.bootstrap.Card} this
12794          * @param {Object} The image information data 
12795          *
12796          */
12797         'preview' : true,
12798          /**
12799          * @event download
12800          * When a the download link is clicked
12801          * @param {Roo.bootstrap.Card} this
12802          * @param {Object} The image information data  contains 
12803          */
12804         'download' : true
12805         
12806     });
12807 };
12808  
12809 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12810     
12811      
12812     errorTimeout : 3000,
12813      
12814     images : false,
12815    
12816     fileCollection : false,
12817     allowBlank : true,
12818     
12819     getAutoCreate : function()
12820     {
12821         
12822         var cfg =  {
12823             cls :'form-group' ,
12824             cn : [
12825                
12826                 {
12827                     tag: 'label',
12828                    //cls : 'input-group-addon',
12829                     html : this.fieldLabel
12830
12831                 },
12832
12833                 {
12834                     tag: 'input',
12835                     type : 'hidden',
12836                     name : this.name,
12837                     value : this.value,
12838                     cls : 'd-none  form-control'
12839                 },
12840                 
12841                 {
12842                     tag: 'input',
12843                     multiple : 'multiple',
12844                     type : 'file',
12845                     cls : 'd-none  roo-card-upload-selector'
12846                 },
12847                 
12848                 {
12849                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12850                 },
12851                 {
12852                     cls : 'card-columns roo-card-uploader-container'
12853                 }
12854
12855             ]
12856         };
12857            
12858          
12859         return cfg;
12860     },
12861     
12862     getChildContainer : function() /// what children are added to.
12863     {
12864         return this.containerEl;
12865     },
12866    
12867     getButtonContainer : function() /// what children are added to.
12868     {
12869         return this.el.select(".roo-card-uploader-button-container").first();
12870     },
12871    
12872     initEvents : function()
12873     {
12874         
12875         Roo.bootstrap.Input.prototype.initEvents.call(this);
12876         
12877         var t = this;
12878         this.addxtype({
12879             xns: Roo.bootstrap,
12880
12881             xtype : 'Button',
12882             container_method : 'getButtonContainer' ,            
12883             html :  this.html, // fix changable?
12884             cls : 'w-100 ',
12885             listeners : {
12886                 'click' : function(btn, e) {
12887                     t.onClick(e);
12888                 }
12889             }
12890         });
12891         
12892         
12893         
12894         
12895         this.urlAPI = (window.createObjectURL && window) || 
12896                                 (window.URL && URL.revokeObjectURL && URL) || 
12897                                 (window.webkitURL && webkitURL);
12898                         
12899          
12900          
12901          
12902         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12903         
12904         this.selectorEl.on('change', this.onFileSelected, this);
12905         if (this.images) {
12906             var t = this;
12907             this.images.forEach(function(img) {
12908                 t.addCard(img)
12909             });
12910             this.images = false;
12911         }
12912         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12913          
12914        
12915     },
12916     
12917    
12918     onClick : function(e)
12919     {
12920         e.preventDefault();
12921          
12922         this.selectorEl.dom.click();
12923          
12924     },
12925     
12926     onFileSelected : function(e)
12927     {
12928         e.preventDefault();
12929         
12930         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12931             return;
12932         }
12933         
12934         Roo.each(this.selectorEl.dom.files, function(file){    
12935             this.addFile(file);
12936         }, this);
12937          
12938     },
12939     
12940       
12941     
12942       
12943     
12944     addFile : function(file)
12945     {
12946            
12947         if(typeof(file) === 'string'){
12948             throw "Add file by name?"; // should not happen
12949             return;
12950         }
12951         
12952         if(!file || !this.urlAPI){
12953             return;
12954         }
12955         
12956         // file;
12957         // file.type;
12958         
12959         var _this = this;
12960         
12961         
12962         var url = _this.urlAPI.createObjectURL( file);
12963            
12964         this.addCard({
12965             id : Roo.bootstrap.CardUploader.ID--,
12966             is_uploaded : false,
12967             src : url,
12968             srcfile : file,
12969             title : file.name,
12970             mimetype : file.type,
12971             preview : false,
12972             is_deleted : 0
12973         });
12974         
12975     },
12976     
12977     /**
12978      * addCard - add an Attachment to the uploader
12979      * @param data - the data about the image to upload
12980      *
12981      * {
12982           id : 123
12983           title : "Title of file",
12984           is_uploaded : false,
12985           src : "http://.....",
12986           srcfile : { the File upload object },
12987           mimetype : file.type,
12988           preview : false,
12989           is_deleted : 0
12990           .. any other data...
12991         }
12992      *
12993      * 
12994     */
12995     
12996     addCard : function (data)
12997     {
12998         // hidden input element?
12999         // if the file is not an image...
13000         //then we need to use something other that and header_image
13001         var t = this;
13002         //   remove.....
13003         var footer = [
13004             {
13005                 xns : Roo.bootstrap,
13006                 xtype : 'CardFooter',
13007                  items: [
13008                     {
13009                         xns : Roo.bootstrap,
13010                         xtype : 'Element',
13011                         cls : 'd-flex',
13012                         items : [
13013                             
13014                             {
13015                                 xns : Roo.bootstrap,
13016                                 xtype : 'Button',
13017                                 html : String.format("<small>{0}</small>", data.title),
13018                                 cls : 'col-10 text-left',
13019                                 size: 'sm',
13020                                 weight: 'link',
13021                                 fa : 'download',
13022                                 listeners : {
13023                                     click : function() {
13024                                      
13025                                         t.fireEvent( "download", t, data );
13026                                     }
13027                                 }
13028                             },
13029                           
13030                             {
13031                                 xns : Roo.bootstrap,
13032                                 xtype : 'Button',
13033                                 style: 'max-height: 28px; ',
13034                                 size : 'sm',
13035                                 weight: 'danger',
13036                                 cls : 'col-2',
13037                                 fa : 'times',
13038                                 listeners : {
13039                                     click : function() {
13040                                         t.removeCard(data.id)
13041                                     }
13042                                 }
13043                             }
13044                         ]
13045                     }
13046                     
13047                 ] 
13048             }
13049             
13050         ];
13051         
13052         var cn = this.addxtype(
13053             {
13054                  
13055                 xns : Roo.bootstrap,
13056                 xtype : 'Card',
13057                 closeable : true,
13058                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13059                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13060                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13061                 data : data,
13062                 html : false,
13063                  
13064                 items : footer,
13065                 initEvents : function() {
13066                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13067                     var card = this;
13068                     this.imgEl = this.el.select('.card-img-top').first();
13069                     if (this.imgEl) {
13070                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13071                         this.imgEl.set({ 'pointer' : 'cursor' });
13072                                   
13073                     }
13074                     this.getCardFooter().addClass('p-1');
13075                     
13076                   
13077                 }
13078                 
13079             }
13080         );
13081         // dont' really need ot update items.
13082         // this.items.push(cn);
13083         this.fileCollection.add(cn);
13084         
13085         if (!data.srcfile) {
13086             this.updateInput();
13087             return;
13088         }
13089             
13090         var _t = this;
13091         var reader = new FileReader();
13092         reader.addEventListener("load", function() {  
13093             data.srcdata =  reader.result;
13094             _t.updateInput();
13095         });
13096         reader.readAsDataURL(data.srcfile);
13097         
13098         
13099         
13100     },
13101     removeCard : function(id)
13102     {
13103         
13104         var card  = this.fileCollection.get(id);
13105         card.data.is_deleted = 1;
13106         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13107         //this.fileCollection.remove(card);
13108         //this.items = this.items.filter(function(e) { return e != card });
13109         // dont' really need ot update items.
13110         card.el.dom.parentNode.removeChild(card.el.dom);
13111         this.updateInput();
13112
13113         
13114     },
13115     reset: function()
13116     {
13117         this.fileCollection.each(function(card) {
13118             if (card.el.dom && card.el.dom.parentNode) {
13119                 card.el.dom.parentNode.removeChild(card.el.dom);
13120             }
13121         });
13122         this.fileCollection.clear();
13123         this.updateInput();
13124     },
13125     
13126     updateInput : function()
13127     {
13128          var data = [];
13129         this.fileCollection.each(function(e) {
13130             data.push(e.data);
13131             
13132         });
13133         this.inputEl().dom.value = JSON.stringify(data);
13134         
13135         
13136         
13137     }
13138     
13139     
13140 });
13141
13142
13143 Roo.bootstrap.CardUploader.ID = -1;/*
13144  * Based on:
13145  * Ext JS Library 1.1.1
13146  * Copyright(c) 2006-2007, Ext JS, LLC.
13147  *
13148  * Originally Released Under LGPL - original licence link has changed is not relivant.
13149  *
13150  * Fork - LGPL
13151  * <script type="text/javascript">
13152  */
13153
13154
13155 /**
13156  * @class Roo.data.SortTypes
13157  * @singleton
13158  * Defines the default sorting (casting?) comparison functions used when sorting data.
13159  */
13160 Roo.data.SortTypes = {
13161     /**
13162      * Default sort that does nothing
13163      * @param {Mixed} s The value being converted
13164      * @return {Mixed} The comparison value
13165      */
13166     none : function(s){
13167         return s;
13168     },
13169     
13170     /**
13171      * The regular expression used to strip tags
13172      * @type {RegExp}
13173      * @property
13174      */
13175     stripTagsRE : /<\/?[^>]+>/gi,
13176     
13177     /**
13178      * Strips all HTML tags to sort on text only
13179      * @param {Mixed} s The value being converted
13180      * @return {String} The comparison value
13181      */
13182     asText : function(s){
13183         return String(s).replace(this.stripTagsRE, "");
13184     },
13185     
13186     /**
13187      * Strips all HTML tags to sort on text only - Case insensitive
13188      * @param {Mixed} s The value being converted
13189      * @return {String} The comparison value
13190      */
13191     asUCText : function(s){
13192         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13193     },
13194     
13195     /**
13196      * Case insensitive string
13197      * @param {Mixed} s The value being converted
13198      * @return {String} The comparison value
13199      */
13200     asUCString : function(s) {
13201         return String(s).toUpperCase();
13202     },
13203     
13204     /**
13205      * Date sorting
13206      * @param {Mixed} s The value being converted
13207      * @return {Number} The comparison value
13208      */
13209     asDate : function(s) {
13210         if(!s){
13211             return 0;
13212         }
13213         if(s instanceof Date){
13214             return s.getTime();
13215         }
13216         return Date.parse(String(s));
13217     },
13218     
13219     /**
13220      * Float sorting
13221      * @param {Mixed} s The value being converted
13222      * @return {Float} The comparison value
13223      */
13224     asFloat : function(s) {
13225         var val = parseFloat(String(s).replace(/,/g, ""));
13226         if(isNaN(val)) {
13227             val = 0;
13228         }
13229         return val;
13230     },
13231     
13232     /**
13233      * Integer sorting
13234      * @param {Mixed} s The value being converted
13235      * @return {Number} The comparison value
13236      */
13237     asInt : function(s) {
13238         var val = parseInt(String(s).replace(/,/g, ""));
13239         if(isNaN(val)) {
13240             val = 0;
13241         }
13242         return val;
13243     }
13244 };/*
13245  * Based on:
13246  * Ext JS Library 1.1.1
13247  * Copyright(c) 2006-2007, Ext JS, LLC.
13248  *
13249  * Originally Released Under LGPL - original licence link has changed is not relivant.
13250  *
13251  * Fork - LGPL
13252  * <script type="text/javascript">
13253  */
13254
13255 /**
13256 * @class Roo.data.Record
13257  * Instances of this class encapsulate both record <em>definition</em> information, and record
13258  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13259  * to access Records cached in an {@link Roo.data.Store} object.<br>
13260  * <p>
13261  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13262  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13263  * objects.<br>
13264  * <p>
13265  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13266  * @constructor
13267  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13268  * {@link #create}. The parameters are the same.
13269  * @param {Array} data An associative Array of data values keyed by the field name.
13270  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13271  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13272  * not specified an integer id is generated.
13273  */
13274 Roo.data.Record = function(data, id){
13275     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13276     this.data = data;
13277 };
13278
13279 /**
13280  * Generate a constructor for a specific record layout.
13281  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13282  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13283  * Each field definition object may contain the following properties: <ul>
13284  * <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,
13285  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13286  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13287  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13288  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13289  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13290  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13291  * this may be omitted.</p></li>
13292  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13293  * <ul><li>auto (Default, implies no conversion)</li>
13294  * <li>string</li>
13295  * <li>int</li>
13296  * <li>float</li>
13297  * <li>boolean</li>
13298  * <li>date</li></ul></p></li>
13299  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13300  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13301  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13302  * by the Reader into an object that will be stored in the Record. It is passed the
13303  * following parameters:<ul>
13304  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13305  * </ul></p></li>
13306  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13307  * </ul>
13308  * <br>usage:<br><pre><code>
13309 var TopicRecord = Roo.data.Record.create(
13310     {name: 'title', mapping: 'topic_title'},
13311     {name: 'author', mapping: 'username'},
13312     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13313     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13314     {name: 'lastPoster', mapping: 'user2'},
13315     {name: 'excerpt', mapping: 'post_text'}
13316 );
13317
13318 var myNewRecord = new TopicRecord({
13319     title: 'Do my job please',
13320     author: 'noobie',
13321     totalPosts: 1,
13322     lastPost: new Date(),
13323     lastPoster: 'Animal',
13324     excerpt: 'No way dude!'
13325 });
13326 myStore.add(myNewRecord);
13327 </code></pre>
13328  * @method create
13329  * @static
13330  */
13331 Roo.data.Record.create = function(o){
13332     var f = function(){
13333         f.superclass.constructor.apply(this, arguments);
13334     };
13335     Roo.extend(f, Roo.data.Record);
13336     var p = f.prototype;
13337     p.fields = new Roo.util.MixedCollection(false, function(field){
13338         return field.name;
13339     });
13340     for(var i = 0, len = o.length; i < len; i++){
13341         p.fields.add(new Roo.data.Field(o[i]));
13342     }
13343     f.getField = function(name){
13344         return p.fields.get(name);  
13345     };
13346     return f;
13347 };
13348
13349 Roo.data.Record.AUTO_ID = 1000;
13350 Roo.data.Record.EDIT = 'edit';
13351 Roo.data.Record.REJECT = 'reject';
13352 Roo.data.Record.COMMIT = 'commit';
13353
13354 Roo.data.Record.prototype = {
13355     /**
13356      * Readonly flag - true if this record has been modified.
13357      * @type Boolean
13358      */
13359     dirty : false,
13360     editing : false,
13361     error: null,
13362     modified: null,
13363
13364     // private
13365     join : function(store){
13366         this.store = store;
13367     },
13368
13369     /**
13370      * Set the named field to the specified value.
13371      * @param {String} name The name of the field to set.
13372      * @param {Object} value The value to set the field to.
13373      */
13374     set : function(name, value){
13375         if(this.data[name] == value){
13376             return;
13377         }
13378         this.dirty = true;
13379         if(!this.modified){
13380             this.modified = {};
13381         }
13382         if(typeof this.modified[name] == 'undefined'){
13383             this.modified[name] = this.data[name];
13384         }
13385         this.data[name] = value;
13386         if(!this.editing && this.store){
13387             this.store.afterEdit(this);
13388         }       
13389     },
13390
13391     /**
13392      * Get the value of the named field.
13393      * @param {String} name The name of the field to get the value of.
13394      * @return {Object} The value of the field.
13395      */
13396     get : function(name){
13397         return this.data[name]; 
13398     },
13399
13400     // private
13401     beginEdit : function(){
13402         this.editing = true;
13403         this.modified = {}; 
13404     },
13405
13406     // private
13407     cancelEdit : function(){
13408         this.editing = false;
13409         delete this.modified;
13410     },
13411
13412     // private
13413     endEdit : function(){
13414         this.editing = false;
13415         if(this.dirty && this.store){
13416             this.store.afterEdit(this);
13417         }
13418     },
13419
13420     /**
13421      * Usually called by the {@link Roo.data.Store} which owns the Record.
13422      * Rejects all changes made to the Record since either creation, or the last commit operation.
13423      * Modified fields are reverted to their original values.
13424      * <p>
13425      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13426      * of reject operations.
13427      */
13428     reject : function(){
13429         var m = this.modified;
13430         for(var n in m){
13431             if(typeof m[n] != "function"){
13432                 this.data[n] = m[n];
13433             }
13434         }
13435         this.dirty = false;
13436         delete this.modified;
13437         this.editing = false;
13438         if(this.store){
13439             this.store.afterReject(this);
13440         }
13441     },
13442
13443     /**
13444      * Usually called by the {@link Roo.data.Store} which owns the Record.
13445      * Commits all changes made to the Record since either creation, or the last commit operation.
13446      * <p>
13447      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13448      * of commit operations.
13449      */
13450     commit : function(){
13451         this.dirty = false;
13452         delete this.modified;
13453         this.editing = false;
13454         if(this.store){
13455             this.store.afterCommit(this);
13456         }
13457     },
13458
13459     // private
13460     hasError : function(){
13461         return this.error != null;
13462     },
13463
13464     // private
13465     clearError : function(){
13466         this.error = null;
13467     },
13468
13469     /**
13470      * Creates a copy of this record.
13471      * @param {String} id (optional) A new record id if you don't want to use this record's id
13472      * @return {Record}
13473      */
13474     copy : function(newId) {
13475         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13476     }
13477 };/*
13478  * Based on:
13479  * Ext JS Library 1.1.1
13480  * Copyright(c) 2006-2007, Ext JS, LLC.
13481  *
13482  * Originally Released Under LGPL - original licence link has changed is not relivant.
13483  *
13484  * Fork - LGPL
13485  * <script type="text/javascript">
13486  */
13487
13488
13489
13490 /**
13491  * @class Roo.data.Store
13492  * @extends Roo.util.Observable
13493  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13494  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13495  * <p>
13496  * 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
13497  * has no knowledge of the format of the data returned by the Proxy.<br>
13498  * <p>
13499  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13500  * instances from the data object. These records are cached and made available through accessor functions.
13501  * @constructor
13502  * Creates a new Store.
13503  * @param {Object} config A config object containing the objects needed for the Store to access data,
13504  * and read the data into Records.
13505  */
13506 Roo.data.Store = function(config){
13507     this.data = new Roo.util.MixedCollection(false);
13508     this.data.getKey = function(o){
13509         return o.id;
13510     };
13511     this.baseParams = {};
13512     // private
13513     this.paramNames = {
13514         "start" : "start",
13515         "limit" : "limit",
13516         "sort" : "sort",
13517         "dir" : "dir",
13518         "multisort" : "_multisort"
13519     };
13520
13521     if(config && config.data){
13522         this.inlineData = config.data;
13523         delete config.data;
13524     }
13525
13526     Roo.apply(this, config);
13527     
13528     if(this.reader){ // reader passed
13529         this.reader = Roo.factory(this.reader, Roo.data);
13530         this.reader.xmodule = this.xmodule || false;
13531         if(!this.recordType){
13532             this.recordType = this.reader.recordType;
13533         }
13534         if(this.reader.onMetaChange){
13535             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13536         }
13537     }
13538
13539     if(this.recordType){
13540         this.fields = this.recordType.prototype.fields;
13541     }
13542     this.modified = [];
13543
13544     this.addEvents({
13545         /**
13546          * @event datachanged
13547          * Fires when the data cache has changed, and a widget which is using this Store
13548          * as a Record cache should refresh its view.
13549          * @param {Store} this
13550          */
13551         datachanged : true,
13552         /**
13553          * @event metachange
13554          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13555          * @param {Store} this
13556          * @param {Object} meta The JSON metadata
13557          */
13558         metachange : true,
13559         /**
13560          * @event add
13561          * Fires when Records have been added to the Store
13562          * @param {Store} this
13563          * @param {Roo.data.Record[]} records The array of Records added
13564          * @param {Number} index The index at which the record(s) were added
13565          */
13566         add : true,
13567         /**
13568          * @event remove
13569          * Fires when a Record has been removed from the Store
13570          * @param {Store} this
13571          * @param {Roo.data.Record} record The Record that was removed
13572          * @param {Number} index The index at which the record was removed
13573          */
13574         remove : true,
13575         /**
13576          * @event update
13577          * Fires when a Record has been updated
13578          * @param {Store} this
13579          * @param {Roo.data.Record} record The Record that was updated
13580          * @param {String} operation The update operation being performed.  Value may be one of:
13581          * <pre><code>
13582  Roo.data.Record.EDIT
13583  Roo.data.Record.REJECT
13584  Roo.data.Record.COMMIT
13585          * </code></pre>
13586          */
13587         update : true,
13588         /**
13589          * @event clear
13590          * Fires when the data cache has been cleared.
13591          * @param {Store} this
13592          */
13593         clear : true,
13594         /**
13595          * @event beforeload
13596          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13597          * the load action will be canceled.
13598          * @param {Store} this
13599          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13600          */
13601         beforeload : true,
13602         /**
13603          * @event beforeloadadd
13604          * Fires after a new set of Records has been loaded.
13605          * @param {Store} this
13606          * @param {Roo.data.Record[]} records The Records that were loaded
13607          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13608          */
13609         beforeloadadd : true,
13610         /**
13611          * @event load
13612          * Fires after a new set of Records has been loaded, before they are added to the store.
13613          * @param {Store} this
13614          * @param {Roo.data.Record[]} records The Records that were loaded
13615          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13616          * @params {Object} return from reader
13617          */
13618         load : true,
13619         /**
13620          * @event loadexception
13621          * Fires if an exception occurs in the Proxy during loading.
13622          * Called with the signature of the Proxy's "loadexception" event.
13623          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13624          * 
13625          * @param {Proxy} 
13626          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13627          * @param {Object} load options 
13628          * @param {Object} jsonData from your request (normally this contains the Exception)
13629          */
13630         loadexception : true
13631     });
13632     
13633     if(this.proxy){
13634         this.proxy = Roo.factory(this.proxy, Roo.data);
13635         this.proxy.xmodule = this.xmodule || false;
13636         this.relayEvents(this.proxy,  ["loadexception"]);
13637     }
13638     this.sortToggle = {};
13639     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13640
13641     Roo.data.Store.superclass.constructor.call(this);
13642
13643     if(this.inlineData){
13644         this.loadData(this.inlineData);
13645         delete this.inlineData;
13646     }
13647 };
13648
13649 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13650      /**
13651     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13652     * without a remote query - used by combo/forms at present.
13653     */
13654     
13655     /**
13656     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13657     */
13658     /**
13659     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13660     */
13661     /**
13662     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13663     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13664     */
13665     /**
13666     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13667     * on any HTTP request
13668     */
13669     /**
13670     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13671     */
13672     /**
13673     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13674     */
13675     multiSort: false,
13676     /**
13677     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13678     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13679     */
13680     remoteSort : false,
13681
13682     /**
13683     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13684      * loaded or when a record is removed. (defaults to false).
13685     */
13686     pruneModifiedRecords : false,
13687
13688     // private
13689     lastOptions : null,
13690
13691     /**
13692      * Add Records to the Store and fires the add event.
13693      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13694      */
13695     add : function(records){
13696         records = [].concat(records);
13697         for(var i = 0, len = records.length; i < len; i++){
13698             records[i].join(this);
13699         }
13700         var index = this.data.length;
13701         this.data.addAll(records);
13702         this.fireEvent("add", this, records, index);
13703     },
13704
13705     /**
13706      * Remove a Record from the Store and fires the remove event.
13707      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13708      */
13709     remove : function(record){
13710         var index = this.data.indexOf(record);
13711         this.data.removeAt(index);
13712  
13713         if(this.pruneModifiedRecords){
13714             this.modified.remove(record);
13715         }
13716         this.fireEvent("remove", this, record, index);
13717     },
13718
13719     /**
13720      * Remove all Records from the Store and fires the clear event.
13721      */
13722     removeAll : function(){
13723         this.data.clear();
13724         if(this.pruneModifiedRecords){
13725             this.modified = [];
13726         }
13727         this.fireEvent("clear", this);
13728     },
13729
13730     /**
13731      * Inserts Records to the Store at the given index and fires the add event.
13732      * @param {Number} index The start index at which to insert the passed Records.
13733      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13734      */
13735     insert : function(index, records){
13736         records = [].concat(records);
13737         for(var i = 0, len = records.length; i < len; i++){
13738             this.data.insert(index, records[i]);
13739             records[i].join(this);
13740         }
13741         this.fireEvent("add", this, records, index);
13742     },
13743
13744     /**
13745      * Get the index within the cache of the passed Record.
13746      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13747      * @return {Number} The index of the passed Record. Returns -1 if not found.
13748      */
13749     indexOf : function(record){
13750         return this.data.indexOf(record);
13751     },
13752
13753     /**
13754      * Get the index within the cache of the Record with the passed id.
13755      * @param {String} id The id of the Record to find.
13756      * @return {Number} The index of the Record. Returns -1 if not found.
13757      */
13758     indexOfId : function(id){
13759         return this.data.indexOfKey(id);
13760     },
13761
13762     /**
13763      * Get the Record with the specified id.
13764      * @param {String} id The id of the Record to find.
13765      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13766      */
13767     getById : function(id){
13768         return this.data.key(id);
13769     },
13770
13771     /**
13772      * Get the Record at the specified index.
13773      * @param {Number} index The index of the Record to find.
13774      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13775      */
13776     getAt : function(index){
13777         return this.data.itemAt(index);
13778     },
13779
13780     /**
13781      * Returns a range of Records between specified indices.
13782      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13783      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13784      * @return {Roo.data.Record[]} An array of Records
13785      */
13786     getRange : function(start, end){
13787         return this.data.getRange(start, end);
13788     },
13789
13790     // private
13791     storeOptions : function(o){
13792         o = Roo.apply({}, o);
13793         delete o.callback;
13794         delete o.scope;
13795         this.lastOptions = o;
13796     },
13797
13798     /**
13799      * Loads the Record cache from the configured Proxy using the configured Reader.
13800      * <p>
13801      * If using remote paging, then the first load call must specify the <em>start</em>
13802      * and <em>limit</em> properties in the options.params property to establish the initial
13803      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13804      * <p>
13805      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13806      * and this call will return before the new data has been loaded. Perform any post-processing
13807      * in a callback function, or in a "load" event handler.</strong>
13808      * <p>
13809      * @param {Object} options An object containing properties which control loading options:<ul>
13810      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13811      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13812      * passed the following arguments:<ul>
13813      * <li>r : Roo.data.Record[]</li>
13814      * <li>options: Options object from the load call</li>
13815      * <li>success: Boolean success indicator</li></ul></li>
13816      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13817      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13818      * </ul>
13819      */
13820     load : function(options){
13821         options = options || {};
13822         if(this.fireEvent("beforeload", this, options) !== false){
13823             this.storeOptions(options);
13824             var p = Roo.apply(options.params || {}, this.baseParams);
13825             // if meta was not loaded from remote source.. try requesting it.
13826             if (!this.reader.metaFromRemote) {
13827                 p._requestMeta = 1;
13828             }
13829             if(this.sortInfo && this.remoteSort){
13830                 var pn = this.paramNames;
13831                 p[pn["sort"]] = this.sortInfo.field;
13832                 p[pn["dir"]] = this.sortInfo.direction;
13833             }
13834             if (this.multiSort) {
13835                 var pn = this.paramNames;
13836                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13837             }
13838             
13839             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13840         }
13841     },
13842
13843     /**
13844      * Reloads the Record cache from the configured Proxy using the configured Reader and
13845      * the options from the last load operation performed.
13846      * @param {Object} options (optional) An object containing properties which may override the options
13847      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13848      * the most recently used options are reused).
13849      */
13850     reload : function(options){
13851         this.load(Roo.applyIf(options||{}, this.lastOptions));
13852     },
13853
13854     // private
13855     // Called as a callback by the Reader during a load operation.
13856     loadRecords : function(o, options, success){
13857         if(!o || success === false){
13858             if(success !== false){
13859                 this.fireEvent("load", this, [], options, o);
13860             }
13861             if(options.callback){
13862                 options.callback.call(options.scope || this, [], options, false);
13863             }
13864             return;
13865         }
13866         // if data returned failure - throw an exception.
13867         if (o.success === false) {
13868             // show a message if no listener is registered.
13869             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13870                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13871             }
13872             // loadmask wil be hooked into this..
13873             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13874             return;
13875         }
13876         var r = o.records, t = o.totalRecords || r.length;
13877         
13878         this.fireEvent("beforeloadadd", this, r, options, o);
13879         
13880         if(!options || options.add !== true){
13881             if(this.pruneModifiedRecords){
13882                 this.modified = [];
13883             }
13884             for(var i = 0, len = r.length; i < len; i++){
13885                 r[i].join(this);
13886             }
13887             if(this.snapshot){
13888                 this.data = this.snapshot;
13889                 delete this.snapshot;
13890             }
13891             this.data.clear();
13892             this.data.addAll(r);
13893             this.totalLength = t;
13894             this.applySort();
13895             this.fireEvent("datachanged", this);
13896         }else{
13897             this.totalLength = Math.max(t, this.data.length+r.length);
13898             this.add(r);
13899         }
13900         
13901         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13902                 
13903             var e = new Roo.data.Record({});
13904
13905             e.set(this.parent.displayField, this.parent.emptyTitle);
13906             e.set(this.parent.valueField, '');
13907
13908             this.insert(0, e);
13909         }
13910             
13911         this.fireEvent("load", this, r, options, o);
13912         if(options.callback){
13913             options.callback.call(options.scope || this, r, options, true);
13914         }
13915     },
13916
13917
13918     /**
13919      * Loads data from a passed data block. A Reader which understands the format of the data
13920      * must have been configured in the constructor.
13921      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13922      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13923      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13924      */
13925     loadData : function(o, append){
13926         var r = this.reader.readRecords(o);
13927         this.loadRecords(r, {add: append}, true);
13928     },
13929     
13930      /**
13931      * using 'cn' the nested child reader read the child array into it's child stores.
13932      * @param {Object} rec The record with a 'children array
13933      */
13934     loadDataFromChildren : function(rec)
13935     {
13936         this.loadData(this.reader.toLoadData(rec));
13937     },
13938     
13939
13940     /**
13941      * Gets the number of cached records.
13942      * <p>
13943      * <em>If using paging, this may not be the total size of the dataset. If the data object
13944      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13945      * the data set size</em>
13946      */
13947     getCount : function(){
13948         return this.data.length || 0;
13949     },
13950
13951     /**
13952      * Gets the total number of records in the dataset as returned by the server.
13953      * <p>
13954      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13955      * the dataset size</em>
13956      */
13957     getTotalCount : function(){
13958         return this.totalLength || 0;
13959     },
13960
13961     /**
13962      * Returns the sort state of the Store as an object with two properties:
13963      * <pre><code>
13964  field {String} The name of the field by which the Records are sorted
13965  direction {String} The sort order, "ASC" or "DESC"
13966      * </code></pre>
13967      */
13968     getSortState : function(){
13969         return this.sortInfo;
13970     },
13971
13972     // private
13973     applySort : function(){
13974         if(this.sortInfo && !this.remoteSort){
13975             var s = this.sortInfo, f = s.field;
13976             var st = this.fields.get(f).sortType;
13977             var fn = function(r1, r2){
13978                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13979                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13980             };
13981             this.data.sort(s.direction, fn);
13982             if(this.snapshot && this.snapshot != this.data){
13983                 this.snapshot.sort(s.direction, fn);
13984             }
13985         }
13986     },
13987
13988     /**
13989      * Sets the default sort column and order to be used by the next load operation.
13990      * @param {String} fieldName The name of the field to sort by.
13991      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13992      */
13993     setDefaultSort : function(field, dir){
13994         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13995     },
13996
13997     /**
13998      * Sort the Records.
13999      * If remote sorting is used, the sort is performed on the server, and the cache is
14000      * reloaded. If local sorting is used, the cache is sorted internally.
14001      * @param {String} fieldName The name of the field to sort by.
14002      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14003      */
14004     sort : function(fieldName, dir){
14005         var f = this.fields.get(fieldName);
14006         if(!dir){
14007             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14008             
14009             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14010                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14011             }else{
14012                 dir = f.sortDir;
14013             }
14014         }
14015         this.sortToggle[f.name] = dir;
14016         this.sortInfo = {field: f.name, direction: dir};
14017         if(!this.remoteSort){
14018             this.applySort();
14019             this.fireEvent("datachanged", this);
14020         }else{
14021             this.load(this.lastOptions);
14022         }
14023     },
14024
14025     /**
14026      * Calls the specified function for each of the Records in the cache.
14027      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14028      * Returning <em>false</em> aborts and exits the iteration.
14029      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14030      */
14031     each : function(fn, scope){
14032         this.data.each(fn, scope);
14033     },
14034
14035     /**
14036      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14037      * (e.g., during paging).
14038      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14039      */
14040     getModifiedRecords : function(){
14041         return this.modified;
14042     },
14043
14044     // private
14045     createFilterFn : function(property, value, anyMatch){
14046         if(!value.exec){ // not a regex
14047             value = String(value);
14048             if(value.length == 0){
14049                 return false;
14050             }
14051             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14052         }
14053         return function(r){
14054             return value.test(r.data[property]);
14055         };
14056     },
14057
14058     /**
14059      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14060      * @param {String} property A field on your records
14061      * @param {Number} start The record index to start at (defaults to 0)
14062      * @param {Number} end The last record index to include (defaults to length - 1)
14063      * @return {Number} The sum
14064      */
14065     sum : function(property, start, end){
14066         var rs = this.data.items, v = 0;
14067         start = start || 0;
14068         end = (end || end === 0) ? end : rs.length-1;
14069
14070         for(var i = start; i <= end; i++){
14071             v += (rs[i].data[property] || 0);
14072         }
14073         return v;
14074     },
14075
14076     /**
14077      * Filter the records by a specified property.
14078      * @param {String} field A field on your records
14079      * @param {String/RegExp} value Either a string that the field
14080      * should start with or a RegExp to test against the field
14081      * @param {Boolean} anyMatch True to match any part not just the beginning
14082      */
14083     filter : function(property, value, anyMatch){
14084         var fn = this.createFilterFn(property, value, anyMatch);
14085         return fn ? this.filterBy(fn) : this.clearFilter();
14086     },
14087
14088     /**
14089      * Filter by a function. The specified function will be called with each
14090      * record in this data source. If the function returns true the record is included,
14091      * otherwise it is filtered.
14092      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14093      * @param {Object} scope (optional) The scope of the function (defaults to this)
14094      */
14095     filterBy : function(fn, scope){
14096         this.snapshot = this.snapshot || this.data;
14097         this.data = this.queryBy(fn, scope||this);
14098         this.fireEvent("datachanged", this);
14099     },
14100
14101     /**
14102      * Query the records by a specified property.
14103      * @param {String} field A field on your records
14104      * @param {String/RegExp} value Either a string that the field
14105      * should start with or a RegExp to test against the field
14106      * @param {Boolean} anyMatch True to match any part not just the beginning
14107      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14108      */
14109     query : function(property, value, anyMatch){
14110         var fn = this.createFilterFn(property, value, anyMatch);
14111         return fn ? this.queryBy(fn) : this.data.clone();
14112     },
14113
14114     /**
14115      * Query by a function. The specified function will be called with each
14116      * record in this data source. If the function returns true the record is included
14117      * in the results.
14118      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14119      * @param {Object} scope (optional) The scope of the function (defaults to this)
14120       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14121      **/
14122     queryBy : function(fn, scope){
14123         var data = this.snapshot || this.data;
14124         return data.filterBy(fn, scope||this);
14125     },
14126
14127     /**
14128      * Collects unique values for a particular dataIndex from this store.
14129      * @param {String} dataIndex The property to collect
14130      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14131      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14132      * @return {Array} An array of the unique values
14133      **/
14134     collect : function(dataIndex, allowNull, bypassFilter){
14135         var d = (bypassFilter === true && this.snapshot) ?
14136                 this.snapshot.items : this.data.items;
14137         var v, sv, r = [], l = {};
14138         for(var i = 0, len = d.length; i < len; i++){
14139             v = d[i].data[dataIndex];
14140             sv = String(v);
14141             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14142                 l[sv] = true;
14143                 r[r.length] = v;
14144             }
14145         }
14146         return r;
14147     },
14148
14149     /**
14150      * Revert to a view of the Record cache with no filtering applied.
14151      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14152      */
14153     clearFilter : function(suppressEvent){
14154         if(this.snapshot && this.snapshot != this.data){
14155             this.data = this.snapshot;
14156             delete this.snapshot;
14157             if(suppressEvent !== true){
14158                 this.fireEvent("datachanged", this);
14159             }
14160         }
14161     },
14162
14163     // private
14164     afterEdit : function(record){
14165         if(this.modified.indexOf(record) == -1){
14166             this.modified.push(record);
14167         }
14168         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14169     },
14170     
14171     // private
14172     afterReject : function(record){
14173         this.modified.remove(record);
14174         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14175     },
14176
14177     // private
14178     afterCommit : function(record){
14179         this.modified.remove(record);
14180         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14181     },
14182
14183     /**
14184      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14185      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14186      */
14187     commitChanges : function(){
14188         var m = this.modified.slice(0);
14189         this.modified = [];
14190         for(var i = 0, len = m.length; i < len; i++){
14191             m[i].commit();
14192         }
14193     },
14194
14195     /**
14196      * Cancel outstanding changes on all changed records.
14197      */
14198     rejectChanges : function(){
14199         var m = this.modified.slice(0);
14200         this.modified = [];
14201         for(var i = 0, len = m.length; i < len; i++){
14202             m[i].reject();
14203         }
14204     },
14205
14206     onMetaChange : function(meta, rtype, o){
14207         this.recordType = rtype;
14208         this.fields = rtype.prototype.fields;
14209         delete this.snapshot;
14210         this.sortInfo = meta.sortInfo || this.sortInfo;
14211         this.modified = [];
14212         this.fireEvent('metachange', this, this.reader.meta);
14213     },
14214     
14215     moveIndex : function(data, type)
14216     {
14217         var index = this.indexOf(data);
14218         
14219         var newIndex = index + type;
14220         
14221         this.remove(data);
14222         
14223         this.insert(newIndex, data);
14224         
14225     }
14226 });/*
14227  * Based on:
14228  * Ext JS Library 1.1.1
14229  * Copyright(c) 2006-2007, Ext JS, LLC.
14230  *
14231  * Originally Released Under LGPL - original licence link has changed is not relivant.
14232  *
14233  * Fork - LGPL
14234  * <script type="text/javascript">
14235  */
14236
14237 /**
14238  * @class Roo.data.SimpleStore
14239  * @extends Roo.data.Store
14240  * Small helper class to make creating Stores from Array data easier.
14241  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14242  * @cfg {Array} fields An array of field definition objects, or field name strings.
14243  * @cfg {Object} an existing reader (eg. copied from another store)
14244  * @cfg {Array} data The multi-dimensional array of data
14245  * @constructor
14246  * @param {Object} config
14247  */
14248 Roo.data.SimpleStore = function(config)
14249 {
14250     Roo.data.SimpleStore.superclass.constructor.call(this, {
14251         isLocal : true,
14252         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14253                 id: config.id
14254             },
14255             Roo.data.Record.create(config.fields)
14256         ),
14257         proxy : new Roo.data.MemoryProxy(config.data)
14258     });
14259     this.load();
14260 };
14261 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14262  * Based on:
14263  * Ext JS Library 1.1.1
14264  * Copyright(c) 2006-2007, Ext JS, LLC.
14265  *
14266  * Originally Released Under LGPL - original licence link has changed is not relivant.
14267  *
14268  * Fork - LGPL
14269  * <script type="text/javascript">
14270  */
14271
14272 /**
14273 /**
14274  * @extends Roo.data.Store
14275  * @class Roo.data.JsonStore
14276  * Small helper class to make creating Stores for JSON data easier. <br/>
14277 <pre><code>
14278 var store = new Roo.data.JsonStore({
14279     url: 'get-images.php',
14280     root: 'images',
14281     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14282 });
14283 </code></pre>
14284  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14285  * JsonReader and HttpProxy (unless inline data is provided).</b>
14286  * @cfg {Array} fields An array of field definition objects, or field name strings.
14287  * @constructor
14288  * @param {Object} config
14289  */
14290 Roo.data.JsonStore = function(c){
14291     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14292         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14293         reader: new Roo.data.JsonReader(c, c.fields)
14294     }));
14295 };
14296 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14297  * Based on:
14298  * Ext JS Library 1.1.1
14299  * Copyright(c) 2006-2007, Ext JS, LLC.
14300  *
14301  * Originally Released Under LGPL - original licence link has changed is not relivant.
14302  *
14303  * Fork - LGPL
14304  * <script type="text/javascript">
14305  */
14306
14307  
14308 Roo.data.Field = function(config){
14309     if(typeof config == "string"){
14310         config = {name: config};
14311     }
14312     Roo.apply(this, config);
14313     
14314     if(!this.type){
14315         this.type = "auto";
14316     }
14317     
14318     var st = Roo.data.SortTypes;
14319     // named sortTypes are supported, here we look them up
14320     if(typeof this.sortType == "string"){
14321         this.sortType = st[this.sortType];
14322     }
14323     
14324     // set default sortType for strings and dates
14325     if(!this.sortType){
14326         switch(this.type){
14327             case "string":
14328                 this.sortType = st.asUCString;
14329                 break;
14330             case "date":
14331                 this.sortType = st.asDate;
14332                 break;
14333             default:
14334                 this.sortType = st.none;
14335         }
14336     }
14337
14338     // define once
14339     var stripRe = /[\$,%]/g;
14340
14341     // prebuilt conversion function for this field, instead of
14342     // switching every time we're reading a value
14343     if(!this.convert){
14344         var cv, dateFormat = this.dateFormat;
14345         switch(this.type){
14346             case "":
14347             case "auto":
14348             case undefined:
14349                 cv = function(v){ return v; };
14350                 break;
14351             case "string":
14352                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14353                 break;
14354             case "int":
14355                 cv = function(v){
14356                     return v !== undefined && v !== null && v !== '' ?
14357                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14358                     };
14359                 break;
14360             case "float":
14361                 cv = function(v){
14362                     return v !== undefined && v !== null && v !== '' ?
14363                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14364                     };
14365                 break;
14366             case "bool":
14367             case "boolean":
14368                 cv = function(v){ return v === true || v === "true" || v == 1; };
14369                 break;
14370             case "date":
14371                 cv = function(v){
14372                     if(!v){
14373                         return '';
14374                     }
14375                     if(v instanceof Date){
14376                         return v;
14377                     }
14378                     if(dateFormat){
14379                         if(dateFormat == "timestamp"){
14380                             return new Date(v*1000);
14381                         }
14382                         return Date.parseDate(v, dateFormat);
14383                     }
14384                     var parsed = Date.parse(v);
14385                     return parsed ? new Date(parsed) : null;
14386                 };
14387              break;
14388             
14389         }
14390         this.convert = cv;
14391     }
14392 };
14393
14394 Roo.data.Field.prototype = {
14395     dateFormat: null,
14396     defaultValue: "",
14397     mapping: null,
14398     sortType : null,
14399     sortDir : "ASC"
14400 };/*
14401  * Based on:
14402  * Ext JS Library 1.1.1
14403  * Copyright(c) 2006-2007, Ext JS, LLC.
14404  *
14405  * Originally Released Under LGPL - original licence link has changed is not relivant.
14406  *
14407  * Fork - LGPL
14408  * <script type="text/javascript">
14409  */
14410  
14411 // Base class for reading structured data from a data source.  This class is intended to be
14412 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14413
14414 /**
14415  * @class Roo.data.DataReader
14416  * Base class for reading structured data from a data source.  This class is intended to be
14417  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14418  */
14419
14420 Roo.data.DataReader = function(meta, recordType){
14421     
14422     this.meta = meta;
14423     
14424     this.recordType = recordType instanceof Array ? 
14425         Roo.data.Record.create(recordType) : recordType;
14426 };
14427
14428 Roo.data.DataReader.prototype = {
14429     
14430     
14431     readerType : 'Data',
14432      /**
14433      * Create an empty record
14434      * @param {Object} data (optional) - overlay some values
14435      * @return {Roo.data.Record} record created.
14436      */
14437     newRow :  function(d) {
14438         var da =  {};
14439         this.recordType.prototype.fields.each(function(c) {
14440             switch( c.type) {
14441                 case 'int' : da[c.name] = 0; break;
14442                 case 'date' : da[c.name] = new Date(); break;
14443                 case 'float' : da[c.name] = 0.0; break;
14444                 case 'boolean' : da[c.name] = false; break;
14445                 default : da[c.name] = ""; break;
14446             }
14447             
14448         });
14449         return new this.recordType(Roo.apply(da, d));
14450     }
14451     
14452     
14453 };/*
14454  * Based on:
14455  * Ext JS Library 1.1.1
14456  * Copyright(c) 2006-2007, Ext JS, LLC.
14457  *
14458  * Originally Released Under LGPL - original licence link has changed is not relivant.
14459  *
14460  * Fork - LGPL
14461  * <script type="text/javascript">
14462  */
14463
14464 /**
14465  * @class Roo.data.DataProxy
14466  * @extends Roo.data.Observable
14467  * This class is an abstract base class for implementations which provide retrieval of
14468  * unformatted data objects.<br>
14469  * <p>
14470  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14471  * (of the appropriate type which knows how to parse the data object) to provide a block of
14472  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14473  * <p>
14474  * Custom implementations must implement the load method as described in
14475  * {@link Roo.data.HttpProxy#load}.
14476  */
14477 Roo.data.DataProxy = function(){
14478     this.addEvents({
14479         /**
14480          * @event beforeload
14481          * Fires before a network request is made to retrieve a data object.
14482          * @param {Object} This DataProxy object.
14483          * @param {Object} params The params parameter to the load function.
14484          */
14485         beforeload : true,
14486         /**
14487          * @event load
14488          * Fires before the load method's callback is called.
14489          * @param {Object} This DataProxy object.
14490          * @param {Object} o The data object.
14491          * @param {Object} arg The callback argument object passed to the load function.
14492          */
14493         load : true,
14494         /**
14495          * @event loadexception
14496          * Fires if an Exception occurs during data retrieval.
14497          * @param {Object} This DataProxy object.
14498          * @param {Object} o The data object.
14499          * @param {Object} arg The callback argument object passed to the load function.
14500          * @param {Object} e The Exception.
14501          */
14502         loadexception : true
14503     });
14504     Roo.data.DataProxy.superclass.constructor.call(this);
14505 };
14506
14507 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14508
14509     /**
14510      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14511      */
14512 /*
14513  * Based on:
14514  * Ext JS Library 1.1.1
14515  * Copyright(c) 2006-2007, Ext JS, LLC.
14516  *
14517  * Originally Released Under LGPL - original licence link has changed is not relivant.
14518  *
14519  * Fork - LGPL
14520  * <script type="text/javascript">
14521  */
14522 /**
14523  * @class Roo.data.MemoryProxy
14524  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14525  * to the Reader when its load method is called.
14526  * @constructor
14527  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14528  */
14529 Roo.data.MemoryProxy = function(data){
14530     if (data.data) {
14531         data = data.data;
14532     }
14533     Roo.data.MemoryProxy.superclass.constructor.call(this);
14534     this.data = data;
14535 };
14536
14537 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14538     
14539     /**
14540      * Load data from the requested source (in this case an in-memory
14541      * data object passed to the constructor), read the data object into
14542      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14543      * process that block using the passed callback.
14544      * @param {Object} params This parameter is not used by the MemoryProxy class.
14545      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14546      * object into a block of Roo.data.Records.
14547      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14548      * The function must be passed <ul>
14549      * <li>The Record block object</li>
14550      * <li>The "arg" argument from the load function</li>
14551      * <li>A boolean success indicator</li>
14552      * </ul>
14553      * @param {Object} scope The scope in which to call the callback
14554      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14555      */
14556     load : function(params, reader, callback, scope, arg){
14557         params = params || {};
14558         var result;
14559         try {
14560             result = reader.readRecords(params.data ? params.data :this.data);
14561         }catch(e){
14562             this.fireEvent("loadexception", this, arg, null, e);
14563             callback.call(scope, null, arg, false);
14564             return;
14565         }
14566         callback.call(scope, result, arg, true);
14567     },
14568     
14569     // private
14570     update : function(params, records){
14571         
14572     }
14573 });/*
14574  * Based on:
14575  * Ext JS Library 1.1.1
14576  * Copyright(c) 2006-2007, Ext JS, LLC.
14577  *
14578  * Originally Released Under LGPL - original licence link has changed is not relivant.
14579  *
14580  * Fork - LGPL
14581  * <script type="text/javascript">
14582  */
14583 /**
14584  * @class Roo.data.HttpProxy
14585  * @extends Roo.data.DataProxy
14586  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14587  * configured to reference a certain URL.<br><br>
14588  * <p>
14589  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14590  * from which the running page was served.<br><br>
14591  * <p>
14592  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14593  * <p>
14594  * Be aware that to enable the browser to parse an XML document, the server must set
14595  * the Content-Type header in the HTTP response to "text/xml".
14596  * @constructor
14597  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14598  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14599  * will be used to make the request.
14600  */
14601 Roo.data.HttpProxy = function(conn){
14602     Roo.data.HttpProxy.superclass.constructor.call(this);
14603     // is conn a conn config or a real conn?
14604     this.conn = conn;
14605     this.useAjax = !conn || !conn.events;
14606   
14607 };
14608
14609 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14610     // thse are take from connection...
14611     
14612     /**
14613      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14614      */
14615     /**
14616      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14617      * extra parameters to each request made by this object. (defaults to undefined)
14618      */
14619     /**
14620      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14621      *  to each request made by this object. (defaults to undefined)
14622      */
14623     /**
14624      * @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)
14625      */
14626     /**
14627      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14628      */
14629      /**
14630      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14631      * @type Boolean
14632      */
14633   
14634
14635     /**
14636      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14637      * @type Boolean
14638      */
14639     /**
14640      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14641      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14642      * a finer-grained basis than the DataProxy events.
14643      */
14644     getConnection : function(){
14645         return this.useAjax ? Roo.Ajax : this.conn;
14646     },
14647
14648     /**
14649      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14650      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14651      * process that block using the passed callback.
14652      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14653      * for the request to the remote server.
14654      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14655      * object into a block of Roo.data.Records.
14656      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14657      * The function must be passed <ul>
14658      * <li>The Record block object</li>
14659      * <li>The "arg" argument from the load function</li>
14660      * <li>A boolean success indicator</li>
14661      * </ul>
14662      * @param {Object} scope The scope in which to call the callback
14663      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14664      */
14665     load : function(params, reader, callback, scope, arg){
14666         if(this.fireEvent("beforeload", this, params) !== false){
14667             var  o = {
14668                 params : params || {},
14669                 request: {
14670                     callback : callback,
14671                     scope : scope,
14672                     arg : arg
14673                 },
14674                 reader: reader,
14675                 callback : this.loadResponse,
14676                 scope: this
14677             };
14678             if(this.useAjax){
14679                 Roo.applyIf(o, this.conn);
14680                 if(this.activeRequest){
14681                     Roo.Ajax.abort(this.activeRequest);
14682                 }
14683                 this.activeRequest = Roo.Ajax.request(o);
14684             }else{
14685                 this.conn.request(o);
14686             }
14687         }else{
14688             callback.call(scope||this, null, arg, false);
14689         }
14690     },
14691
14692     // private
14693     loadResponse : function(o, success, response){
14694         delete this.activeRequest;
14695         if(!success){
14696             this.fireEvent("loadexception", this, o, response);
14697             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14698             return;
14699         }
14700         var result;
14701         try {
14702             result = o.reader.read(response);
14703         }catch(e){
14704             this.fireEvent("loadexception", this, o, response, e);
14705             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14706             return;
14707         }
14708         
14709         this.fireEvent("load", this, o, o.request.arg);
14710         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14711     },
14712
14713     // private
14714     update : function(dataSet){
14715
14716     },
14717
14718     // private
14719     updateResponse : function(dataSet){
14720
14721     }
14722 });/*
14723  * Based on:
14724  * Ext JS Library 1.1.1
14725  * Copyright(c) 2006-2007, Ext JS, LLC.
14726  *
14727  * Originally Released Under LGPL - original licence link has changed is not relivant.
14728  *
14729  * Fork - LGPL
14730  * <script type="text/javascript">
14731  */
14732
14733 /**
14734  * @class Roo.data.ScriptTagProxy
14735  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14736  * other than the originating domain of the running page.<br><br>
14737  * <p>
14738  * <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
14739  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14740  * <p>
14741  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14742  * source code that is used as the source inside a &lt;script> tag.<br><br>
14743  * <p>
14744  * In order for the browser to process the returned data, the server must wrap the data object
14745  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14746  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14747  * depending on whether the callback name was passed:
14748  * <p>
14749  * <pre><code>
14750 boolean scriptTag = false;
14751 String cb = request.getParameter("callback");
14752 if (cb != null) {
14753     scriptTag = true;
14754     response.setContentType("text/javascript");
14755 } else {
14756     response.setContentType("application/x-json");
14757 }
14758 Writer out = response.getWriter();
14759 if (scriptTag) {
14760     out.write(cb + "(");
14761 }
14762 out.print(dataBlock.toJsonString());
14763 if (scriptTag) {
14764     out.write(");");
14765 }
14766 </pre></code>
14767  *
14768  * @constructor
14769  * @param {Object} config A configuration object.
14770  */
14771 Roo.data.ScriptTagProxy = function(config){
14772     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14773     Roo.apply(this, config);
14774     this.head = document.getElementsByTagName("head")[0];
14775 };
14776
14777 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14778
14779 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14780     /**
14781      * @cfg {String} url The URL from which to request the data object.
14782      */
14783     /**
14784      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14785      */
14786     timeout : 30000,
14787     /**
14788      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14789      * the server the name of the callback function set up by the load call to process the returned data object.
14790      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14791      * javascript output which calls this named function passing the data object as its only parameter.
14792      */
14793     callbackParam : "callback",
14794     /**
14795      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14796      * name to the request.
14797      */
14798     nocache : true,
14799
14800     /**
14801      * Load data from the configured URL, read the data object into
14802      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14803      * process that block using the passed callback.
14804      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14805      * for the request to the remote server.
14806      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14807      * object into a block of Roo.data.Records.
14808      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14809      * The function must be passed <ul>
14810      * <li>The Record block object</li>
14811      * <li>The "arg" argument from the load function</li>
14812      * <li>A boolean success indicator</li>
14813      * </ul>
14814      * @param {Object} scope The scope in which to call the callback
14815      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14816      */
14817     load : function(params, reader, callback, scope, arg){
14818         if(this.fireEvent("beforeload", this, params) !== false){
14819
14820             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14821
14822             var url = this.url;
14823             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14824             if(this.nocache){
14825                 url += "&_dc=" + (new Date().getTime());
14826             }
14827             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14828             var trans = {
14829                 id : transId,
14830                 cb : "stcCallback"+transId,
14831                 scriptId : "stcScript"+transId,
14832                 params : params,
14833                 arg : arg,
14834                 url : url,
14835                 callback : callback,
14836                 scope : scope,
14837                 reader : reader
14838             };
14839             var conn = this;
14840
14841             window[trans.cb] = function(o){
14842                 conn.handleResponse(o, trans);
14843             };
14844
14845             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14846
14847             if(this.autoAbort !== false){
14848                 this.abort();
14849             }
14850
14851             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14852
14853             var script = document.createElement("script");
14854             script.setAttribute("src", url);
14855             script.setAttribute("type", "text/javascript");
14856             script.setAttribute("id", trans.scriptId);
14857             this.head.appendChild(script);
14858
14859             this.trans = trans;
14860         }else{
14861             callback.call(scope||this, null, arg, false);
14862         }
14863     },
14864
14865     // private
14866     isLoading : function(){
14867         return this.trans ? true : false;
14868     },
14869
14870     /**
14871      * Abort the current server request.
14872      */
14873     abort : function(){
14874         if(this.isLoading()){
14875             this.destroyTrans(this.trans);
14876         }
14877     },
14878
14879     // private
14880     destroyTrans : function(trans, isLoaded){
14881         this.head.removeChild(document.getElementById(trans.scriptId));
14882         clearTimeout(trans.timeoutId);
14883         if(isLoaded){
14884             window[trans.cb] = undefined;
14885             try{
14886                 delete window[trans.cb];
14887             }catch(e){}
14888         }else{
14889             // if hasn't been loaded, wait for load to remove it to prevent script error
14890             window[trans.cb] = function(){
14891                 window[trans.cb] = undefined;
14892                 try{
14893                     delete window[trans.cb];
14894                 }catch(e){}
14895             };
14896         }
14897     },
14898
14899     // private
14900     handleResponse : function(o, trans){
14901         this.trans = false;
14902         this.destroyTrans(trans, true);
14903         var result;
14904         try {
14905             result = trans.reader.readRecords(o);
14906         }catch(e){
14907             this.fireEvent("loadexception", this, o, trans.arg, e);
14908             trans.callback.call(trans.scope||window, null, trans.arg, false);
14909             return;
14910         }
14911         this.fireEvent("load", this, o, trans.arg);
14912         trans.callback.call(trans.scope||window, result, trans.arg, true);
14913     },
14914
14915     // private
14916     handleFailure : function(trans){
14917         this.trans = false;
14918         this.destroyTrans(trans, false);
14919         this.fireEvent("loadexception", this, null, trans.arg);
14920         trans.callback.call(trans.scope||window, null, trans.arg, false);
14921     }
14922 });/*
14923  * Based on:
14924  * Ext JS Library 1.1.1
14925  * Copyright(c) 2006-2007, Ext JS, LLC.
14926  *
14927  * Originally Released Under LGPL - original licence link has changed is not relivant.
14928  *
14929  * Fork - LGPL
14930  * <script type="text/javascript">
14931  */
14932
14933 /**
14934  * @class Roo.data.JsonReader
14935  * @extends Roo.data.DataReader
14936  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14937  * based on mappings in a provided Roo.data.Record constructor.
14938  * 
14939  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14940  * in the reply previously. 
14941  * 
14942  * <p>
14943  * Example code:
14944  * <pre><code>
14945 var RecordDef = Roo.data.Record.create([
14946     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14947     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14948 ]);
14949 var myReader = new Roo.data.JsonReader({
14950     totalProperty: "results",    // The property which contains the total dataset size (optional)
14951     root: "rows",                // The property which contains an Array of row objects
14952     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14953 }, RecordDef);
14954 </code></pre>
14955  * <p>
14956  * This would consume a JSON file like this:
14957  * <pre><code>
14958 { 'results': 2, 'rows': [
14959     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14960     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14961 }
14962 </code></pre>
14963  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14964  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14965  * paged from the remote server.
14966  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14967  * @cfg {String} root name of the property which contains the Array of row objects.
14968  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14969  * @cfg {Array} fields Array of field definition objects
14970  * @constructor
14971  * Create a new JsonReader
14972  * @param {Object} meta Metadata configuration options
14973  * @param {Object} recordType Either an Array of field definition objects,
14974  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14975  */
14976 Roo.data.JsonReader = function(meta, recordType){
14977     
14978     meta = meta || {};
14979     // set some defaults:
14980     Roo.applyIf(meta, {
14981         totalProperty: 'total',
14982         successProperty : 'success',
14983         root : 'data',
14984         id : 'id'
14985     });
14986     
14987     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14988 };
14989 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14990     
14991     readerType : 'Json',
14992     
14993     /**
14994      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14995      * Used by Store query builder to append _requestMeta to params.
14996      * 
14997      */
14998     metaFromRemote : false,
14999     /**
15000      * This method is only used by a DataProxy which has retrieved data from a remote server.
15001      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15002      * @return {Object} data A data block which is used by an Roo.data.Store object as
15003      * a cache of Roo.data.Records.
15004      */
15005     read : function(response){
15006         var json = response.responseText;
15007        
15008         var o = /* eval:var:o */ eval("("+json+")");
15009         if(!o) {
15010             throw {message: "JsonReader.read: Json object not found"};
15011         }
15012         
15013         if(o.metaData){
15014             
15015             delete this.ef;
15016             this.metaFromRemote = true;
15017             this.meta = o.metaData;
15018             this.recordType = Roo.data.Record.create(o.metaData.fields);
15019             this.onMetaChange(this.meta, this.recordType, o);
15020         }
15021         return this.readRecords(o);
15022     },
15023
15024     // private function a store will implement
15025     onMetaChange : function(meta, recordType, o){
15026
15027     },
15028
15029     /**
15030          * @ignore
15031          */
15032     simpleAccess: function(obj, subsc) {
15033         return obj[subsc];
15034     },
15035
15036         /**
15037          * @ignore
15038          */
15039     getJsonAccessor: function(){
15040         var re = /[\[\.]/;
15041         return function(expr) {
15042             try {
15043                 return(re.test(expr))
15044                     ? new Function("obj", "return obj." + expr)
15045                     : function(obj){
15046                         return obj[expr];
15047                     };
15048             } catch(e){}
15049             return Roo.emptyFn;
15050         };
15051     }(),
15052
15053     /**
15054      * Create a data block containing Roo.data.Records from an XML document.
15055      * @param {Object} o An object which contains an Array of row objects in the property specified
15056      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15057      * which contains the total size of the dataset.
15058      * @return {Object} data A data block which is used by an Roo.data.Store object as
15059      * a cache of Roo.data.Records.
15060      */
15061     readRecords : function(o){
15062         /**
15063          * After any data loads, the raw JSON data is available for further custom processing.
15064          * @type Object
15065          */
15066         this.o = o;
15067         var s = this.meta, Record = this.recordType,
15068             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15069
15070 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15071         if (!this.ef) {
15072             if(s.totalProperty) {
15073                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15074                 }
15075                 if(s.successProperty) {
15076                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15077                 }
15078                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15079                 if (s.id) {
15080                         var g = this.getJsonAccessor(s.id);
15081                         this.getId = function(rec) {
15082                                 var r = g(rec);  
15083                                 return (r === undefined || r === "") ? null : r;
15084                         };
15085                 } else {
15086                         this.getId = function(){return null;};
15087                 }
15088             this.ef = [];
15089             for(var jj = 0; jj < fl; jj++){
15090                 f = fi[jj];
15091                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15092                 this.ef[jj] = this.getJsonAccessor(map);
15093             }
15094         }
15095
15096         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15097         if(s.totalProperty){
15098             var vt = parseInt(this.getTotal(o), 10);
15099             if(!isNaN(vt)){
15100                 totalRecords = vt;
15101             }
15102         }
15103         if(s.successProperty){
15104             var vs = this.getSuccess(o);
15105             if(vs === false || vs === 'false'){
15106                 success = false;
15107             }
15108         }
15109         var records = [];
15110         for(var i = 0; i < c; i++){
15111                 var n = root[i];
15112             var values = {};
15113             var id = this.getId(n);
15114             for(var j = 0; j < fl; j++){
15115                 f = fi[j];
15116             var v = this.ef[j](n);
15117             if (!f.convert) {
15118                 Roo.log('missing convert for ' + f.name);
15119                 Roo.log(f);
15120                 continue;
15121             }
15122             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15123             }
15124             var record = new Record(values, id);
15125             record.json = n;
15126             records[i] = record;
15127         }
15128         return {
15129             raw : o,
15130             success : success,
15131             records : records,
15132             totalRecords : totalRecords
15133         };
15134     },
15135     // used when loading children.. @see loadDataFromChildren
15136     toLoadData: function(rec)
15137     {
15138         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15139         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15140         return { data : data, total : data.length };
15141         
15142     }
15143 });/*
15144  * Based on:
15145  * Ext JS Library 1.1.1
15146  * Copyright(c) 2006-2007, Ext JS, LLC.
15147  *
15148  * Originally Released Under LGPL - original licence link has changed is not relivant.
15149  *
15150  * Fork - LGPL
15151  * <script type="text/javascript">
15152  */
15153
15154 /**
15155  * @class Roo.data.ArrayReader
15156  * @extends Roo.data.DataReader
15157  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15158  * Each element of that Array represents a row of data fields. The
15159  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15160  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15161  * <p>
15162  * Example code:.
15163  * <pre><code>
15164 var RecordDef = Roo.data.Record.create([
15165     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15166     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15167 ]);
15168 var myReader = new Roo.data.ArrayReader({
15169     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15170 }, RecordDef);
15171 </code></pre>
15172  * <p>
15173  * This would consume an Array like this:
15174  * <pre><code>
15175 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15176   </code></pre>
15177  
15178  * @constructor
15179  * Create a new JsonReader
15180  * @param {Object} meta Metadata configuration options.
15181  * @param {Object|Array} recordType Either an Array of field definition objects
15182  * 
15183  * @cfg {Array} fields Array of field definition objects
15184  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15185  * as specified to {@link Roo.data.Record#create},
15186  * or an {@link Roo.data.Record} object
15187  *
15188  * 
15189  * created using {@link Roo.data.Record#create}.
15190  */
15191 Roo.data.ArrayReader = function(meta, recordType)
15192 {    
15193     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15194 };
15195
15196 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15197     
15198       /**
15199      * Create a data block containing Roo.data.Records from an XML document.
15200      * @param {Object} o An Array of row objects which represents the dataset.
15201      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15202      * a cache of Roo.data.Records.
15203      */
15204     readRecords : function(o)
15205     {
15206         var sid = this.meta ? this.meta.id : null;
15207         var recordType = this.recordType, fields = recordType.prototype.fields;
15208         var records = [];
15209         var root = o;
15210         for(var i = 0; i < root.length; i++){
15211             var n = root[i];
15212             var values = {};
15213             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15214             for(var j = 0, jlen = fields.length; j < jlen; j++){
15215                 var f = fields.items[j];
15216                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15217                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15218                 v = f.convert(v);
15219                 values[f.name] = v;
15220             }
15221             var record = new recordType(values, id);
15222             record.json = n;
15223             records[records.length] = record;
15224         }
15225         return {
15226             records : records,
15227             totalRecords : records.length
15228         };
15229     },
15230     // used when loading children.. @see loadDataFromChildren
15231     toLoadData: function(rec)
15232     {
15233         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15234         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15235         
15236     }
15237     
15238     
15239 });/*
15240  * - LGPL
15241  * * 
15242  */
15243
15244 /**
15245  * @class Roo.bootstrap.ComboBox
15246  * @extends Roo.bootstrap.TriggerField
15247  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15248  * @cfg {Boolean} append (true|false) default false
15249  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15250  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15251  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15252  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15253  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15254  * @cfg {Boolean} animate default true
15255  * @cfg {Boolean} emptyResultText only for touch device
15256  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15257  * @cfg {String} emptyTitle default ''
15258  * @cfg {Number} width fixed with? experimental
15259  * @constructor
15260  * Create a new ComboBox.
15261  * @param {Object} config Configuration options
15262  */
15263 Roo.bootstrap.ComboBox = function(config){
15264     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15265     this.addEvents({
15266         /**
15267          * @event expand
15268          * Fires when the dropdown list is expanded
15269         * @param {Roo.bootstrap.ComboBox} combo This combo box
15270         */
15271         'expand' : true,
15272         /**
15273          * @event collapse
15274          * Fires when the dropdown list is collapsed
15275         * @param {Roo.bootstrap.ComboBox} combo This combo box
15276         */
15277         'collapse' : true,
15278         /**
15279          * @event beforeselect
15280          * Fires before a list item is selected. Return false to cancel the selection.
15281         * @param {Roo.bootstrap.ComboBox} combo This combo box
15282         * @param {Roo.data.Record} record The data record returned from the underlying store
15283         * @param {Number} index The index of the selected item in the dropdown list
15284         */
15285         'beforeselect' : true,
15286         /**
15287          * @event select
15288          * Fires when a list item is selected
15289         * @param {Roo.bootstrap.ComboBox} combo This combo box
15290         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15291         * @param {Number} index The index of the selected item in the dropdown list
15292         */
15293         'select' : true,
15294         /**
15295          * @event beforequery
15296          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15297          * The event object passed has these properties:
15298         * @param {Roo.bootstrap.ComboBox} combo This combo box
15299         * @param {String} query The query
15300         * @param {Boolean} forceAll true to force "all" query
15301         * @param {Boolean} cancel true to cancel the query
15302         * @param {Object} e The query event object
15303         */
15304         'beforequery': true,
15305          /**
15306          * @event add
15307          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15308         * @param {Roo.bootstrap.ComboBox} combo This combo box
15309         */
15310         'add' : true,
15311         /**
15312          * @event edit
15313          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15314         * @param {Roo.bootstrap.ComboBox} combo This combo box
15315         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15316         */
15317         'edit' : true,
15318         /**
15319          * @event remove
15320          * Fires when the remove value from the combobox array
15321         * @param {Roo.bootstrap.ComboBox} combo This combo box
15322         */
15323         'remove' : true,
15324         /**
15325          * @event afterremove
15326          * Fires when the remove value from the combobox array
15327         * @param {Roo.bootstrap.ComboBox} combo This combo box
15328         */
15329         'afterremove' : true,
15330         /**
15331          * @event specialfilter
15332          * Fires when specialfilter
15333             * @param {Roo.bootstrap.ComboBox} combo This combo box
15334             */
15335         'specialfilter' : true,
15336         /**
15337          * @event tick
15338          * Fires when tick the element
15339             * @param {Roo.bootstrap.ComboBox} combo This combo box
15340             */
15341         'tick' : true,
15342         /**
15343          * @event touchviewdisplay
15344          * Fires when touch view require special display (default is using displayField)
15345             * @param {Roo.bootstrap.ComboBox} combo This combo box
15346             * @param {Object} cfg set html .
15347             */
15348         'touchviewdisplay' : true
15349         
15350     });
15351     
15352     this.item = [];
15353     this.tickItems = [];
15354     
15355     this.selectedIndex = -1;
15356     if(this.mode == 'local'){
15357         if(config.queryDelay === undefined){
15358             this.queryDelay = 10;
15359         }
15360         if(config.minChars === undefined){
15361             this.minChars = 0;
15362         }
15363     }
15364 };
15365
15366 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15367      
15368     /**
15369      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15370      * rendering into an Roo.Editor, defaults to false)
15371      */
15372     /**
15373      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15374      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15375      */
15376     /**
15377      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15378      */
15379     /**
15380      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15381      * the dropdown list (defaults to undefined, with no header element)
15382      */
15383
15384      /**
15385      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15386      */
15387      
15388      /**
15389      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15390      */
15391     listWidth: undefined,
15392     /**
15393      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15394      * mode = 'remote' or 'text' if mode = 'local')
15395      */
15396     displayField: undefined,
15397     
15398     /**
15399      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15400      * mode = 'remote' or 'value' if mode = 'local'). 
15401      * Note: use of a valueField requires the user make a selection
15402      * in order for a value to be mapped.
15403      */
15404     valueField: undefined,
15405     /**
15406      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15407      */
15408     modalTitle : '',
15409     
15410     /**
15411      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15412      * field's data value (defaults to the underlying DOM element's name)
15413      */
15414     hiddenName: undefined,
15415     /**
15416      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15417      */
15418     listClass: '',
15419     /**
15420      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15421      */
15422     selectedClass: 'active',
15423     
15424     /**
15425      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15426      */
15427     shadow:'sides',
15428     /**
15429      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15430      * anchor positions (defaults to 'tl-bl')
15431      */
15432     listAlign: 'tl-bl?',
15433     /**
15434      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15435      */
15436     maxHeight: 300,
15437     /**
15438      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15439      * query specified by the allQuery config option (defaults to 'query')
15440      */
15441     triggerAction: 'query',
15442     /**
15443      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15444      * (defaults to 4, does not apply if editable = false)
15445      */
15446     minChars : 4,
15447     /**
15448      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15449      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15450      */
15451     typeAhead: false,
15452     /**
15453      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15454      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15455      */
15456     queryDelay: 500,
15457     /**
15458      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15459      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15460      */
15461     pageSize: 0,
15462     /**
15463      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15464      * when editable = true (defaults to false)
15465      */
15466     selectOnFocus:false,
15467     /**
15468      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15469      */
15470     queryParam: 'query',
15471     /**
15472      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15473      * when mode = 'remote' (defaults to 'Loading...')
15474      */
15475     loadingText: 'Loading...',
15476     /**
15477      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15478      */
15479     resizable: false,
15480     /**
15481      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15482      */
15483     handleHeight : 8,
15484     /**
15485      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15486      * traditional select (defaults to true)
15487      */
15488     editable: true,
15489     /**
15490      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15491      */
15492     allQuery: '',
15493     /**
15494      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15495      */
15496     mode: 'remote',
15497     /**
15498      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15499      * listWidth has a higher value)
15500      */
15501     minListWidth : 70,
15502     /**
15503      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15504      * allow the user to set arbitrary text into the field (defaults to false)
15505      */
15506     forceSelection:false,
15507     /**
15508      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15509      * if typeAhead = true (defaults to 250)
15510      */
15511     typeAheadDelay : 250,
15512     /**
15513      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15514      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15515      */
15516     valueNotFoundText : undefined,
15517     /**
15518      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15519      */
15520     blockFocus : false,
15521     
15522     /**
15523      * @cfg {Boolean} disableClear Disable showing of clear button.
15524      */
15525     disableClear : false,
15526     /**
15527      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15528      */
15529     alwaysQuery : false,
15530     
15531     /**
15532      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15533      */
15534     multiple : false,
15535     
15536     /**
15537      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15538      */
15539     invalidClass : "has-warning",
15540     
15541     /**
15542      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15543      */
15544     validClass : "has-success",
15545     
15546     /**
15547      * @cfg {Boolean} specialFilter (true|false) special filter default false
15548      */
15549     specialFilter : false,
15550     
15551     /**
15552      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15553      */
15554     mobileTouchView : true,
15555     
15556     /**
15557      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15558      */
15559     useNativeIOS : false,
15560     
15561     /**
15562      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15563      */
15564     mobile_restrict_height : false,
15565     
15566     ios_options : false,
15567     
15568     //private
15569     addicon : false,
15570     editicon: false,
15571     
15572     page: 0,
15573     hasQuery: false,
15574     append: false,
15575     loadNext: false,
15576     autoFocus : true,
15577     tickable : false,
15578     btnPosition : 'right',
15579     triggerList : true,
15580     showToggleBtn : true,
15581     animate : true,
15582     emptyResultText: 'Empty',
15583     triggerText : 'Select',
15584     emptyTitle : '',
15585     width : false,
15586     
15587     // element that contains real text value.. (when hidden is used..)
15588     
15589     getAutoCreate : function()
15590     {   
15591         var cfg = false;
15592         //render
15593         /*
15594          * Render classic select for iso
15595          */
15596         
15597         if(Roo.isIOS && this.useNativeIOS){
15598             cfg = this.getAutoCreateNativeIOS();
15599             return cfg;
15600         }
15601         
15602         /*
15603          * Touch Devices
15604          */
15605         
15606         if(Roo.isTouch && this.mobileTouchView){
15607             cfg = this.getAutoCreateTouchView();
15608             return cfg;;
15609         }
15610         
15611         /*
15612          *  Normal ComboBox
15613          */
15614         if(!this.tickable){
15615             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15616             return cfg;
15617         }
15618         
15619         /*
15620          *  ComboBox with tickable selections
15621          */
15622              
15623         var align = this.labelAlign || this.parentLabelAlign();
15624         
15625         cfg = {
15626             cls : 'form-group roo-combobox-tickable' //input-group
15627         };
15628         
15629         var btn_text_select = '';
15630         var btn_text_done = '';
15631         var btn_text_cancel = '';
15632         
15633         if (this.btn_text_show) {
15634             btn_text_select = 'Select';
15635             btn_text_done = 'Done';
15636             btn_text_cancel = 'Cancel'; 
15637         }
15638         
15639         var buttons = {
15640             tag : 'div',
15641             cls : 'tickable-buttons',
15642             cn : [
15643                 {
15644                     tag : 'button',
15645                     type : 'button',
15646                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15647                     //html : this.triggerText
15648                     html: btn_text_select
15649                 },
15650                 {
15651                     tag : 'button',
15652                     type : 'button',
15653                     name : 'ok',
15654                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15655                     //html : 'Done'
15656                     html: btn_text_done
15657                 },
15658                 {
15659                     tag : 'button',
15660                     type : 'button',
15661                     name : 'cancel',
15662                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15663                     //html : 'Cancel'
15664                     html: btn_text_cancel
15665                 }
15666             ]
15667         };
15668         
15669         if(this.editable){
15670             buttons.cn.unshift({
15671                 tag: 'input',
15672                 cls: 'roo-select2-search-field-input'
15673             });
15674         }
15675         
15676         var _this = this;
15677         
15678         Roo.each(buttons.cn, function(c){
15679             if (_this.size) {
15680                 c.cls += ' btn-' + _this.size;
15681             }
15682
15683             if (_this.disabled) {
15684                 c.disabled = true;
15685             }
15686         });
15687         
15688         var box = {
15689             tag: 'div',
15690             style : 'display: contents',
15691             cn: [
15692                 {
15693                     tag: 'input',
15694                     type : 'hidden',
15695                     cls: 'form-hidden-field'
15696                 },
15697                 {
15698                     tag: 'ul',
15699                     cls: 'roo-select2-choices',
15700                     cn:[
15701                         {
15702                             tag: 'li',
15703                             cls: 'roo-select2-search-field',
15704                             cn: [
15705                                 buttons
15706                             ]
15707                         }
15708                     ]
15709                 }
15710             ]
15711         };
15712         
15713         var combobox = {
15714             cls: 'roo-select2-container input-group roo-select2-container-multi',
15715             cn: [
15716                 
15717                 box
15718 //                {
15719 //                    tag: 'ul',
15720 //                    cls: 'typeahead typeahead-long dropdown-menu',
15721 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15722 //                }
15723             ]
15724         };
15725         
15726         if(this.hasFeedback && !this.allowBlank){
15727             
15728             var feedback = {
15729                 tag: 'span',
15730                 cls: 'glyphicon form-control-feedback'
15731             };
15732
15733             combobox.cn.push(feedback);
15734         }
15735         
15736         
15737         
15738         var indicator = {
15739             tag : 'i',
15740             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15741             tooltip : 'This field is required'
15742         };
15743         if (Roo.bootstrap.version == 4) {
15744             indicator = {
15745                 tag : 'i',
15746                 style : 'display:none'
15747             };
15748         }
15749         if (align ==='left' && this.fieldLabel.length) {
15750             
15751             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15752             
15753             cfg.cn = [
15754                 indicator,
15755                 {
15756                     tag: 'label',
15757                     'for' :  id,
15758                     cls : 'control-label col-form-label',
15759                     html : this.fieldLabel
15760
15761                 },
15762                 {
15763                     cls : "", 
15764                     cn: [
15765                         combobox
15766                     ]
15767                 }
15768
15769             ];
15770             
15771             var labelCfg = cfg.cn[1];
15772             var contentCfg = cfg.cn[2];
15773             
15774
15775             if(this.indicatorpos == 'right'){
15776                 
15777                 cfg.cn = [
15778                     {
15779                         tag: 'label',
15780                         'for' :  id,
15781                         cls : 'control-label col-form-label',
15782                         cn : [
15783                             {
15784                                 tag : 'span',
15785                                 html : this.fieldLabel
15786                             },
15787                             indicator
15788                         ]
15789                     },
15790                     {
15791                         cls : "",
15792                         cn: [
15793                             combobox
15794                         ]
15795                     }
15796
15797                 ];
15798                 
15799                 
15800                 
15801                 labelCfg = cfg.cn[0];
15802                 contentCfg = cfg.cn[1];
15803             
15804             }
15805             
15806             if(this.labelWidth > 12){
15807                 labelCfg.style = "width: " + this.labelWidth + 'px';
15808             }
15809             if(this.width * 1 > 0){
15810                 contentCfg.style = "width: " + this.width + 'px';
15811             }
15812             if(this.labelWidth < 13 && this.labelmd == 0){
15813                 this.labelmd = this.labelWidth;
15814             }
15815             
15816             if(this.labellg > 0){
15817                 labelCfg.cls += ' col-lg-' + this.labellg;
15818                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15819             }
15820             
15821             if(this.labelmd > 0){
15822                 labelCfg.cls += ' col-md-' + this.labelmd;
15823                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15824             }
15825             
15826             if(this.labelsm > 0){
15827                 labelCfg.cls += ' col-sm-' + this.labelsm;
15828                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15829             }
15830             
15831             if(this.labelxs > 0){
15832                 labelCfg.cls += ' col-xs-' + this.labelxs;
15833                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15834             }
15835                 
15836                 
15837         } else if ( this.fieldLabel.length) {
15838 //                Roo.log(" label");
15839                  cfg.cn = [
15840                    indicator,
15841                     {
15842                         tag: 'label',
15843                         //cls : 'input-group-addon',
15844                         html : this.fieldLabel
15845                     },
15846                     combobox
15847                 ];
15848                 
15849                 if(this.indicatorpos == 'right'){
15850                     cfg.cn = [
15851                         {
15852                             tag: 'label',
15853                             //cls : 'input-group-addon',
15854                             html : this.fieldLabel
15855                         },
15856                         indicator,
15857                         combobox
15858                     ];
15859                     
15860                 }
15861
15862         } else {
15863             
15864 //                Roo.log(" no label && no align");
15865                 cfg = combobox
15866                      
15867                 
15868         }
15869          
15870         var settings=this;
15871         ['xs','sm','md','lg'].map(function(size){
15872             if (settings[size]) {
15873                 cfg.cls += ' col-' + size + '-' + settings[size];
15874             }
15875         });
15876         
15877         return cfg;
15878         
15879     },
15880     
15881     _initEventsCalled : false,
15882     
15883     // private
15884     initEvents: function()
15885     {   
15886         if (this._initEventsCalled) { // as we call render... prevent looping...
15887             return;
15888         }
15889         this._initEventsCalled = true;
15890         
15891         if (!this.store) {
15892             throw "can not find store for combo";
15893         }
15894         
15895         this.indicator = this.indicatorEl();
15896         
15897         this.store = Roo.factory(this.store, Roo.data);
15898         this.store.parent = this;
15899         
15900         // if we are building from html. then this element is so complex, that we can not really
15901         // use the rendered HTML.
15902         // so we have to trash and replace the previous code.
15903         if (Roo.XComponent.build_from_html) {
15904             // remove this element....
15905             var e = this.el.dom, k=0;
15906             while (e ) { e = e.previousSibling;  ++k;}
15907
15908             this.el.remove();
15909             
15910             this.el=false;
15911             this.rendered = false;
15912             
15913             this.render(this.parent().getChildContainer(true), k);
15914         }
15915         
15916         if(Roo.isIOS && this.useNativeIOS){
15917             this.initIOSView();
15918             return;
15919         }
15920         
15921         /*
15922          * Touch Devices
15923          */
15924         
15925         if(Roo.isTouch && this.mobileTouchView){
15926             this.initTouchView();
15927             return;
15928         }
15929         
15930         if(this.tickable){
15931             this.initTickableEvents();
15932             return;
15933         }
15934         
15935         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15936         
15937         if(this.hiddenName){
15938             
15939             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15940             
15941             this.hiddenField.dom.value =
15942                 this.hiddenValue !== undefined ? this.hiddenValue :
15943                 this.value !== undefined ? this.value : '';
15944
15945             // prevent input submission
15946             this.el.dom.removeAttribute('name');
15947             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15948              
15949              
15950         }
15951         //if(Roo.isGecko){
15952         //    this.el.dom.setAttribute('autocomplete', 'off');
15953         //}
15954         
15955         var cls = 'x-combo-list';
15956         
15957         //this.list = new Roo.Layer({
15958         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15959         //});
15960         
15961         var _this = this;
15962         
15963         (function(){
15964             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15965             _this.list.setWidth(lw);
15966         }).defer(100);
15967         
15968         this.list.on('mouseover', this.onViewOver, this);
15969         this.list.on('mousemove', this.onViewMove, this);
15970         this.list.on('scroll', this.onViewScroll, this);
15971         
15972         /*
15973         this.list.swallowEvent('mousewheel');
15974         this.assetHeight = 0;
15975
15976         if(this.title){
15977             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15978             this.assetHeight += this.header.getHeight();
15979         }
15980
15981         this.innerList = this.list.createChild({cls:cls+'-inner'});
15982         this.innerList.on('mouseover', this.onViewOver, this);
15983         this.innerList.on('mousemove', this.onViewMove, this);
15984         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15985         
15986         if(this.allowBlank && !this.pageSize && !this.disableClear){
15987             this.footer = this.list.createChild({cls:cls+'-ft'});
15988             this.pageTb = new Roo.Toolbar(this.footer);
15989            
15990         }
15991         if(this.pageSize){
15992             this.footer = this.list.createChild({cls:cls+'-ft'});
15993             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15994                     {pageSize: this.pageSize});
15995             
15996         }
15997         
15998         if (this.pageTb && this.allowBlank && !this.disableClear) {
15999             var _this = this;
16000             this.pageTb.add(new Roo.Toolbar.Fill(), {
16001                 cls: 'x-btn-icon x-btn-clear',
16002                 text: '&#160;',
16003                 handler: function()
16004                 {
16005                     _this.collapse();
16006                     _this.clearValue();
16007                     _this.onSelect(false, -1);
16008                 }
16009             });
16010         }
16011         if (this.footer) {
16012             this.assetHeight += this.footer.getHeight();
16013         }
16014         */
16015             
16016         if(!this.tpl){
16017             this.tpl = Roo.bootstrap.version == 4 ?
16018                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16019                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16020         }
16021
16022         this.view = new Roo.View(this.list, this.tpl, {
16023             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16024         });
16025         //this.view.wrapEl.setDisplayed(false);
16026         this.view.on('click', this.onViewClick, this);
16027         
16028         
16029         this.store.on('beforeload', this.onBeforeLoad, this);
16030         this.store.on('load', this.onLoad, this);
16031         this.store.on('loadexception', this.onLoadException, this);
16032         /*
16033         if(this.resizable){
16034             this.resizer = new Roo.Resizable(this.list,  {
16035                pinned:true, handles:'se'
16036             });
16037             this.resizer.on('resize', function(r, w, h){
16038                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16039                 this.listWidth = w;
16040                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16041                 this.restrictHeight();
16042             }, this);
16043             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16044         }
16045         */
16046         if(!this.editable){
16047             this.editable = true;
16048             this.setEditable(false);
16049         }
16050         
16051         /*
16052         
16053         if (typeof(this.events.add.listeners) != 'undefined') {
16054             
16055             this.addicon = this.wrap.createChild(
16056                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16057        
16058             this.addicon.on('click', function(e) {
16059                 this.fireEvent('add', this);
16060             }, this);
16061         }
16062         if (typeof(this.events.edit.listeners) != 'undefined') {
16063             
16064             this.editicon = this.wrap.createChild(
16065                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16066             if (this.addicon) {
16067                 this.editicon.setStyle('margin-left', '40px');
16068             }
16069             this.editicon.on('click', function(e) {
16070                 
16071                 // we fire even  if inothing is selected..
16072                 this.fireEvent('edit', this, this.lastData );
16073                 
16074             }, this);
16075         }
16076         */
16077         
16078         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16079             "up" : function(e){
16080                 this.inKeyMode = true;
16081                 this.selectPrev();
16082             },
16083
16084             "down" : function(e){
16085                 if(!this.isExpanded()){
16086                     this.onTriggerClick();
16087                 }else{
16088                     this.inKeyMode = true;
16089                     this.selectNext();
16090                 }
16091             },
16092
16093             "enter" : function(e){
16094 //                this.onViewClick();
16095                 //return true;
16096                 this.collapse();
16097                 
16098                 if(this.fireEvent("specialkey", this, e)){
16099                     this.onViewClick(false);
16100                 }
16101                 
16102                 return true;
16103             },
16104
16105             "esc" : function(e){
16106                 this.collapse();
16107             },
16108
16109             "tab" : function(e){
16110                 this.collapse();
16111                 
16112                 if(this.fireEvent("specialkey", this, e)){
16113                     this.onViewClick(false);
16114                 }
16115                 
16116                 return true;
16117             },
16118
16119             scope : this,
16120
16121             doRelay : function(foo, bar, hname){
16122                 if(hname == 'down' || this.scope.isExpanded()){
16123                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16124                 }
16125                 return true;
16126             },
16127
16128             forceKeyDown: true
16129         });
16130         
16131         
16132         this.queryDelay = Math.max(this.queryDelay || 10,
16133                 this.mode == 'local' ? 10 : 250);
16134         
16135         
16136         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16137         
16138         if(this.typeAhead){
16139             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16140         }
16141         if(this.editable !== false){
16142             this.inputEl().on("keyup", this.onKeyUp, this);
16143         }
16144         if(this.forceSelection){
16145             this.inputEl().on('blur', this.doForce, this);
16146         }
16147         
16148         if(this.multiple){
16149             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16150             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16151         }
16152     },
16153     
16154     initTickableEvents: function()
16155     {   
16156         this.createList();
16157         
16158         if(this.hiddenName){
16159             
16160             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16161             
16162             this.hiddenField.dom.value =
16163                 this.hiddenValue !== undefined ? this.hiddenValue :
16164                 this.value !== undefined ? this.value : '';
16165
16166             // prevent input submission
16167             this.el.dom.removeAttribute('name');
16168             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16169              
16170              
16171         }
16172         
16173 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16174         
16175         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16176         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16177         if(this.triggerList){
16178             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16179         }
16180          
16181         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16182         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16183         
16184         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16185         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16186         
16187         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16188         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16189         
16190         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16191         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16192         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16193         
16194         this.okBtn.hide();
16195         this.cancelBtn.hide();
16196         
16197         var _this = this;
16198         
16199         (function(){
16200             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16201             _this.list.setWidth(lw);
16202         }).defer(100);
16203         
16204         this.list.on('mouseover', this.onViewOver, this);
16205         this.list.on('mousemove', this.onViewMove, this);
16206         
16207         this.list.on('scroll', this.onViewScroll, this);
16208         
16209         if(!this.tpl){
16210             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16211                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16212         }
16213
16214         this.view = new Roo.View(this.list, this.tpl, {
16215             singleSelect:true,
16216             tickable:true,
16217             parent:this,
16218             store: this.store,
16219             selectedClass: this.selectedClass
16220         });
16221         
16222         //this.view.wrapEl.setDisplayed(false);
16223         this.view.on('click', this.onViewClick, this);
16224         
16225         
16226         
16227         this.store.on('beforeload', this.onBeforeLoad, this);
16228         this.store.on('load', this.onLoad, this);
16229         this.store.on('loadexception', this.onLoadException, this);
16230         
16231         if(this.editable){
16232             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16233                 "up" : function(e){
16234                     this.inKeyMode = true;
16235                     this.selectPrev();
16236                 },
16237
16238                 "down" : function(e){
16239                     this.inKeyMode = true;
16240                     this.selectNext();
16241                 },
16242
16243                 "enter" : function(e){
16244                     if(this.fireEvent("specialkey", this, e)){
16245                         this.onViewClick(false);
16246                     }
16247                     
16248                     return true;
16249                 },
16250
16251                 "esc" : function(e){
16252                     this.onTickableFooterButtonClick(e, false, false);
16253                 },
16254
16255                 "tab" : function(e){
16256                     this.fireEvent("specialkey", this, e);
16257                     
16258                     this.onTickableFooterButtonClick(e, false, false);
16259                     
16260                     return true;
16261                 },
16262
16263                 scope : this,
16264
16265                 doRelay : function(e, fn, key){
16266                     if(this.scope.isExpanded()){
16267                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16268                     }
16269                     return true;
16270                 },
16271
16272                 forceKeyDown: true
16273             });
16274         }
16275         
16276         this.queryDelay = Math.max(this.queryDelay || 10,
16277                 this.mode == 'local' ? 10 : 250);
16278         
16279         
16280         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16281         
16282         if(this.typeAhead){
16283             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16284         }
16285         
16286         if(this.editable !== false){
16287             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16288         }
16289         
16290         this.indicator = this.indicatorEl();
16291         
16292         if(this.indicator){
16293             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16294             this.indicator.hide();
16295         }
16296         
16297     },
16298
16299     onDestroy : function(){
16300         if(this.view){
16301             this.view.setStore(null);
16302             this.view.el.removeAllListeners();
16303             this.view.el.remove();
16304             this.view.purgeListeners();
16305         }
16306         if(this.list){
16307             this.list.dom.innerHTML  = '';
16308         }
16309         
16310         if(this.store){
16311             this.store.un('beforeload', this.onBeforeLoad, this);
16312             this.store.un('load', this.onLoad, this);
16313             this.store.un('loadexception', this.onLoadException, this);
16314         }
16315         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16316     },
16317
16318     // private
16319     fireKey : function(e){
16320         if(e.isNavKeyPress() && !this.list.isVisible()){
16321             this.fireEvent("specialkey", this, e);
16322         }
16323     },
16324
16325     // private
16326     onResize: function(w, h)
16327     {
16328         
16329         
16330 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16331 //        
16332 //        if(typeof w != 'number'){
16333 //            // we do not handle it!?!?
16334 //            return;
16335 //        }
16336 //        var tw = this.trigger.getWidth();
16337 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16338 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16339 //        var x = w - tw;
16340 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16341 //            
16342 //        //this.trigger.setStyle('left', x+'px');
16343 //        
16344 //        if(this.list && this.listWidth === undefined){
16345 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16346 //            this.list.setWidth(lw);
16347 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16348 //        }
16349         
16350     
16351         
16352     },
16353
16354     /**
16355      * Allow or prevent the user from directly editing the field text.  If false is passed,
16356      * the user will only be able to select from the items defined in the dropdown list.  This method
16357      * is the runtime equivalent of setting the 'editable' config option at config time.
16358      * @param {Boolean} value True to allow the user to directly edit the field text
16359      */
16360     setEditable : function(value){
16361         if(value == this.editable){
16362             return;
16363         }
16364         this.editable = value;
16365         if(!value){
16366             this.inputEl().dom.setAttribute('readOnly', true);
16367             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16368             this.inputEl().addClass('x-combo-noedit');
16369         }else{
16370             this.inputEl().dom.setAttribute('readOnly', false);
16371             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16372             this.inputEl().removeClass('x-combo-noedit');
16373         }
16374     },
16375
16376     // private
16377     
16378     onBeforeLoad : function(combo,opts){
16379         if(!this.hasFocus){
16380             return;
16381         }
16382          if (!opts.add) {
16383             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16384          }
16385         this.restrictHeight();
16386         this.selectedIndex = -1;
16387     },
16388
16389     // private
16390     onLoad : function(){
16391         
16392         this.hasQuery = false;
16393         
16394         if(!this.hasFocus){
16395             return;
16396         }
16397         
16398         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16399             this.loading.hide();
16400         }
16401         
16402         if(this.store.getCount() > 0){
16403             
16404             this.expand();
16405             this.restrictHeight();
16406             if(this.lastQuery == this.allQuery){
16407                 if(this.editable && !this.tickable){
16408                     this.inputEl().dom.select();
16409                 }
16410                 
16411                 if(
16412                     !this.selectByValue(this.value, true) &&
16413                     this.autoFocus && 
16414                     (
16415                         !this.store.lastOptions ||
16416                         typeof(this.store.lastOptions.add) == 'undefined' || 
16417                         this.store.lastOptions.add != true
16418                     )
16419                 ){
16420                     this.select(0, true);
16421                 }
16422             }else{
16423                 if(this.autoFocus){
16424                     this.selectNext();
16425                 }
16426                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16427                     this.taTask.delay(this.typeAheadDelay);
16428                 }
16429             }
16430         }else{
16431             this.onEmptyResults();
16432         }
16433         
16434         //this.el.focus();
16435     },
16436     // private
16437     onLoadException : function()
16438     {
16439         this.hasQuery = false;
16440         
16441         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16442             this.loading.hide();
16443         }
16444         
16445         if(this.tickable && this.editable){
16446             return;
16447         }
16448         
16449         this.collapse();
16450         // only causes errors at present
16451         //Roo.log(this.store.reader.jsonData);
16452         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16453             // fixme
16454             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16455         //}
16456         
16457         
16458     },
16459     // private
16460     onTypeAhead : function(){
16461         if(this.store.getCount() > 0){
16462             var r = this.store.getAt(0);
16463             var newValue = r.data[this.displayField];
16464             var len = newValue.length;
16465             var selStart = this.getRawValue().length;
16466             
16467             if(selStart != len){
16468                 this.setRawValue(newValue);
16469                 this.selectText(selStart, newValue.length);
16470             }
16471         }
16472     },
16473
16474     // private
16475     onSelect : function(record, index){
16476         
16477         if(this.fireEvent('beforeselect', this, record, index) !== false){
16478         
16479             this.setFromData(index > -1 ? record.data : false);
16480             
16481             this.collapse();
16482             this.fireEvent('select', this, record, index);
16483         }
16484     },
16485
16486     /**
16487      * Returns the currently selected field value or empty string if no value is set.
16488      * @return {String} value The selected value
16489      */
16490     getValue : function()
16491     {
16492         if(Roo.isIOS && this.useNativeIOS){
16493             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16494         }
16495         
16496         if(this.multiple){
16497             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16498         }
16499         
16500         if(this.valueField){
16501             return typeof this.value != 'undefined' ? this.value : '';
16502         }else{
16503             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16504         }
16505     },
16506     
16507     getRawValue : function()
16508     {
16509         if(Roo.isIOS && this.useNativeIOS){
16510             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16511         }
16512         
16513         var v = this.inputEl().getValue();
16514         
16515         return v;
16516     },
16517
16518     /**
16519      * Clears any text/value currently set in the field
16520      */
16521     clearValue : function(){
16522         
16523         if(this.hiddenField){
16524             this.hiddenField.dom.value = '';
16525         }
16526         this.value = '';
16527         this.setRawValue('');
16528         this.lastSelectionText = '';
16529         this.lastData = false;
16530         
16531         var close = this.closeTriggerEl();
16532         
16533         if(close){
16534             close.hide();
16535         }
16536         
16537         this.validate();
16538         
16539     },
16540
16541     /**
16542      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16543      * will be displayed in the field.  If the value does not match the data value of an existing item,
16544      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16545      * Otherwise the field will be blank (although the value will still be set).
16546      * @param {String} value The value to match
16547      */
16548     setValue : function(v)
16549     {
16550         if(Roo.isIOS && this.useNativeIOS){
16551             this.setIOSValue(v);
16552             return;
16553         }
16554         
16555         if(this.multiple){
16556             this.syncValue();
16557             return;
16558         }
16559         
16560         var text = v;
16561         if(this.valueField){
16562             var r = this.findRecord(this.valueField, v);
16563             if(r){
16564                 text = r.data[this.displayField];
16565             }else if(this.valueNotFoundText !== undefined){
16566                 text = this.valueNotFoundText;
16567             }
16568         }
16569         this.lastSelectionText = text;
16570         if(this.hiddenField){
16571             this.hiddenField.dom.value = v;
16572         }
16573         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16574         this.value = v;
16575         
16576         var close = this.closeTriggerEl();
16577         
16578         if(close){
16579             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16580         }
16581         
16582         this.validate();
16583     },
16584     /**
16585      * @property {Object} the last set data for the element
16586      */
16587     
16588     lastData : false,
16589     /**
16590      * Sets the value of the field based on a object which is related to the record format for the store.
16591      * @param {Object} value the value to set as. or false on reset?
16592      */
16593     setFromData : function(o){
16594         
16595         if(this.multiple){
16596             this.addItem(o);
16597             return;
16598         }
16599             
16600         var dv = ''; // display value
16601         var vv = ''; // value value..
16602         this.lastData = o;
16603         if (this.displayField) {
16604             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16605         } else {
16606             // this is an error condition!!!
16607             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16608         }
16609         
16610         if(this.valueField){
16611             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16612         }
16613         
16614         var close = this.closeTriggerEl();
16615         
16616         if(close){
16617             if(dv.length || vv * 1 > 0){
16618                 close.show() ;
16619                 this.blockFocus=true;
16620             } else {
16621                 close.hide();
16622             }             
16623         }
16624         
16625         if(this.hiddenField){
16626             this.hiddenField.dom.value = vv;
16627             
16628             this.lastSelectionText = dv;
16629             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16630             this.value = vv;
16631             return;
16632         }
16633         // no hidden field.. - we store the value in 'value', but still display
16634         // display field!!!!
16635         this.lastSelectionText = dv;
16636         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16637         this.value = vv;
16638         
16639         
16640         
16641     },
16642     // private
16643     reset : function(){
16644         // overridden so that last data is reset..
16645         
16646         if(this.multiple){
16647             this.clearItem();
16648             return;
16649         }
16650         
16651         this.setValue(this.originalValue);
16652         //this.clearInvalid();
16653         this.lastData = false;
16654         if (this.view) {
16655             this.view.clearSelections();
16656         }
16657         
16658         this.validate();
16659     },
16660     // private
16661     findRecord : function(prop, value){
16662         var record;
16663         if(this.store.getCount() > 0){
16664             this.store.each(function(r){
16665                 if(r.data[prop] == value){
16666                     record = r;
16667                     return false;
16668                 }
16669                 return true;
16670             });
16671         }
16672         return record;
16673     },
16674     
16675     getName: function()
16676     {
16677         // returns hidden if it's set..
16678         if (!this.rendered) {return ''};
16679         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16680         
16681     },
16682     // private
16683     onViewMove : function(e, t){
16684         this.inKeyMode = false;
16685     },
16686
16687     // private
16688     onViewOver : function(e, t){
16689         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16690             return;
16691         }
16692         var item = this.view.findItemFromChild(t);
16693         
16694         if(item){
16695             var index = this.view.indexOf(item);
16696             this.select(index, false);
16697         }
16698     },
16699
16700     // private
16701     onViewClick : function(view, doFocus, el, e)
16702     {
16703         var index = this.view.getSelectedIndexes()[0];
16704         
16705         var r = this.store.getAt(index);
16706         
16707         if(this.tickable){
16708             
16709             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16710                 return;
16711             }
16712             
16713             var rm = false;
16714             var _this = this;
16715             
16716             Roo.each(this.tickItems, function(v,k){
16717                 
16718                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16719                     Roo.log(v);
16720                     _this.tickItems.splice(k, 1);
16721                     
16722                     if(typeof(e) == 'undefined' && view == false){
16723                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16724                     }
16725                     
16726                     rm = true;
16727                     return;
16728                 }
16729             });
16730             
16731             if(rm){
16732                 return;
16733             }
16734             
16735             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16736                 this.tickItems.push(r.data);
16737             }
16738             
16739             if(typeof(e) == 'undefined' && view == false){
16740                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16741             }
16742                     
16743             return;
16744         }
16745         
16746         if(r){
16747             this.onSelect(r, index);
16748         }
16749         if(doFocus !== false && !this.blockFocus){
16750             this.inputEl().focus();
16751         }
16752     },
16753
16754     // private
16755     restrictHeight : function(){
16756         //this.innerList.dom.style.height = '';
16757         //var inner = this.innerList.dom;
16758         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16759         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16760         //this.list.beginUpdate();
16761         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16762         this.list.alignTo(this.inputEl(), this.listAlign);
16763         this.list.alignTo(this.inputEl(), this.listAlign);
16764         //this.list.endUpdate();
16765     },
16766
16767     // private
16768     onEmptyResults : function(){
16769         
16770         if(this.tickable && this.editable){
16771             this.hasFocus = false;
16772             this.restrictHeight();
16773             return;
16774         }
16775         
16776         this.collapse();
16777     },
16778
16779     /**
16780      * Returns true if the dropdown list is expanded, else false.
16781      */
16782     isExpanded : function(){
16783         return this.list.isVisible();
16784     },
16785
16786     /**
16787      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16788      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16789      * @param {String} value The data value of the item to select
16790      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16791      * selected item if it is not currently in view (defaults to true)
16792      * @return {Boolean} True if the value matched an item in the list, else false
16793      */
16794     selectByValue : function(v, scrollIntoView){
16795         if(v !== undefined && v !== null){
16796             var r = this.findRecord(this.valueField || this.displayField, v);
16797             if(r){
16798                 this.select(this.store.indexOf(r), scrollIntoView);
16799                 return true;
16800             }
16801         }
16802         return false;
16803     },
16804
16805     /**
16806      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16807      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16808      * @param {Number} index The zero-based index of the list item to select
16809      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16810      * selected item if it is not currently in view (defaults to true)
16811      */
16812     select : function(index, scrollIntoView){
16813         this.selectedIndex = index;
16814         this.view.select(index);
16815         if(scrollIntoView !== false){
16816             var el = this.view.getNode(index);
16817             /*
16818              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16819              */
16820             if(el){
16821                 this.list.scrollChildIntoView(el, false);
16822             }
16823         }
16824     },
16825
16826     // private
16827     selectNext : function(){
16828         var ct = this.store.getCount();
16829         if(ct > 0){
16830             if(this.selectedIndex == -1){
16831                 this.select(0);
16832             }else if(this.selectedIndex < ct-1){
16833                 this.select(this.selectedIndex+1);
16834             }
16835         }
16836     },
16837
16838     // private
16839     selectPrev : function(){
16840         var ct = this.store.getCount();
16841         if(ct > 0){
16842             if(this.selectedIndex == -1){
16843                 this.select(0);
16844             }else if(this.selectedIndex != 0){
16845                 this.select(this.selectedIndex-1);
16846             }
16847         }
16848     },
16849
16850     // private
16851     onKeyUp : function(e){
16852         if(this.editable !== false && !e.isSpecialKey()){
16853             this.lastKey = e.getKey();
16854             this.dqTask.delay(this.queryDelay);
16855         }
16856     },
16857
16858     // private
16859     validateBlur : function(){
16860         return !this.list || !this.list.isVisible();   
16861     },
16862
16863     // private
16864     initQuery : function(){
16865         
16866         var v = this.getRawValue();
16867         
16868         if(this.tickable && this.editable){
16869             v = this.tickableInputEl().getValue();
16870         }
16871         
16872         this.doQuery(v);
16873     },
16874
16875     // private
16876     doForce : function(){
16877         if(this.inputEl().dom.value.length > 0){
16878             this.inputEl().dom.value =
16879                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16880              
16881         }
16882     },
16883
16884     /**
16885      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16886      * query allowing the query action to be canceled if needed.
16887      * @param {String} query The SQL query to execute
16888      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16889      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16890      * saved in the current store (defaults to false)
16891      */
16892     doQuery : function(q, forceAll){
16893         
16894         if(q === undefined || q === null){
16895             q = '';
16896         }
16897         var qe = {
16898             query: q,
16899             forceAll: forceAll,
16900             combo: this,
16901             cancel:false
16902         };
16903         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16904             return false;
16905         }
16906         q = qe.query;
16907         
16908         forceAll = qe.forceAll;
16909         if(forceAll === true || (q.length >= this.minChars)){
16910             
16911             this.hasQuery = true;
16912             
16913             if(this.lastQuery != q || this.alwaysQuery){
16914                 this.lastQuery = q;
16915                 if(this.mode == 'local'){
16916                     this.selectedIndex = -1;
16917                     if(forceAll){
16918                         this.store.clearFilter();
16919                     }else{
16920                         
16921                         if(this.specialFilter){
16922                             this.fireEvent('specialfilter', this);
16923                             this.onLoad();
16924                             return;
16925                         }
16926                         
16927                         this.store.filter(this.displayField, q);
16928                     }
16929                     
16930                     this.store.fireEvent("datachanged", this.store);
16931                     
16932                     this.onLoad();
16933                     
16934                     
16935                 }else{
16936                     
16937                     this.store.baseParams[this.queryParam] = q;
16938                     
16939                     var options = {params : this.getParams(q)};
16940                     
16941                     if(this.loadNext){
16942                         options.add = true;
16943                         options.params.start = this.page * this.pageSize;
16944                     }
16945                     
16946                     this.store.load(options);
16947                     
16948                     /*
16949                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16950                      *  we should expand the list on onLoad
16951                      *  so command out it
16952                      */
16953 //                    this.expand();
16954                 }
16955             }else{
16956                 this.selectedIndex = -1;
16957                 this.onLoad();   
16958             }
16959         }
16960         
16961         this.loadNext = false;
16962     },
16963     
16964     // private
16965     getParams : function(q){
16966         var p = {};
16967         //p[this.queryParam] = q;
16968         
16969         if(this.pageSize){
16970             p.start = 0;
16971             p.limit = this.pageSize;
16972         }
16973         return p;
16974     },
16975
16976     /**
16977      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16978      */
16979     collapse : function(){
16980         if(!this.isExpanded()){
16981             return;
16982         }
16983         
16984         this.list.hide();
16985         
16986         this.hasFocus = false;
16987         
16988         if(this.tickable){
16989             this.okBtn.hide();
16990             this.cancelBtn.hide();
16991             this.trigger.show();
16992             
16993             if(this.editable){
16994                 this.tickableInputEl().dom.value = '';
16995                 this.tickableInputEl().blur();
16996             }
16997             
16998         }
16999         
17000         Roo.get(document).un('mousedown', this.collapseIf, this);
17001         Roo.get(document).un('mousewheel', this.collapseIf, this);
17002         if (!this.editable) {
17003             Roo.get(document).un('keydown', this.listKeyPress, this);
17004         }
17005         this.fireEvent('collapse', this);
17006         
17007         this.validate();
17008     },
17009
17010     // private
17011     collapseIf : function(e){
17012         var in_combo  = e.within(this.el);
17013         var in_list =  e.within(this.list);
17014         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17015         
17016         if (in_combo || in_list || is_list) {
17017             //e.stopPropagation();
17018             return;
17019         }
17020         
17021         if(this.tickable){
17022             this.onTickableFooterButtonClick(e, false, false);
17023         }
17024
17025         this.collapse();
17026         
17027     },
17028
17029     /**
17030      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17031      */
17032     expand : function(){
17033        
17034         if(this.isExpanded() || !this.hasFocus){
17035             return;
17036         }
17037         
17038         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17039         this.list.setWidth(lw);
17040         
17041         Roo.log('expand');
17042         
17043         this.list.show();
17044         
17045         this.restrictHeight();
17046         
17047         if(this.tickable){
17048             
17049             this.tickItems = Roo.apply([], this.item);
17050             
17051             this.okBtn.show();
17052             this.cancelBtn.show();
17053             this.trigger.hide();
17054             
17055             if(this.editable){
17056                 this.tickableInputEl().focus();
17057             }
17058             
17059         }
17060         
17061         Roo.get(document).on('mousedown', this.collapseIf, this);
17062         Roo.get(document).on('mousewheel', this.collapseIf, this);
17063         if (!this.editable) {
17064             Roo.get(document).on('keydown', this.listKeyPress, this);
17065         }
17066         
17067         this.fireEvent('expand', this);
17068     },
17069
17070     // private
17071     // Implements the default empty TriggerField.onTriggerClick function
17072     onTriggerClick : function(e)
17073     {
17074         Roo.log('trigger click');
17075         
17076         if(this.disabled || !this.triggerList){
17077             return;
17078         }
17079         
17080         this.page = 0;
17081         this.loadNext = false;
17082         
17083         if(this.isExpanded()){
17084             this.collapse();
17085             if (!this.blockFocus) {
17086                 this.inputEl().focus();
17087             }
17088             
17089         }else {
17090             this.hasFocus = true;
17091             if(this.triggerAction == 'all') {
17092                 this.doQuery(this.allQuery, true);
17093             } else {
17094                 this.doQuery(this.getRawValue());
17095             }
17096             if (!this.blockFocus) {
17097                 this.inputEl().focus();
17098             }
17099         }
17100     },
17101     
17102     onTickableTriggerClick : function(e)
17103     {
17104         if(this.disabled){
17105             return;
17106         }
17107         
17108         this.page = 0;
17109         this.loadNext = false;
17110         this.hasFocus = true;
17111         
17112         if(this.triggerAction == 'all') {
17113             this.doQuery(this.allQuery, true);
17114         } else {
17115             this.doQuery(this.getRawValue());
17116         }
17117     },
17118     
17119     onSearchFieldClick : function(e)
17120     {
17121         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17122             this.onTickableFooterButtonClick(e, false, false);
17123             return;
17124         }
17125         
17126         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17127             return;
17128         }
17129         
17130         this.page = 0;
17131         this.loadNext = false;
17132         this.hasFocus = true;
17133         
17134         if(this.triggerAction == 'all') {
17135             this.doQuery(this.allQuery, true);
17136         } else {
17137             this.doQuery(this.getRawValue());
17138         }
17139     },
17140     
17141     listKeyPress : function(e)
17142     {
17143         //Roo.log('listkeypress');
17144         // scroll to first matching element based on key pres..
17145         if (e.isSpecialKey()) {
17146             return false;
17147         }
17148         var k = String.fromCharCode(e.getKey()).toUpperCase();
17149         //Roo.log(k);
17150         var match  = false;
17151         var csel = this.view.getSelectedNodes();
17152         var cselitem = false;
17153         if (csel.length) {
17154             var ix = this.view.indexOf(csel[0]);
17155             cselitem  = this.store.getAt(ix);
17156             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17157                 cselitem = false;
17158             }
17159             
17160         }
17161         
17162         this.store.each(function(v) { 
17163             if (cselitem) {
17164                 // start at existing selection.
17165                 if (cselitem.id == v.id) {
17166                     cselitem = false;
17167                 }
17168                 return true;
17169             }
17170                 
17171             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17172                 match = this.store.indexOf(v);
17173                 return false;
17174             }
17175             return true;
17176         }, this);
17177         
17178         if (match === false) {
17179             return true; // no more action?
17180         }
17181         // scroll to?
17182         this.view.select(match);
17183         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17184         sn.scrollIntoView(sn.dom.parentNode, false);
17185     },
17186     
17187     onViewScroll : function(e, t){
17188         
17189         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){
17190             return;
17191         }
17192         
17193         this.hasQuery = true;
17194         
17195         this.loading = this.list.select('.loading', true).first();
17196         
17197         if(this.loading === null){
17198             this.list.createChild({
17199                 tag: 'div',
17200                 cls: 'loading roo-select2-more-results roo-select2-active',
17201                 html: 'Loading more results...'
17202             });
17203             
17204             this.loading = this.list.select('.loading', true).first();
17205             
17206             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17207             
17208             this.loading.hide();
17209         }
17210         
17211         this.loading.show();
17212         
17213         var _combo = this;
17214         
17215         this.page++;
17216         this.loadNext = true;
17217         
17218         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17219         
17220         return;
17221     },
17222     
17223     addItem : function(o)
17224     {   
17225         var dv = ''; // display value
17226         
17227         if (this.displayField) {
17228             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17229         } else {
17230             // this is an error condition!!!
17231             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17232         }
17233         
17234         if(!dv.length){
17235             return;
17236         }
17237         
17238         var choice = this.choices.createChild({
17239             tag: 'li',
17240             cls: 'roo-select2-search-choice',
17241             cn: [
17242                 {
17243                     tag: 'div',
17244                     html: dv
17245                 },
17246                 {
17247                     tag: 'a',
17248                     href: '#',
17249                     cls: 'roo-select2-search-choice-close fa fa-times',
17250                     tabindex: '-1'
17251                 }
17252             ]
17253             
17254         }, this.searchField);
17255         
17256         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17257         
17258         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17259         
17260         this.item.push(o);
17261         
17262         this.lastData = o;
17263         
17264         this.syncValue();
17265         
17266         this.inputEl().dom.value = '';
17267         
17268         this.validate();
17269     },
17270     
17271     onRemoveItem : function(e, _self, o)
17272     {
17273         e.preventDefault();
17274         
17275         this.lastItem = Roo.apply([], this.item);
17276         
17277         var index = this.item.indexOf(o.data) * 1;
17278         
17279         if( index < 0){
17280             Roo.log('not this item?!');
17281             return;
17282         }
17283         
17284         this.item.splice(index, 1);
17285         o.item.remove();
17286         
17287         this.syncValue();
17288         
17289         this.fireEvent('remove', this, e);
17290         
17291         this.validate();
17292         
17293     },
17294     
17295     syncValue : function()
17296     {
17297         if(!this.item.length){
17298             this.clearValue();
17299             return;
17300         }
17301             
17302         var value = [];
17303         var _this = this;
17304         Roo.each(this.item, function(i){
17305             if(_this.valueField){
17306                 value.push(i[_this.valueField]);
17307                 return;
17308             }
17309
17310             value.push(i);
17311         });
17312
17313         this.value = value.join(',');
17314
17315         if(this.hiddenField){
17316             this.hiddenField.dom.value = this.value;
17317         }
17318         
17319         this.store.fireEvent("datachanged", this.store);
17320         
17321         this.validate();
17322     },
17323     
17324     clearItem : function()
17325     {
17326         if(!this.multiple){
17327             return;
17328         }
17329         
17330         this.item = [];
17331         
17332         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17333            c.remove();
17334         });
17335         
17336         this.syncValue();
17337         
17338         this.validate();
17339         
17340         if(this.tickable && !Roo.isTouch){
17341             this.view.refresh();
17342         }
17343     },
17344     
17345     inputEl: function ()
17346     {
17347         if(Roo.isIOS && this.useNativeIOS){
17348             return this.el.select('select.roo-ios-select', true).first();
17349         }
17350         
17351         if(Roo.isTouch && this.mobileTouchView){
17352             return this.el.select('input.form-control',true).first();
17353         }
17354         
17355         if(this.tickable){
17356             return this.searchField;
17357         }
17358         
17359         return this.el.select('input.form-control',true).first();
17360     },
17361     
17362     onTickableFooterButtonClick : function(e, btn, el)
17363     {
17364         e.preventDefault();
17365         
17366         this.lastItem = Roo.apply([], this.item);
17367         
17368         if(btn && btn.name == 'cancel'){
17369             this.tickItems = Roo.apply([], this.item);
17370             this.collapse();
17371             return;
17372         }
17373         
17374         this.clearItem();
17375         
17376         var _this = this;
17377         
17378         Roo.each(this.tickItems, function(o){
17379             _this.addItem(o);
17380         });
17381         
17382         this.collapse();
17383         
17384     },
17385     
17386     validate : function()
17387     {
17388         if(this.getVisibilityEl().hasClass('hidden')){
17389             return true;
17390         }
17391         
17392         var v = this.getRawValue();
17393         
17394         if(this.multiple){
17395             v = this.getValue();
17396         }
17397         
17398         if(this.disabled || this.allowBlank || v.length){
17399             this.markValid();
17400             return true;
17401         }
17402         
17403         this.markInvalid();
17404         return false;
17405     },
17406     
17407     tickableInputEl : function()
17408     {
17409         if(!this.tickable || !this.editable){
17410             return this.inputEl();
17411         }
17412         
17413         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17414     },
17415     
17416     
17417     getAutoCreateTouchView : function()
17418     {
17419         var id = Roo.id();
17420         
17421         var cfg = {
17422             cls: 'form-group' //input-group
17423         };
17424         
17425         var input =  {
17426             tag: 'input',
17427             id : id,
17428             type : this.inputType,
17429             cls : 'form-control x-combo-noedit',
17430             autocomplete: 'new-password',
17431             placeholder : this.placeholder || '',
17432             readonly : true
17433         };
17434         
17435         if (this.name) {
17436             input.name = this.name;
17437         }
17438         
17439         if (this.size) {
17440             input.cls += ' input-' + this.size;
17441         }
17442         
17443         if (this.disabled) {
17444             input.disabled = true;
17445         }
17446         
17447         var inputblock = {
17448             cls : 'roo-combobox-wrap',
17449             cn : [
17450                 input
17451             ]
17452         };
17453         
17454         if(this.before){
17455             inputblock.cls += ' input-group';
17456             
17457             inputblock.cn.unshift({
17458                 tag :'span',
17459                 cls : 'input-group-addon input-group-prepend input-group-text',
17460                 html : this.before
17461             });
17462         }
17463         
17464         if(this.removable && !this.multiple){
17465             inputblock.cls += ' roo-removable';
17466             
17467             inputblock.cn.push({
17468                 tag: 'button',
17469                 html : 'x',
17470                 cls : 'roo-combo-removable-btn close'
17471             });
17472         }
17473
17474         if(this.hasFeedback && !this.allowBlank){
17475             
17476             inputblock.cls += ' has-feedback';
17477             
17478             inputblock.cn.push({
17479                 tag: 'span',
17480                 cls: 'glyphicon form-control-feedback'
17481             });
17482             
17483         }
17484         
17485         if (this.after) {
17486             
17487             inputblock.cls += (this.before) ? '' : ' input-group';
17488             
17489             inputblock.cn.push({
17490                 tag :'span',
17491                 cls : 'input-group-addon input-group-append input-group-text',
17492                 html : this.after
17493             });
17494         }
17495
17496         
17497         var ibwrap = inputblock;
17498         
17499         if(this.multiple){
17500             ibwrap = {
17501                 tag: 'ul',
17502                 cls: 'roo-select2-choices',
17503                 cn:[
17504                     {
17505                         tag: 'li',
17506                         cls: 'roo-select2-search-field',
17507                         cn: [
17508
17509                             inputblock
17510                         ]
17511                     }
17512                 ]
17513             };
17514         
17515             
17516         }
17517         
17518         var combobox = {
17519             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17520             cn: [
17521                 {
17522                     tag: 'input',
17523                     type : 'hidden',
17524                     cls: 'form-hidden-field'
17525                 },
17526                 ibwrap
17527             ]
17528         };
17529         
17530         if(!this.multiple && this.showToggleBtn){
17531             
17532             var caret = {
17533                 cls: 'caret'
17534             };
17535             
17536             if (this.caret != false) {
17537                 caret = {
17538                      tag: 'i',
17539                      cls: 'fa fa-' + this.caret
17540                 };
17541                 
17542             }
17543             
17544             combobox.cn.push({
17545                 tag :'span',
17546                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17547                 cn : [
17548                     Roo.bootstrap.version == 3 ? caret : '',
17549                     {
17550                         tag: 'span',
17551                         cls: 'combobox-clear',
17552                         cn  : [
17553                             {
17554                                 tag : 'i',
17555                                 cls: 'icon-remove'
17556                             }
17557                         ]
17558                     }
17559                 ]
17560
17561             })
17562         }
17563         
17564         if(this.multiple){
17565             combobox.cls += ' roo-select2-container-multi';
17566         }
17567         
17568         var align = this.labelAlign || this.parentLabelAlign();
17569         
17570         if (align ==='left' && this.fieldLabel.length) {
17571
17572             cfg.cn = [
17573                 {
17574                    tag : 'i',
17575                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17576                    tooltip : 'This field is required'
17577                 },
17578                 {
17579                     tag: 'label',
17580                     cls : 'control-label col-form-label',
17581                     html : this.fieldLabel
17582
17583                 },
17584                 {
17585                     cls : 'roo-combobox-wrap ', 
17586                     cn: [
17587                         combobox
17588                     ]
17589                 }
17590             ];
17591             
17592             var labelCfg = cfg.cn[1];
17593             var contentCfg = cfg.cn[2];
17594             
17595
17596             if(this.indicatorpos == 'right'){
17597                 cfg.cn = [
17598                     {
17599                         tag: 'label',
17600                         'for' :  id,
17601                         cls : 'control-label col-form-label',
17602                         cn : [
17603                             {
17604                                 tag : 'span',
17605                                 html : this.fieldLabel
17606                             },
17607                             {
17608                                 tag : 'i',
17609                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17610                                 tooltip : 'This field is required'
17611                             }
17612                         ]
17613                     },
17614                     {
17615                         cls : "roo-combobox-wrap ",
17616                         cn: [
17617                             combobox
17618                         ]
17619                     }
17620
17621                 ];
17622                 
17623                 labelCfg = cfg.cn[0];
17624                 contentCfg = cfg.cn[1];
17625             }
17626             
17627            
17628             
17629             if(this.labelWidth > 12){
17630                 labelCfg.style = "width: " + this.labelWidth + 'px';
17631             }
17632            
17633             if(this.labelWidth < 13 && this.labelmd == 0){
17634                 this.labelmd = this.labelWidth;
17635             }
17636             
17637             if(this.labellg > 0){
17638                 labelCfg.cls += ' col-lg-' + this.labellg;
17639                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17640             }
17641             
17642             if(this.labelmd > 0){
17643                 labelCfg.cls += ' col-md-' + this.labelmd;
17644                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17645             }
17646             
17647             if(this.labelsm > 0){
17648                 labelCfg.cls += ' col-sm-' + this.labelsm;
17649                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17650             }
17651             
17652             if(this.labelxs > 0){
17653                 labelCfg.cls += ' col-xs-' + this.labelxs;
17654                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17655             }
17656                 
17657                 
17658         } else if ( this.fieldLabel.length) {
17659             cfg.cn = [
17660                 {
17661                    tag : 'i',
17662                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17663                    tooltip : 'This field is required'
17664                 },
17665                 {
17666                     tag: 'label',
17667                     cls : 'control-label',
17668                     html : this.fieldLabel
17669
17670                 },
17671                 {
17672                     cls : '', 
17673                     cn: [
17674                         combobox
17675                     ]
17676                 }
17677             ];
17678             
17679             if(this.indicatorpos == 'right'){
17680                 cfg.cn = [
17681                     {
17682                         tag: 'label',
17683                         cls : 'control-label',
17684                         html : this.fieldLabel,
17685                         cn : [
17686                             {
17687                                tag : 'i',
17688                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17689                                tooltip : 'This field is required'
17690                             }
17691                         ]
17692                     },
17693                     {
17694                         cls : '', 
17695                         cn: [
17696                             combobox
17697                         ]
17698                     }
17699                 ];
17700             }
17701         } else {
17702             cfg.cn = combobox;    
17703         }
17704         
17705         
17706         var settings = this;
17707         
17708         ['xs','sm','md','lg'].map(function(size){
17709             if (settings[size]) {
17710                 cfg.cls += ' col-' + size + '-' + settings[size];
17711             }
17712         });
17713         
17714         return cfg;
17715     },
17716     
17717     initTouchView : function()
17718     {
17719         this.renderTouchView();
17720         
17721         this.touchViewEl.on('scroll', function(){
17722             this.el.dom.scrollTop = 0;
17723         }, this);
17724         
17725         this.originalValue = this.getValue();
17726         
17727         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17728         
17729         this.inputEl().on("click", this.showTouchView, this);
17730         if (this.triggerEl) {
17731             this.triggerEl.on("click", this.showTouchView, this);
17732         }
17733         
17734         
17735         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17736         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17737         
17738         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17739         
17740         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17741         this.store.on('load', this.onTouchViewLoad, this);
17742         this.store.on('loadexception', this.onTouchViewLoadException, this);
17743         
17744         if(this.hiddenName){
17745             
17746             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17747             
17748             this.hiddenField.dom.value =
17749                 this.hiddenValue !== undefined ? this.hiddenValue :
17750                 this.value !== undefined ? this.value : '';
17751         
17752             this.el.dom.removeAttribute('name');
17753             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17754         }
17755         
17756         if(this.multiple){
17757             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17758             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17759         }
17760         
17761         if(this.removable && !this.multiple){
17762             var close = this.closeTriggerEl();
17763             if(close){
17764                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17765                 close.on('click', this.removeBtnClick, this, close);
17766             }
17767         }
17768         /*
17769          * fix the bug in Safari iOS8
17770          */
17771         this.inputEl().on("focus", function(e){
17772             document.activeElement.blur();
17773         }, this);
17774         
17775         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17776         
17777         return;
17778         
17779         
17780     },
17781     
17782     renderTouchView : function()
17783     {
17784         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17785         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17786         
17787         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17788         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17789         
17790         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17791         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17792         this.touchViewBodyEl.setStyle('overflow', 'auto');
17793         
17794         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17795         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17796         
17797         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17798         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17799         
17800     },
17801     
17802     showTouchView : function()
17803     {
17804         if(this.disabled){
17805             return;
17806         }
17807         
17808         this.touchViewHeaderEl.hide();
17809
17810         if(this.modalTitle.length){
17811             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17812             this.touchViewHeaderEl.show();
17813         }
17814
17815         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17816         this.touchViewEl.show();
17817
17818         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17819         
17820         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17821         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17822
17823         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17824
17825         if(this.modalTitle.length){
17826             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17827         }
17828         
17829         this.touchViewBodyEl.setHeight(bodyHeight);
17830
17831         if(this.animate){
17832             var _this = this;
17833             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17834         }else{
17835             this.touchViewEl.addClass(['in','show']);
17836         }
17837         
17838         if(this._touchViewMask){
17839             Roo.get(document.body).addClass("x-body-masked");
17840             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17841             this._touchViewMask.setStyle('z-index', 10000);
17842             this._touchViewMask.addClass('show');
17843         }
17844         
17845         this.doTouchViewQuery();
17846         
17847     },
17848     
17849     hideTouchView : function()
17850     {
17851         this.touchViewEl.removeClass(['in','show']);
17852
17853         if(this.animate){
17854             var _this = this;
17855             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17856         }else{
17857             this.touchViewEl.setStyle('display', 'none');
17858         }
17859         
17860         if(this._touchViewMask){
17861             this._touchViewMask.removeClass('show');
17862             Roo.get(document.body).removeClass("x-body-masked");
17863         }
17864     },
17865     
17866     setTouchViewValue : function()
17867     {
17868         if(this.multiple){
17869             this.clearItem();
17870         
17871             var _this = this;
17872
17873             Roo.each(this.tickItems, function(o){
17874                 this.addItem(o);
17875             }, this);
17876         }
17877         
17878         this.hideTouchView();
17879     },
17880     
17881     doTouchViewQuery : function()
17882     {
17883         var qe = {
17884             query: '',
17885             forceAll: true,
17886             combo: this,
17887             cancel:false
17888         };
17889         
17890         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17891             return false;
17892         }
17893         
17894         if(!this.alwaysQuery || this.mode == 'local'){
17895             this.onTouchViewLoad();
17896             return;
17897         }
17898         
17899         this.store.load();
17900     },
17901     
17902     onTouchViewBeforeLoad : function(combo,opts)
17903     {
17904         return;
17905     },
17906
17907     // private
17908     onTouchViewLoad : function()
17909     {
17910         if(this.store.getCount() < 1){
17911             this.onTouchViewEmptyResults();
17912             return;
17913         }
17914         
17915         this.clearTouchView();
17916         
17917         var rawValue = this.getRawValue();
17918         
17919         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17920         
17921         this.tickItems = [];
17922         
17923         this.store.data.each(function(d, rowIndex){
17924             var row = this.touchViewListGroup.createChild(template);
17925             
17926             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17927                 row.addClass(d.data.cls);
17928             }
17929             
17930             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17931                 var cfg = {
17932                     data : d.data,
17933                     html : d.data[this.displayField]
17934                 };
17935                 
17936                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17937                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17938                 }
17939             }
17940             row.removeClass('selected');
17941             if(!this.multiple && this.valueField &&
17942                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17943             {
17944                 // radio buttons..
17945                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17946                 row.addClass('selected');
17947             }
17948             
17949             if(this.multiple && this.valueField &&
17950                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17951             {
17952                 
17953                 // checkboxes...
17954                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17955                 this.tickItems.push(d.data);
17956             }
17957             
17958             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17959             
17960         }, this);
17961         
17962         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17963         
17964         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17965
17966         if(this.modalTitle.length){
17967             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17968         }
17969
17970         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17971         
17972         if(this.mobile_restrict_height && listHeight < bodyHeight){
17973             this.touchViewBodyEl.setHeight(listHeight);
17974         }
17975         
17976         var _this = this;
17977         
17978         if(firstChecked && listHeight > bodyHeight){
17979             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17980         }
17981         
17982     },
17983     
17984     onTouchViewLoadException : function()
17985     {
17986         this.hideTouchView();
17987     },
17988     
17989     onTouchViewEmptyResults : function()
17990     {
17991         this.clearTouchView();
17992         
17993         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17994         
17995         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17996         
17997     },
17998     
17999     clearTouchView : function()
18000     {
18001         this.touchViewListGroup.dom.innerHTML = '';
18002     },
18003     
18004     onTouchViewClick : function(e, el, o)
18005     {
18006         e.preventDefault();
18007         
18008         var row = o.row;
18009         var rowIndex = o.rowIndex;
18010         
18011         var r = this.store.getAt(rowIndex);
18012         
18013         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18014             
18015             if(!this.multiple){
18016                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18017                     c.dom.removeAttribute('checked');
18018                 }, this);
18019
18020                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18021
18022                 this.setFromData(r.data);
18023
18024                 var close = this.closeTriggerEl();
18025
18026                 if(close){
18027                     close.show();
18028                 }
18029
18030                 this.hideTouchView();
18031
18032                 this.fireEvent('select', this, r, rowIndex);
18033
18034                 return;
18035             }
18036
18037             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18038                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18039                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18040                 return;
18041             }
18042
18043             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18044             this.addItem(r.data);
18045             this.tickItems.push(r.data);
18046         }
18047     },
18048     
18049     getAutoCreateNativeIOS : function()
18050     {
18051         var cfg = {
18052             cls: 'form-group' //input-group,
18053         };
18054         
18055         var combobox =  {
18056             tag: 'select',
18057             cls : 'roo-ios-select'
18058         };
18059         
18060         if (this.name) {
18061             combobox.name = this.name;
18062         }
18063         
18064         if (this.disabled) {
18065             combobox.disabled = true;
18066         }
18067         
18068         var settings = this;
18069         
18070         ['xs','sm','md','lg'].map(function(size){
18071             if (settings[size]) {
18072                 cfg.cls += ' col-' + size + '-' + settings[size];
18073             }
18074         });
18075         
18076         cfg.cn = combobox;
18077         
18078         return cfg;
18079         
18080     },
18081     
18082     initIOSView : function()
18083     {
18084         this.store.on('load', this.onIOSViewLoad, this);
18085         
18086         return;
18087     },
18088     
18089     onIOSViewLoad : function()
18090     {
18091         if(this.store.getCount() < 1){
18092             return;
18093         }
18094         
18095         this.clearIOSView();
18096         
18097         if(this.allowBlank) {
18098             
18099             var default_text = '-- SELECT --';
18100             
18101             if(this.placeholder.length){
18102                 default_text = this.placeholder;
18103             }
18104             
18105             if(this.emptyTitle.length){
18106                 default_text += ' - ' + this.emptyTitle + ' -';
18107             }
18108             
18109             var opt = this.inputEl().createChild({
18110                 tag: 'option',
18111                 value : 0,
18112                 html : default_text
18113             });
18114             
18115             var o = {};
18116             o[this.valueField] = 0;
18117             o[this.displayField] = default_text;
18118             
18119             this.ios_options.push({
18120                 data : o,
18121                 el : opt
18122             });
18123             
18124         }
18125         
18126         this.store.data.each(function(d, rowIndex){
18127             
18128             var html = '';
18129             
18130             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18131                 html = d.data[this.displayField];
18132             }
18133             
18134             var value = '';
18135             
18136             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18137                 value = d.data[this.valueField];
18138             }
18139             
18140             var option = {
18141                 tag: 'option',
18142                 value : value,
18143                 html : html
18144             };
18145             
18146             if(this.value == d.data[this.valueField]){
18147                 option['selected'] = true;
18148             }
18149             
18150             var opt = this.inputEl().createChild(option);
18151             
18152             this.ios_options.push({
18153                 data : d.data,
18154                 el : opt
18155             });
18156             
18157         }, this);
18158         
18159         this.inputEl().on('change', function(){
18160            this.fireEvent('select', this);
18161         }, this);
18162         
18163     },
18164     
18165     clearIOSView: function()
18166     {
18167         this.inputEl().dom.innerHTML = '';
18168         
18169         this.ios_options = [];
18170     },
18171     
18172     setIOSValue: function(v)
18173     {
18174         this.value = v;
18175         
18176         if(!this.ios_options){
18177             return;
18178         }
18179         
18180         Roo.each(this.ios_options, function(opts){
18181            
18182            opts.el.dom.removeAttribute('selected');
18183            
18184            if(opts.data[this.valueField] != v){
18185                return;
18186            }
18187            
18188            opts.el.dom.setAttribute('selected', true);
18189            
18190         }, this);
18191     }
18192
18193     /** 
18194     * @cfg {Boolean} grow 
18195     * @hide 
18196     */
18197     /** 
18198     * @cfg {Number} growMin 
18199     * @hide 
18200     */
18201     /** 
18202     * @cfg {Number} growMax 
18203     * @hide 
18204     */
18205     /**
18206      * @hide
18207      * @method autoSize
18208      */
18209 });
18210
18211 Roo.apply(Roo.bootstrap.ComboBox,  {
18212     
18213     header : {
18214         tag: 'div',
18215         cls: 'modal-header',
18216         cn: [
18217             {
18218                 tag: 'h4',
18219                 cls: 'modal-title'
18220             }
18221         ]
18222     },
18223     
18224     body : {
18225         tag: 'div',
18226         cls: 'modal-body',
18227         cn: [
18228             {
18229                 tag: 'ul',
18230                 cls: 'list-group'
18231             }
18232         ]
18233     },
18234     
18235     listItemRadio : {
18236         tag: 'li',
18237         cls: 'list-group-item',
18238         cn: [
18239             {
18240                 tag: 'span',
18241                 cls: 'roo-combobox-list-group-item-value'
18242             },
18243             {
18244                 tag: 'div',
18245                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18246                 cn: [
18247                     {
18248                         tag: 'input',
18249                         type: 'radio'
18250                     },
18251                     {
18252                         tag: 'label'
18253                     }
18254                 ]
18255             }
18256         ]
18257     },
18258     
18259     listItemCheckbox : {
18260         tag: 'li',
18261         cls: 'list-group-item',
18262         cn: [
18263             {
18264                 tag: 'span',
18265                 cls: 'roo-combobox-list-group-item-value'
18266             },
18267             {
18268                 tag: 'div',
18269                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18270                 cn: [
18271                     {
18272                         tag: 'input',
18273                         type: 'checkbox'
18274                     },
18275                     {
18276                         tag: 'label'
18277                     }
18278                 ]
18279             }
18280         ]
18281     },
18282     
18283     emptyResult : {
18284         tag: 'div',
18285         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18286     },
18287     
18288     footer : {
18289         tag: 'div',
18290         cls: 'modal-footer',
18291         cn: [
18292             {
18293                 tag: 'div',
18294                 cls: 'row',
18295                 cn: [
18296                     {
18297                         tag: 'div',
18298                         cls: 'col-xs-6 text-left',
18299                         cn: {
18300                             tag: 'button',
18301                             cls: 'btn btn-danger roo-touch-view-cancel',
18302                             html: 'Cancel'
18303                         }
18304                     },
18305                     {
18306                         tag: 'div',
18307                         cls: 'col-xs-6 text-right',
18308                         cn: {
18309                             tag: 'button',
18310                             cls: 'btn btn-success roo-touch-view-ok',
18311                             html: 'OK'
18312                         }
18313                     }
18314                 ]
18315             }
18316         ]
18317         
18318     }
18319 });
18320
18321 Roo.apply(Roo.bootstrap.ComboBox,  {
18322     
18323     touchViewTemplate : {
18324         tag: 'div',
18325         cls: 'modal fade roo-combobox-touch-view',
18326         cn: [
18327             {
18328                 tag: 'div',
18329                 cls: 'modal-dialog',
18330                 style : 'position:fixed', // we have to fix position....
18331                 cn: [
18332                     {
18333                         tag: 'div',
18334                         cls: 'modal-content',
18335                         cn: [
18336                             Roo.bootstrap.ComboBox.header,
18337                             Roo.bootstrap.ComboBox.body,
18338                             Roo.bootstrap.ComboBox.footer
18339                         ]
18340                     }
18341                 ]
18342             }
18343         ]
18344     }
18345 });/*
18346  * Based on:
18347  * Ext JS Library 1.1.1
18348  * Copyright(c) 2006-2007, Ext JS, LLC.
18349  *
18350  * Originally Released Under LGPL - original licence link has changed is not relivant.
18351  *
18352  * Fork - LGPL
18353  * <script type="text/javascript">
18354  */
18355
18356 /**
18357  * @class Roo.View
18358  * @extends Roo.util.Observable
18359  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18360  * This class also supports single and multi selection modes. <br>
18361  * Create a data model bound view:
18362  <pre><code>
18363  var store = new Roo.data.Store(...);
18364
18365  var view = new Roo.View({
18366     el : "my-element",
18367     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18368  
18369     singleSelect: true,
18370     selectedClass: "ydataview-selected",
18371     store: store
18372  });
18373
18374  // listen for node click?
18375  view.on("click", function(vw, index, node, e){
18376  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18377  });
18378
18379  // load XML data
18380  dataModel.load("foobar.xml");
18381  </code></pre>
18382  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18383  * <br><br>
18384  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18385  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18386  * 
18387  * Note: old style constructor is still suported (container, template, config)
18388  * 
18389  * @constructor
18390  * Create a new View
18391  * @param {Object} config The config object
18392  * 
18393  */
18394 Roo.View = function(config, depreciated_tpl, depreciated_config){
18395     
18396     this.parent = false;
18397     
18398     if (typeof(depreciated_tpl) == 'undefined') {
18399         // new way.. - universal constructor.
18400         Roo.apply(this, config);
18401         this.el  = Roo.get(this.el);
18402     } else {
18403         // old format..
18404         this.el  = Roo.get(config);
18405         this.tpl = depreciated_tpl;
18406         Roo.apply(this, depreciated_config);
18407     }
18408     this.wrapEl  = this.el.wrap().wrap();
18409     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18410     
18411     
18412     if(typeof(this.tpl) == "string"){
18413         this.tpl = new Roo.Template(this.tpl);
18414     } else {
18415         // support xtype ctors..
18416         this.tpl = new Roo.factory(this.tpl, Roo);
18417     }
18418     
18419     
18420     this.tpl.compile();
18421     
18422     /** @private */
18423     this.addEvents({
18424         /**
18425          * @event beforeclick
18426          * Fires before a click is processed. Returns false to cancel the default action.
18427          * @param {Roo.View} this
18428          * @param {Number} index The index of the target node
18429          * @param {HTMLElement} node The target node
18430          * @param {Roo.EventObject} e The raw event object
18431          */
18432             "beforeclick" : true,
18433         /**
18434          * @event click
18435          * Fires when a template node is clicked.
18436          * @param {Roo.View} this
18437          * @param {Number} index The index of the target node
18438          * @param {HTMLElement} node The target node
18439          * @param {Roo.EventObject} e The raw event object
18440          */
18441             "click" : true,
18442         /**
18443          * @event dblclick
18444          * Fires when a template node is double clicked.
18445          * @param {Roo.View} this
18446          * @param {Number} index The index of the target node
18447          * @param {HTMLElement} node The target node
18448          * @param {Roo.EventObject} e The raw event object
18449          */
18450             "dblclick" : true,
18451         /**
18452          * @event contextmenu
18453          * Fires when a template node is right clicked.
18454          * @param {Roo.View} this
18455          * @param {Number} index The index of the target node
18456          * @param {HTMLElement} node The target node
18457          * @param {Roo.EventObject} e The raw event object
18458          */
18459             "contextmenu" : true,
18460         /**
18461          * @event selectionchange
18462          * Fires when the selected nodes change.
18463          * @param {Roo.View} this
18464          * @param {Array} selections Array of the selected nodes
18465          */
18466             "selectionchange" : true,
18467     
18468         /**
18469          * @event beforeselect
18470          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18471          * @param {Roo.View} this
18472          * @param {HTMLElement} node The node to be selected
18473          * @param {Array} selections Array of currently selected nodes
18474          */
18475             "beforeselect" : true,
18476         /**
18477          * @event preparedata
18478          * Fires on every row to render, to allow you to change the data.
18479          * @param {Roo.View} this
18480          * @param {Object} data to be rendered (change this)
18481          */
18482           "preparedata" : true
18483           
18484           
18485         });
18486
18487
18488
18489     this.el.on({
18490         "click": this.onClick,
18491         "dblclick": this.onDblClick,
18492         "contextmenu": this.onContextMenu,
18493         scope:this
18494     });
18495
18496     this.selections = [];
18497     this.nodes = [];
18498     this.cmp = new Roo.CompositeElementLite([]);
18499     if(this.store){
18500         this.store = Roo.factory(this.store, Roo.data);
18501         this.setStore(this.store, true);
18502     }
18503     
18504     if ( this.footer && this.footer.xtype) {
18505            
18506          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18507         
18508         this.footer.dataSource = this.store;
18509         this.footer.container = fctr;
18510         this.footer = Roo.factory(this.footer, Roo);
18511         fctr.insertFirst(this.el);
18512         
18513         // this is a bit insane - as the paging toolbar seems to detach the el..
18514 //        dom.parentNode.parentNode.parentNode
18515          // they get detached?
18516     }
18517     
18518     
18519     Roo.View.superclass.constructor.call(this);
18520     
18521     
18522 };
18523
18524 Roo.extend(Roo.View, Roo.util.Observable, {
18525     
18526      /**
18527      * @cfg {Roo.data.Store} store Data store to load data from.
18528      */
18529     store : false,
18530     
18531     /**
18532      * @cfg {String|Roo.Element} el The container element.
18533      */
18534     el : '',
18535     
18536     /**
18537      * @cfg {String|Roo.Template} tpl The template used by this View 
18538      */
18539     tpl : false,
18540     /**
18541      * @cfg {String} dataName the named area of the template to use as the data area
18542      *                          Works with domtemplates roo-name="name"
18543      */
18544     dataName: false,
18545     /**
18546      * @cfg {String} selectedClass The css class to add to selected nodes
18547      */
18548     selectedClass : "x-view-selected",
18549      /**
18550      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18551      */
18552     emptyText : "",
18553     
18554     /**
18555      * @cfg {String} text to display on mask (default Loading)
18556      */
18557     mask : false,
18558     /**
18559      * @cfg {Boolean} multiSelect Allow multiple selection
18560      */
18561     multiSelect : false,
18562     /**
18563      * @cfg {Boolean} singleSelect Allow single selection
18564      */
18565     singleSelect:  false,
18566     
18567     /**
18568      * @cfg {Boolean} toggleSelect - selecting 
18569      */
18570     toggleSelect : false,
18571     
18572     /**
18573      * @cfg {Boolean} tickable - selecting 
18574      */
18575     tickable : false,
18576     
18577     /**
18578      * Returns the element this view is bound to.
18579      * @return {Roo.Element}
18580      */
18581     getEl : function(){
18582         return this.wrapEl;
18583     },
18584     
18585     
18586
18587     /**
18588      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18589      */
18590     refresh : function(){
18591         //Roo.log('refresh');
18592         var t = this.tpl;
18593         
18594         // if we are using something like 'domtemplate', then
18595         // the what gets used is:
18596         // t.applySubtemplate(NAME, data, wrapping data..)
18597         // the outer template then get' applied with
18598         //     the store 'extra data'
18599         // and the body get's added to the
18600         //      roo-name="data" node?
18601         //      <span class='roo-tpl-{name}'></span> ?????
18602         
18603         
18604         
18605         this.clearSelections();
18606         this.el.update("");
18607         var html = [];
18608         var records = this.store.getRange();
18609         if(records.length < 1) {
18610             
18611             // is this valid??  = should it render a template??
18612             
18613             this.el.update(this.emptyText);
18614             return;
18615         }
18616         var el = this.el;
18617         if (this.dataName) {
18618             this.el.update(t.apply(this.store.meta)); //????
18619             el = this.el.child('.roo-tpl-' + this.dataName);
18620         }
18621         
18622         for(var i = 0, len = records.length; i < len; i++){
18623             var data = this.prepareData(records[i].data, i, records[i]);
18624             this.fireEvent("preparedata", this, data, i, records[i]);
18625             
18626             var d = Roo.apply({}, data);
18627             
18628             if(this.tickable){
18629                 Roo.apply(d, {'roo-id' : Roo.id()});
18630                 
18631                 var _this = this;
18632             
18633                 Roo.each(this.parent.item, function(item){
18634                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18635                         return;
18636                     }
18637                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18638                 });
18639             }
18640             
18641             html[html.length] = Roo.util.Format.trim(
18642                 this.dataName ?
18643                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18644                     t.apply(d)
18645             );
18646         }
18647         
18648         
18649         
18650         el.update(html.join(""));
18651         this.nodes = el.dom.childNodes;
18652         this.updateIndexes(0);
18653     },
18654     
18655
18656     /**
18657      * Function to override to reformat the data that is sent to
18658      * the template for each node.
18659      * DEPRICATED - use the preparedata event handler.
18660      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18661      * a JSON object for an UpdateManager bound view).
18662      */
18663     prepareData : function(data, index, record)
18664     {
18665         this.fireEvent("preparedata", this, data, index, record);
18666         return data;
18667     },
18668
18669     onUpdate : function(ds, record){
18670         // Roo.log('on update');   
18671         this.clearSelections();
18672         var index = this.store.indexOf(record);
18673         var n = this.nodes[index];
18674         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18675         n.parentNode.removeChild(n);
18676         this.updateIndexes(index, index);
18677     },
18678
18679     
18680     
18681 // --------- FIXME     
18682     onAdd : function(ds, records, index)
18683     {
18684         //Roo.log(['on Add', ds, records, index] );        
18685         this.clearSelections();
18686         if(this.nodes.length == 0){
18687             this.refresh();
18688             return;
18689         }
18690         var n = this.nodes[index];
18691         for(var i = 0, len = records.length; i < len; i++){
18692             var d = this.prepareData(records[i].data, i, records[i]);
18693             if(n){
18694                 this.tpl.insertBefore(n, d);
18695             }else{
18696                 
18697                 this.tpl.append(this.el, d);
18698             }
18699         }
18700         this.updateIndexes(index);
18701     },
18702
18703     onRemove : function(ds, record, index){
18704        // Roo.log('onRemove');
18705         this.clearSelections();
18706         var el = this.dataName  ?
18707             this.el.child('.roo-tpl-' + this.dataName) :
18708             this.el; 
18709         
18710         el.dom.removeChild(this.nodes[index]);
18711         this.updateIndexes(index);
18712     },
18713
18714     /**
18715      * Refresh an individual node.
18716      * @param {Number} index
18717      */
18718     refreshNode : function(index){
18719         this.onUpdate(this.store, this.store.getAt(index));
18720     },
18721
18722     updateIndexes : function(startIndex, endIndex){
18723         var ns = this.nodes;
18724         startIndex = startIndex || 0;
18725         endIndex = endIndex || ns.length - 1;
18726         for(var i = startIndex; i <= endIndex; i++){
18727             ns[i].nodeIndex = i;
18728         }
18729     },
18730
18731     /**
18732      * Changes the data store this view uses and refresh the view.
18733      * @param {Store} store
18734      */
18735     setStore : function(store, initial){
18736         if(!initial && this.store){
18737             this.store.un("datachanged", this.refresh);
18738             this.store.un("add", this.onAdd);
18739             this.store.un("remove", this.onRemove);
18740             this.store.un("update", this.onUpdate);
18741             this.store.un("clear", this.refresh);
18742             this.store.un("beforeload", this.onBeforeLoad);
18743             this.store.un("load", this.onLoad);
18744             this.store.un("loadexception", this.onLoad);
18745         }
18746         if(store){
18747           
18748             store.on("datachanged", this.refresh, this);
18749             store.on("add", this.onAdd, this);
18750             store.on("remove", this.onRemove, this);
18751             store.on("update", this.onUpdate, this);
18752             store.on("clear", this.refresh, this);
18753             store.on("beforeload", this.onBeforeLoad, this);
18754             store.on("load", this.onLoad, this);
18755             store.on("loadexception", this.onLoad, this);
18756         }
18757         
18758         if(store){
18759             this.refresh();
18760         }
18761     },
18762     /**
18763      * onbeforeLoad - masks the loading area.
18764      *
18765      */
18766     onBeforeLoad : function(store,opts)
18767     {
18768          //Roo.log('onBeforeLoad');   
18769         if (!opts.add) {
18770             this.el.update("");
18771         }
18772         this.el.mask(this.mask ? this.mask : "Loading" ); 
18773     },
18774     onLoad : function ()
18775     {
18776         this.el.unmask();
18777     },
18778     
18779
18780     /**
18781      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18782      * @param {HTMLElement} node
18783      * @return {HTMLElement} The template node
18784      */
18785     findItemFromChild : function(node){
18786         var el = this.dataName  ?
18787             this.el.child('.roo-tpl-' + this.dataName,true) :
18788             this.el.dom; 
18789         
18790         if(!node || node.parentNode == el){
18791                     return node;
18792             }
18793             var p = node.parentNode;
18794             while(p && p != el){
18795             if(p.parentNode == el){
18796                 return p;
18797             }
18798             p = p.parentNode;
18799         }
18800             return null;
18801     },
18802
18803     /** @ignore */
18804     onClick : function(e){
18805         var item = this.findItemFromChild(e.getTarget());
18806         if(item){
18807             var index = this.indexOf(item);
18808             if(this.onItemClick(item, index, e) !== false){
18809                 this.fireEvent("click", this, index, item, e);
18810             }
18811         }else{
18812             this.clearSelections();
18813         }
18814     },
18815
18816     /** @ignore */
18817     onContextMenu : function(e){
18818         var item = this.findItemFromChild(e.getTarget());
18819         if(item){
18820             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18821         }
18822     },
18823
18824     /** @ignore */
18825     onDblClick : function(e){
18826         var item = this.findItemFromChild(e.getTarget());
18827         if(item){
18828             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18829         }
18830     },
18831
18832     onItemClick : function(item, index, e)
18833     {
18834         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18835             return false;
18836         }
18837         if (this.toggleSelect) {
18838             var m = this.isSelected(item) ? 'unselect' : 'select';
18839             //Roo.log(m);
18840             var _t = this;
18841             _t[m](item, true, false);
18842             return true;
18843         }
18844         if(this.multiSelect || this.singleSelect){
18845             if(this.multiSelect && e.shiftKey && this.lastSelection){
18846                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18847             }else{
18848                 this.select(item, this.multiSelect && e.ctrlKey);
18849                 this.lastSelection = item;
18850             }
18851             
18852             if(!this.tickable){
18853                 e.preventDefault();
18854             }
18855             
18856         }
18857         return true;
18858     },
18859
18860     /**
18861      * Get the number of selected nodes.
18862      * @return {Number}
18863      */
18864     getSelectionCount : function(){
18865         return this.selections.length;
18866     },
18867
18868     /**
18869      * Get the currently selected nodes.
18870      * @return {Array} An array of HTMLElements
18871      */
18872     getSelectedNodes : function(){
18873         return this.selections;
18874     },
18875
18876     /**
18877      * Get the indexes of the selected nodes.
18878      * @return {Array}
18879      */
18880     getSelectedIndexes : function(){
18881         var indexes = [], s = this.selections;
18882         for(var i = 0, len = s.length; i < len; i++){
18883             indexes.push(s[i].nodeIndex);
18884         }
18885         return indexes;
18886     },
18887
18888     /**
18889      * Clear all selections
18890      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18891      */
18892     clearSelections : function(suppressEvent){
18893         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18894             this.cmp.elements = this.selections;
18895             this.cmp.removeClass(this.selectedClass);
18896             this.selections = [];
18897             if(!suppressEvent){
18898                 this.fireEvent("selectionchange", this, this.selections);
18899             }
18900         }
18901     },
18902
18903     /**
18904      * Returns true if the passed node is selected
18905      * @param {HTMLElement/Number} node The node or node index
18906      * @return {Boolean}
18907      */
18908     isSelected : function(node){
18909         var s = this.selections;
18910         if(s.length < 1){
18911             return false;
18912         }
18913         node = this.getNode(node);
18914         return s.indexOf(node) !== -1;
18915     },
18916
18917     /**
18918      * Selects nodes.
18919      * @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
18920      * @param {Boolean} keepExisting (optional) true to keep existing selections
18921      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18922      */
18923     select : function(nodeInfo, keepExisting, suppressEvent){
18924         if(nodeInfo instanceof Array){
18925             if(!keepExisting){
18926                 this.clearSelections(true);
18927             }
18928             for(var i = 0, len = nodeInfo.length; i < len; i++){
18929                 this.select(nodeInfo[i], true, true);
18930             }
18931             return;
18932         } 
18933         var node = this.getNode(nodeInfo);
18934         if(!node || this.isSelected(node)){
18935             return; // already selected.
18936         }
18937         if(!keepExisting){
18938             this.clearSelections(true);
18939         }
18940         
18941         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18942             Roo.fly(node).addClass(this.selectedClass);
18943             this.selections.push(node);
18944             if(!suppressEvent){
18945                 this.fireEvent("selectionchange", this, this.selections);
18946             }
18947         }
18948         
18949         
18950     },
18951       /**
18952      * Unselects nodes.
18953      * @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
18954      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18955      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18956      */
18957     unselect : function(nodeInfo, keepExisting, suppressEvent)
18958     {
18959         if(nodeInfo instanceof Array){
18960             Roo.each(this.selections, function(s) {
18961                 this.unselect(s, nodeInfo);
18962             }, this);
18963             return;
18964         }
18965         var node = this.getNode(nodeInfo);
18966         if(!node || !this.isSelected(node)){
18967             //Roo.log("not selected");
18968             return; // not selected.
18969         }
18970         // fireevent???
18971         var ns = [];
18972         Roo.each(this.selections, function(s) {
18973             if (s == node ) {
18974                 Roo.fly(node).removeClass(this.selectedClass);
18975
18976                 return;
18977             }
18978             ns.push(s);
18979         },this);
18980         
18981         this.selections= ns;
18982         this.fireEvent("selectionchange", this, this.selections);
18983     },
18984
18985     /**
18986      * Gets a template node.
18987      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18988      * @return {HTMLElement} The node or null if it wasn't found
18989      */
18990     getNode : function(nodeInfo){
18991         if(typeof nodeInfo == "string"){
18992             return document.getElementById(nodeInfo);
18993         }else if(typeof nodeInfo == "number"){
18994             return this.nodes[nodeInfo];
18995         }
18996         return nodeInfo;
18997     },
18998
18999     /**
19000      * Gets a range template nodes.
19001      * @param {Number} startIndex
19002      * @param {Number} endIndex
19003      * @return {Array} An array of nodes
19004      */
19005     getNodes : function(start, end){
19006         var ns = this.nodes;
19007         start = start || 0;
19008         end = typeof end == "undefined" ? ns.length - 1 : end;
19009         var nodes = [];
19010         if(start <= end){
19011             for(var i = start; i <= end; i++){
19012                 nodes.push(ns[i]);
19013             }
19014         } else{
19015             for(var i = start; i >= end; i--){
19016                 nodes.push(ns[i]);
19017             }
19018         }
19019         return nodes;
19020     },
19021
19022     /**
19023      * Finds the index of the passed 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 {Number} The index of the node or -1
19026      */
19027     indexOf : function(node){
19028         node = this.getNode(node);
19029         if(typeof node.nodeIndex == "number"){
19030             return node.nodeIndex;
19031         }
19032         var ns = this.nodes;
19033         for(var i = 0, len = ns.length; i < len; i++){
19034             if(ns[i] == node){
19035                 return i;
19036             }
19037         }
19038         return -1;
19039     }
19040 });
19041 /*
19042  * - LGPL
19043  *
19044  * based on jquery fullcalendar
19045  * 
19046  */
19047
19048 Roo.bootstrap = Roo.bootstrap || {};
19049 /**
19050  * @class Roo.bootstrap.Calendar
19051  * @extends Roo.bootstrap.Component
19052  * Bootstrap Calendar class
19053  * @cfg {Boolean} loadMask (true|false) default false
19054  * @cfg {Object} header generate the user specific header of the calendar, default false
19055
19056  * @constructor
19057  * Create a new Container
19058  * @param {Object} config The config object
19059  */
19060
19061
19062
19063 Roo.bootstrap.Calendar = function(config){
19064     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19065      this.addEvents({
19066         /**
19067              * @event select
19068              * Fires when a date is selected
19069              * @param {DatePicker} this
19070              * @param {Date} date The selected date
19071              */
19072         'select': true,
19073         /**
19074              * @event monthchange
19075              * Fires when the displayed month changes 
19076              * @param {DatePicker} this
19077              * @param {Date} date The selected month
19078              */
19079         'monthchange': true,
19080         /**
19081              * @event evententer
19082              * Fires when mouse over an event
19083              * @param {Calendar} this
19084              * @param {event} Event
19085              */
19086         'evententer': true,
19087         /**
19088              * @event eventleave
19089              * Fires when the mouse leaves an
19090              * @param {Calendar} this
19091              * @param {event}
19092              */
19093         'eventleave': true,
19094         /**
19095              * @event eventclick
19096              * Fires when the mouse click an
19097              * @param {Calendar} this
19098              * @param {event}
19099              */
19100         'eventclick': true
19101         
19102     });
19103
19104 };
19105
19106 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19107     
19108      /**
19109      * @cfg {Number} startDay
19110      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19111      */
19112     startDay : 0,
19113     
19114     loadMask : false,
19115     
19116     header : false,
19117       
19118     getAutoCreate : function(){
19119         
19120         
19121         var fc_button = function(name, corner, style, content ) {
19122             return Roo.apply({},{
19123                 tag : 'span',
19124                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19125                          (corner.length ?
19126                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19127                             ''
19128                         ),
19129                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19130                 unselectable: 'on'
19131             });
19132         };
19133         
19134         var header = {};
19135         
19136         if(!this.header){
19137             header = {
19138                 tag : 'table',
19139                 cls : 'fc-header',
19140                 style : 'width:100%',
19141                 cn : [
19142                     {
19143                         tag: 'tr',
19144                         cn : [
19145                             {
19146                                 tag : 'td',
19147                                 cls : 'fc-header-left',
19148                                 cn : [
19149                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19150                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19151                                     { tag: 'span', cls: 'fc-header-space' },
19152                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19153
19154
19155                                 ]
19156                             },
19157
19158                             {
19159                                 tag : 'td',
19160                                 cls : 'fc-header-center',
19161                                 cn : [
19162                                     {
19163                                         tag: 'span',
19164                                         cls: 'fc-header-title',
19165                                         cn : {
19166                                             tag: 'H2',
19167                                             html : 'month / year'
19168                                         }
19169                                     }
19170
19171                                 ]
19172                             },
19173                             {
19174                                 tag : 'td',
19175                                 cls : 'fc-header-right',
19176                                 cn : [
19177                               /*      fc_button('month', 'left', '', 'month' ),
19178                                     fc_button('week', '', '', 'week' ),
19179                                     fc_button('day', 'right', '', 'day' )
19180                                 */    
19181
19182                                 ]
19183                             }
19184
19185                         ]
19186                     }
19187                 ]
19188             };
19189         }
19190         
19191         header = this.header;
19192         
19193        
19194         var cal_heads = function() {
19195             var ret = [];
19196             // fixme - handle this.
19197             
19198             for (var i =0; i < Date.dayNames.length; i++) {
19199                 var d = Date.dayNames[i];
19200                 ret.push({
19201                     tag: 'th',
19202                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19203                     html : d.substring(0,3)
19204                 });
19205                 
19206             }
19207             ret[0].cls += ' fc-first';
19208             ret[6].cls += ' fc-last';
19209             return ret;
19210         };
19211         var cal_cell = function(n) {
19212             return  {
19213                 tag: 'td',
19214                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19215                 cn : [
19216                     {
19217                         cn : [
19218                             {
19219                                 cls: 'fc-day-number',
19220                                 html: 'D'
19221                             },
19222                             {
19223                                 cls: 'fc-day-content',
19224                              
19225                                 cn : [
19226                                      {
19227                                         style: 'position: relative;' // height: 17px;
19228                                     }
19229                                 ]
19230                             }
19231                             
19232                             
19233                         ]
19234                     }
19235                 ]
19236                 
19237             }
19238         };
19239         var cal_rows = function() {
19240             
19241             var ret = [];
19242             for (var r = 0; r < 6; r++) {
19243                 var row= {
19244                     tag : 'tr',
19245                     cls : 'fc-week',
19246                     cn : []
19247                 };
19248                 
19249                 for (var i =0; i < Date.dayNames.length; i++) {
19250                     var d = Date.dayNames[i];
19251                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19252
19253                 }
19254                 row.cn[0].cls+=' fc-first';
19255                 row.cn[0].cn[0].style = 'min-height:90px';
19256                 row.cn[6].cls+=' fc-last';
19257                 ret.push(row);
19258                 
19259             }
19260             ret[0].cls += ' fc-first';
19261             ret[4].cls += ' fc-prev-last';
19262             ret[5].cls += ' fc-last';
19263             return ret;
19264             
19265         };
19266         
19267         var cal_table = {
19268             tag: 'table',
19269             cls: 'fc-border-separate',
19270             style : 'width:100%',
19271             cellspacing  : 0,
19272             cn : [
19273                 { 
19274                     tag: 'thead',
19275                     cn : [
19276                         { 
19277                             tag: 'tr',
19278                             cls : 'fc-first fc-last',
19279                             cn : cal_heads()
19280                         }
19281                     ]
19282                 },
19283                 { 
19284                     tag: 'tbody',
19285                     cn : cal_rows()
19286                 }
19287                   
19288             ]
19289         };
19290          
19291          var cfg = {
19292             cls : 'fc fc-ltr',
19293             cn : [
19294                 header,
19295                 {
19296                     cls : 'fc-content',
19297                     style : "position: relative;",
19298                     cn : [
19299                         {
19300                             cls : 'fc-view fc-view-month fc-grid',
19301                             style : 'position: relative',
19302                             unselectable : 'on',
19303                             cn : [
19304                                 {
19305                                     cls : 'fc-event-container',
19306                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19307                                 },
19308                                 cal_table
19309                             ]
19310                         }
19311                     ]
19312     
19313                 }
19314            ] 
19315             
19316         };
19317         
19318          
19319         
19320         return cfg;
19321     },
19322     
19323     
19324     initEvents : function()
19325     {
19326         if(!this.store){
19327             throw "can not find store for calendar";
19328         }
19329         
19330         var mark = {
19331             tag: "div",
19332             cls:"x-dlg-mask",
19333             style: "text-align:center",
19334             cn: [
19335                 {
19336                     tag: "div",
19337                     style: "background-color:white;width:50%;margin:250 auto",
19338                     cn: [
19339                         {
19340                             tag: "img",
19341                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19342                         },
19343                         {
19344                             tag: "span",
19345                             html: "Loading"
19346                         }
19347                         
19348                     ]
19349                 }
19350             ]
19351         };
19352         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19353         
19354         var size = this.el.select('.fc-content', true).first().getSize();
19355         this.maskEl.setSize(size.width, size.height);
19356         this.maskEl.enableDisplayMode("block");
19357         if(!this.loadMask){
19358             this.maskEl.hide();
19359         }
19360         
19361         this.store = Roo.factory(this.store, Roo.data);
19362         this.store.on('load', this.onLoad, this);
19363         this.store.on('beforeload', this.onBeforeLoad, this);
19364         
19365         this.resize();
19366         
19367         this.cells = this.el.select('.fc-day',true);
19368         //Roo.log(this.cells);
19369         this.textNodes = this.el.query('.fc-day-number');
19370         this.cells.addClassOnOver('fc-state-hover');
19371         
19372         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19373         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19374         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19375         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19376         
19377         this.on('monthchange', this.onMonthChange, this);
19378         
19379         this.update(new Date().clearTime());
19380     },
19381     
19382     resize : function() {
19383         var sz  = this.el.getSize();
19384         
19385         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19386         this.el.select('.fc-day-content div',true).setHeight(34);
19387     },
19388     
19389     
19390     // private
19391     showPrevMonth : function(e){
19392         this.update(this.activeDate.add("mo", -1));
19393     },
19394     showToday : function(e){
19395         this.update(new Date().clearTime());
19396     },
19397     // private
19398     showNextMonth : function(e){
19399         this.update(this.activeDate.add("mo", 1));
19400     },
19401
19402     // private
19403     showPrevYear : function(){
19404         this.update(this.activeDate.add("y", -1));
19405     },
19406
19407     // private
19408     showNextYear : function(){
19409         this.update(this.activeDate.add("y", 1));
19410     },
19411
19412     
19413    // private
19414     update : function(date)
19415     {
19416         var vd = this.activeDate;
19417         this.activeDate = date;
19418 //        if(vd && this.el){
19419 //            var t = date.getTime();
19420 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19421 //                Roo.log('using add remove');
19422 //                
19423 //                this.fireEvent('monthchange', this, date);
19424 //                
19425 //                this.cells.removeClass("fc-state-highlight");
19426 //                this.cells.each(function(c){
19427 //                   if(c.dateValue == t){
19428 //                       c.addClass("fc-state-highlight");
19429 //                       setTimeout(function(){
19430 //                            try{c.dom.firstChild.focus();}catch(e){}
19431 //                       }, 50);
19432 //                       return false;
19433 //                   }
19434 //                   return true;
19435 //                });
19436 //                return;
19437 //            }
19438 //        }
19439         
19440         var days = date.getDaysInMonth();
19441         
19442         var firstOfMonth = date.getFirstDateOfMonth();
19443         var startingPos = firstOfMonth.getDay()-this.startDay;
19444         
19445         if(startingPos < this.startDay){
19446             startingPos += 7;
19447         }
19448         
19449         var pm = date.add(Date.MONTH, -1);
19450         var prevStart = pm.getDaysInMonth()-startingPos;
19451 //        
19452         this.cells = this.el.select('.fc-day',true);
19453         this.textNodes = this.el.query('.fc-day-number');
19454         this.cells.addClassOnOver('fc-state-hover');
19455         
19456         var cells = this.cells.elements;
19457         var textEls = this.textNodes;
19458         
19459         Roo.each(cells, function(cell){
19460             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19461         });
19462         
19463         days += startingPos;
19464
19465         // convert everything to numbers so it's fast
19466         var day = 86400000;
19467         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19468         //Roo.log(d);
19469         //Roo.log(pm);
19470         //Roo.log(prevStart);
19471         
19472         var today = new Date().clearTime().getTime();
19473         var sel = date.clearTime().getTime();
19474         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19475         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19476         var ddMatch = this.disabledDatesRE;
19477         var ddText = this.disabledDatesText;
19478         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19479         var ddaysText = this.disabledDaysText;
19480         var format = this.format;
19481         
19482         var setCellClass = function(cal, cell){
19483             cell.row = 0;
19484             cell.events = [];
19485             cell.more = [];
19486             //Roo.log('set Cell Class');
19487             cell.title = "";
19488             var t = d.getTime();
19489             
19490             //Roo.log(d);
19491             
19492             cell.dateValue = t;
19493             if(t == today){
19494                 cell.className += " fc-today";
19495                 cell.className += " fc-state-highlight";
19496                 cell.title = cal.todayText;
19497             }
19498             if(t == sel){
19499                 // disable highlight in other month..
19500                 //cell.className += " fc-state-highlight";
19501                 
19502             }
19503             // disabling
19504             if(t < min) {
19505                 cell.className = " fc-state-disabled";
19506                 cell.title = cal.minText;
19507                 return;
19508             }
19509             if(t > max) {
19510                 cell.className = " fc-state-disabled";
19511                 cell.title = cal.maxText;
19512                 return;
19513             }
19514             if(ddays){
19515                 if(ddays.indexOf(d.getDay()) != -1){
19516                     cell.title = ddaysText;
19517                     cell.className = " fc-state-disabled";
19518                 }
19519             }
19520             if(ddMatch && format){
19521                 var fvalue = d.dateFormat(format);
19522                 if(ddMatch.test(fvalue)){
19523                     cell.title = ddText.replace("%0", fvalue);
19524                     cell.className = " fc-state-disabled";
19525                 }
19526             }
19527             
19528             if (!cell.initialClassName) {
19529                 cell.initialClassName = cell.dom.className;
19530             }
19531             
19532             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19533         };
19534
19535         var i = 0;
19536         
19537         for(; i < startingPos; i++) {
19538             textEls[i].innerHTML = (++prevStart);
19539             d.setDate(d.getDate()+1);
19540             
19541             cells[i].className = "fc-past fc-other-month";
19542             setCellClass(this, cells[i]);
19543         }
19544         
19545         var intDay = 0;
19546         
19547         for(; i < days; i++){
19548             intDay = i - startingPos + 1;
19549             textEls[i].innerHTML = (intDay);
19550             d.setDate(d.getDate()+1);
19551             
19552             cells[i].className = ''; // "x-date-active";
19553             setCellClass(this, cells[i]);
19554         }
19555         var extraDays = 0;
19556         
19557         for(; i < 42; i++) {
19558             textEls[i].innerHTML = (++extraDays);
19559             d.setDate(d.getDate()+1);
19560             
19561             cells[i].className = "fc-future fc-other-month";
19562             setCellClass(this, cells[i]);
19563         }
19564         
19565         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19566         
19567         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19568         
19569         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19570         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19571         
19572         if(totalRows != 6){
19573             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19574             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19575         }
19576         
19577         this.fireEvent('monthchange', this, date);
19578         
19579         
19580         /*
19581         if(!this.internalRender){
19582             var main = this.el.dom.firstChild;
19583             var w = main.offsetWidth;
19584             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19585             Roo.fly(main).setWidth(w);
19586             this.internalRender = true;
19587             // opera does not respect the auto grow header center column
19588             // then, after it gets a width opera refuses to recalculate
19589             // without a second pass
19590             if(Roo.isOpera && !this.secondPass){
19591                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19592                 this.secondPass = true;
19593                 this.update.defer(10, this, [date]);
19594             }
19595         }
19596         */
19597         
19598     },
19599     
19600     findCell : function(dt) {
19601         dt = dt.clearTime().getTime();
19602         var ret = false;
19603         this.cells.each(function(c){
19604             //Roo.log("check " +c.dateValue + '?=' + dt);
19605             if(c.dateValue == dt){
19606                 ret = c;
19607                 return false;
19608             }
19609             return true;
19610         });
19611         
19612         return ret;
19613     },
19614     
19615     findCells : function(ev) {
19616         var s = ev.start.clone().clearTime().getTime();
19617        // Roo.log(s);
19618         var e= ev.end.clone().clearTime().getTime();
19619        // Roo.log(e);
19620         var ret = [];
19621         this.cells.each(function(c){
19622              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19623             
19624             if(c.dateValue > e){
19625                 return ;
19626             }
19627             if(c.dateValue < s){
19628                 return ;
19629             }
19630             ret.push(c);
19631         });
19632         
19633         return ret;    
19634     },
19635     
19636 //    findBestRow: function(cells)
19637 //    {
19638 //        var ret = 0;
19639 //        
19640 //        for (var i =0 ; i < cells.length;i++) {
19641 //            ret  = Math.max(cells[i].rows || 0,ret);
19642 //        }
19643 //        return ret;
19644 //        
19645 //    },
19646     
19647     
19648     addItem : function(ev)
19649     {
19650         // look for vertical location slot in
19651         var cells = this.findCells(ev);
19652         
19653 //        ev.row = this.findBestRow(cells);
19654         
19655         // work out the location.
19656         
19657         var crow = false;
19658         var rows = [];
19659         for(var i =0; i < cells.length; i++) {
19660             
19661             cells[i].row = cells[0].row;
19662             
19663             if(i == 0){
19664                 cells[i].row = cells[i].row + 1;
19665             }
19666             
19667             if (!crow) {
19668                 crow = {
19669                     start : cells[i],
19670                     end :  cells[i]
19671                 };
19672                 continue;
19673             }
19674             if (crow.start.getY() == cells[i].getY()) {
19675                 // on same row.
19676                 crow.end = cells[i];
19677                 continue;
19678             }
19679             // different row.
19680             rows.push(crow);
19681             crow = {
19682                 start: cells[i],
19683                 end : cells[i]
19684             };
19685             
19686         }
19687         
19688         rows.push(crow);
19689         ev.els = [];
19690         ev.rows = rows;
19691         ev.cells = cells;
19692         
19693         cells[0].events.push(ev);
19694         
19695         this.calevents.push(ev);
19696     },
19697     
19698     clearEvents: function() {
19699         
19700         if(!this.calevents){
19701             return;
19702         }
19703         
19704         Roo.each(this.cells.elements, function(c){
19705             c.row = 0;
19706             c.events = [];
19707             c.more = [];
19708         });
19709         
19710         Roo.each(this.calevents, function(e) {
19711             Roo.each(e.els, function(el) {
19712                 el.un('mouseenter' ,this.onEventEnter, this);
19713                 el.un('mouseleave' ,this.onEventLeave, this);
19714                 el.remove();
19715             },this);
19716         },this);
19717         
19718         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19719             e.remove();
19720         });
19721         
19722     },
19723     
19724     renderEvents: function()
19725     {   
19726         var _this = this;
19727         
19728         this.cells.each(function(c) {
19729             
19730             if(c.row < 5){
19731                 return;
19732             }
19733             
19734             var ev = c.events;
19735             
19736             var r = 4;
19737             if(c.row != c.events.length){
19738                 r = 4 - (4 - (c.row - c.events.length));
19739             }
19740             
19741             c.events = ev.slice(0, r);
19742             c.more = ev.slice(r);
19743             
19744             if(c.more.length && c.more.length == 1){
19745                 c.events.push(c.more.pop());
19746             }
19747             
19748             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19749             
19750         });
19751             
19752         this.cells.each(function(c) {
19753             
19754             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19755             
19756             
19757             for (var e = 0; e < c.events.length; e++){
19758                 var ev = c.events[e];
19759                 var rows = ev.rows;
19760                 
19761                 for(var i = 0; i < rows.length; i++) {
19762                 
19763                     // how many rows should it span..
19764
19765                     var  cfg = {
19766                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19767                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19768
19769                         unselectable : "on",
19770                         cn : [
19771                             {
19772                                 cls: 'fc-event-inner',
19773                                 cn : [
19774     //                                {
19775     //                                  tag:'span',
19776     //                                  cls: 'fc-event-time',
19777     //                                  html : cells.length > 1 ? '' : ev.time
19778     //                                },
19779                                     {
19780                                       tag:'span',
19781                                       cls: 'fc-event-title',
19782                                       html : String.format('{0}', ev.title)
19783                                     }
19784
19785
19786                                 ]
19787                             },
19788                             {
19789                                 cls: 'ui-resizable-handle ui-resizable-e',
19790                                 html : '&nbsp;&nbsp;&nbsp'
19791                             }
19792
19793                         ]
19794                     };
19795
19796                     if (i == 0) {
19797                         cfg.cls += ' fc-event-start';
19798                     }
19799                     if ((i+1) == rows.length) {
19800                         cfg.cls += ' fc-event-end';
19801                     }
19802
19803                     var ctr = _this.el.select('.fc-event-container',true).first();
19804                     var cg = ctr.createChild(cfg);
19805
19806                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19807                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19808
19809                     var r = (c.more.length) ? 1 : 0;
19810                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19811                     cg.setWidth(ebox.right - sbox.x -2);
19812
19813                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19814                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19815                     cg.on('click', _this.onEventClick, _this, ev);
19816
19817                     ev.els.push(cg);
19818                     
19819                 }
19820                 
19821             }
19822             
19823             
19824             if(c.more.length){
19825                 var  cfg = {
19826                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19827                     style : 'position: absolute',
19828                     unselectable : "on",
19829                     cn : [
19830                         {
19831                             cls: 'fc-event-inner',
19832                             cn : [
19833                                 {
19834                                   tag:'span',
19835                                   cls: 'fc-event-title',
19836                                   html : 'More'
19837                                 }
19838
19839
19840                             ]
19841                         },
19842                         {
19843                             cls: 'ui-resizable-handle ui-resizable-e',
19844                             html : '&nbsp;&nbsp;&nbsp'
19845                         }
19846
19847                     ]
19848                 };
19849
19850                 var ctr = _this.el.select('.fc-event-container',true).first();
19851                 var cg = ctr.createChild(cfg);
19852
19853                 var sbox = c.select('.fc-day-content',true).first().getBox();
19854                 var ebox = c.select('.fc-day-content',true).first().getBox();
19855                 //Roo.log(cg);
19856                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19857                 cg.setWidth(ebox.right - sbox.x -2);
19858
19859                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19860                 
19861             }
19862             
19863         });
19864         
19865         
19866         
19867     },
19868     
19869     onEventEnter: function (e, el,event,d) {
19870         this.fireEvent('evententer', this, el, event);
19871     },
19872     
19873     onEventLeave: function (e, el,event,d) {
19874         this.fireEvent('eventleave', this, el, event);
19875     },
19876     
19877     onEventClick: function (e, el,event,d) {
19878         this.fireEvent('eventclick', this, el, event);
19879     },
19880     
19881     onMonthChange: function () {
19882         this.store.load();
19883     },
19884     
19885     onMoreEventClick: function(e, el, more)
19886     {
19887         var _this = this;
19888         
19889         this.calpopover.placement = 'right';
19890         this.calpopover.setTitle('More');
19891         
19892         this.calpopover.setContent('');
19893         
19894         var ctr = this.calpopover.el.select('.popover-content', true).first();
19895         
19896         Roo.each(more, function(m){
19897             var cfg = {
19898                 cls : 'fc-event-hori fc-event-draggable',
19899                 html : m.title
19900             };
19901             var cg = ctr.createChild(cfg);
19902             
19903             cg.on('click', _this.onEventClick, _this, m);
19904         });
19905         
19906         this.calpopover.show(el);
19907         
19908         
19909     },
19910     
19911     onLoad: function () 
19912     {   
19913         this.calevents = [];
19914         var cal = this;
19915         
19916         if(this.store.getCount() > 0){
19917             this.store.data.each(function(d){
19918                cal.addItem({
19919                     id : d.data.id,
19920                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19921                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19922                     time : d.data.start_time,
19923                     title : d.data.title,
19924                     description : d.data.description,
19925                     venue : d.data.venue
19926                 });
19927             });
19928         }
19929         
19930         this.renderEvents();
19931         
19932         if(this.calevents.length && this.loadMask){
19933             this.maskEl.hide();
19934         }
19935     },
19936     
19937     onBeforeLoad: function()
19938     {
19939         this.clearEvents();
19940         if(this.loadMask){
19941             this.maskEl.show();
19942         }
19943     }
19944 });
19945
19946  
19947  /*
19948  * - LGPL
19949  *
19950  * element
19951  * 
19952  */
19953
19954 /**
19955  * @class Roo.bootstrap.Popover
19956  * @extends Roo.bootstrap.Component
19957  * Bootstrap Popover class
19958  * @cfg {String} html contents of the popover   (or false to use children..)
19959  * @cfg {String} title of popover (or false to hide)
19960  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19961  * @cfg {String} trigger click || hover (or false to trigger manually)
19962  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19963  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19964  *      - if false and it has a 'parent' then it will be automatically added to that element
19965  *      - if string - Roo.get  will be called 
19966  * @cfg {Number} delay - delay before showing
19967  
19968  * @constructor
19969  * Create a new Popover
19970  * @param {Object} config The config object
19971  */
19972
19973 Roo.bootstrap.Popover = function(config){
19974     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19975     
19976     this.addEvents({
19977         // raw events
19978          /**
19979          * @event show
19980          * After the popover show
19981          * 
19982          * @param {Roo.bootstrap.Popover} this
19983          */
19984         "show" : true,
19985         /**
19986          * @event hide
19987          * After the popover hide
19988          * 
19989          * @param {Roo.bootstrap.Popover} this
19990          */
19991         "hide" : true
19992     });
19993 };
19994
19995 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19996     
19997     title: false,
19998     html: false,
19999     
20000     placement : 'right',
20001     trigger : 'hover', // hover
20002     modal : false,
20003     delay : 0,
20004     
20005     over: false,
20006     
20007     can_build_overlaid : false,
20008     
20009     maskEl : false, // the mask element
20010     headerEl : false,
20011     contentEl : false,
20012     alignEl : false, // when show is called with an element - this get's stored.
20013     
20014     getChildContainer : function()
20015     {
20016         return this.contentEl;
20017         
20018     },
20019     getPopoverHeader : function()
20020     {
20021         this.title = true; // flag not to hide it..
20022         this.headerEl.addClass('p-0');
20023         return this.headerEl
20024     },
20025     
20026     
20027     getAutoCreate : function(){
20028          
20029         var cfg = {
20030            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20031            style: 'display:block',
20032            cn : [
20033                 {
20034                     cls : 'arrow'
20035                 },
20036                 {
20037                     cls : 'popover-inner ',
20038                     cn : [
20039                         {
20040                             tag: 'h3',
20041                             cls: 'popover-title popover-header',
20042                             html : this.title === false ? '' : this.title
20043                         },
20044                         {
20045                             cls : 'popover-content popover-body '  + (this.cls || ''),
20046                             html : this.html || ''
20047                         }
20048                     ]
20049                     
20050                 }
20051            ]
20052         };
20053         
20054         return cfg;
20055     },
20056     /**
20057      * @param {string} the title
20058      */
20059     setTitle: function(str)
20060     {
20061         this.title = str;
20062         if (this.el) {
20063             this.headerEl.dom.innerHTML = str;
20064         }
20065         
20066     },
20067     /**
20068      * @param {string} the body content
20069      */
20070     setContent: function(str)
20071     {
20072         this.html = str;
20073         if (this.contentEl) {
20074             this.contentEl.dom.innerHTML = str;
20075         }
20076         
20077     },
20078     // as it get's added to the bottom of the page.
20079     onRender : function(ct, position)
20080     {
20081         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20082         
20083         
20084         
20085         if(!this.el){
20086             var cfg = Roo.apply({},  this.getAutoCreate());
20087             cfg.id = Roo.id();
20088             
20089             if (this.cls) {
20090                 cfg.cls += ' ' + this.cls;
20091             }
20092             if (this.style) {
20093                 cfg.style = this.style;
20094             }
20095             //Roo.log("adding to ");
20096             this.el = Roo.get(document.body).createChild(cfg, position);
20097 //            Roo.log(this.el);
20098         }
20099         
20100         this.contentEl = this.el.select('.popover-content',true).first();
20101         this.headerEl =  this.el.select('.popover-title',true).first();
20102         
20103         var nitems = [];
20104         if(typeof(this.items) != 'undefined'){
20105             var items = this.items;
20106             delete this.items;
20107
20108             for(var i =0;i < items.length;i++) {
20109                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20110             }
20111         }
20112
20113         this.items = nitems;
20114         
20115         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20116         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20117         
20118         
20119         
20120         this.initEvents();
20121     },
20122     
20123     resizeMask : function()
20124     {
20125         this.maskEl.setSize(
20126             Roo.lib.Dom.getViewWidth(true),
20127             Roo.lib.Dom.getViewHeight(true)
20128         );
20129     },
20130     
20131     initEvents : function()
20132     {
20133         
20134         if (!this.modal) { 
20135             Roo.bootstrap.Popover.register(this);
20136         }
20137          
20138         this.arrowEl = this.el.select('.arrow',true).first();
20139         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20140         this.el.enableDisplayMode('block');
20141         this.el.hide();
20142  
20143         
20144         if (this.over === false && !this.parent()) {
20145             return; 
20146         }
20147         if (this.triggers === false) {
20148             return;
20149         }
20150          
20151         // support parent
20152         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20153         var triggers = this.trigger ? this.trigger.split(' ') : [];
20154         Roo.each(triggers, function(trigger) {
20155         
20156             if (trigger == 'click') {
20157                 on_el.on('click', this.toggle, this);
20158             } else if (trigger != 'manual') {
20159                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20160                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20161       
20162                 on_el.on(eventIn  ,this.enter, this);
20163                 on_el.on(eventOut, this.leave, this);
20164             }
20165         }, this);
20166     },
20167     
20168     
20169     // private
20170     timeout : null,
20171     hoverState : null,
20172     
20173     toggle : function () {
20174         this.hoverState == 'in' ? this.leave() : this.enter();
20175     },
20176     
20177     enter : function () {
20178         
20179         clearTimeout(this.timeout);
20180     
20181         this.hoverState = 'in';
20182     
20183         if (!this.delay || !this.delay.show) {
20184             this.show();
20185             return;
20186         }
20187         var _t = this;
20188         this.timeout = setTimeout(function () {
20189             if (_t.hoverState == 'in') {
20190                 _t.show();
20191             }
20192         }, this.delay.show)
20193     },
20194     
20195     leave : function() {
20196         clearTimeout(this.timeout);
20197     
20198         this.hoverState = 'out';
20199     
20200         if (!this.delay || !this.delay.hide) {
20201             this.hide();
20202             return;
20203         }
20204         var _t = this;
20205         this.timeout = setTimeout(function () {
20206             if (_t.hoverState == 'out') {
20207                 _t.hide();
20208             }
20209         }, this.delay.hide)
20210     },
20211     /**
20212      * Show the popover
20213      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20214      * @param {string} (left|right|top|bottom) position
20215      */
20216     show : function (on_el, placement)
20217     {
20218         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20219         on_el = on_el || false; // default to false
20220          
20221         if (!on_el) {
20222             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20223                 on_el = this.parent().el;
20224             } else if (this.over) {
20225                 Roo.get(this.over);
20226             }
20227             
20228         }
20229         
20230         this.alignEl = Roo.get( on_el );
20231
20232         if (!this.el) {
20233             this.render(document.body);
20234         }
20235         
20236         
20237          
20238         
20239         if (this.title === false) {
20240             this.headerEl.hide();
20241         }
20242         
20243        
20244         this.el.show();
20245         this.el.dom.style.display = 'block';
20246          
20247  
20248         if (this.alignEl) {
20249             this.updatePosition(this.placement, true);
20250              
20251         } else {
20252             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20253             var es = this.el.getSize();
20254             var x = Roo.lib.Dom.getViewWidth()/2;
20255             var y = Roo.lib.Dom.getViewHeight()/2;
20256             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20257             
20258         }
20259
20260         
20261         //var arrow = this.el.select('.arrow',true).first();
20262         //arrow.set(align[2], 
20263         
20264         this.el.addClass('in');
20265         
20266          
20267         
20268         this.hoverState = 'in';
20269         
20270         if (this.modal) {
20271             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20272             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20273             this.maskEl.dom.style.display = 'block';
20274             this.maskEl.addClass('show');
20275         }
20276         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20277  
20278         this.fireEvent('show', this);
20279         
20280     },
20281     /**
20282      * fire this manually after loading a grid in the table for example
20283      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20284      * @param {Boolean} try and move it if we cant get right position.
20285      */
20286     updatePosition : function(placement, try_move)
20287     {
20288         // allow for calling with no parameters
20289         placement = placement   ? placement :  this.placement;
20290         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20291         
20292         this.el.removeClass([
20293             'fade','top','bottom', 'left', 'right','in',
20294             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20295         ]);
20296         this.el.addClass(placement + ' bs-popover-' + placement);
20297         
20298         if (!this.alignEl ) {
20299             return false;
20300         }
20301         
20302         switch (placement) {
20303             case 'right':
20304                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20305                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20306                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20307                     //normal display... or moved up/down.
20308                     this.el.setXY(offset);
20309                     var xy = this.alignEl.getAnchorXY('tr', false);
20310                     xy[0]+=2;xy[1]+=5;
20311                     this.arrowEl.setXY(xy);
20312                     return true;
20313                 }
20314                 // continue through...
20315                 return this.updatePosition('left', false);
20316                 
20317             
20318             case 'left':
20319                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20320                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20321                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20322                     //normal display... or moved up/down.
20323                     this.el.setXY(offset);
20324                     var xy = this.alignEl.getAnchorXY('tl', false);
20325                     xy[0]-=10;xy[1]+=5; // << fix me
20326                     this.arrowEl.setXY(xy);
20327                     return true;
20328                 }
20329                 // call self...
20330                 return this.updatePosition('right', false);
20331             
20332             case 'top':
20333                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20334                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20335                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20336                     //normal display... or moved up/down.
20337                     this.el.setXY(offset);
20338                     var xy = this.alignEl.getAnchorXY('t', false);
20339                     xy[1]-=10; // << fix me
20340                     this.arrowEl.setXY(xy);
20341                     return true;
20342                 }
20343                 // fall through
20344                return this.updatePosition('bottom', false);
20345             
20346             case 'bottom':
20347                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20348                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20349                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20350                     //normal display... or moved up/down.
20351                     this.el.setXY(offset);
20352                     var xy = this.alignEl.getAnchorXY('b', false);
20353                      xy[1]+=2; // << fix me
20354                     this.arrowEl.setXY(xy);
20355                     return true;
20356                 }
20357                 // fall through
20358                 return this.updatePosition('top', false);
20359                 
20360             
20361         }
20362         
20363         
20364         return false;
20365     },
20366     
20367     hide : function()
20368     {
20369         this.el.setXY([0,0]);
20370         this.el.removeClass('in');
20371         this.el.hide();
20372         this.hoverState = null;
20373         this.maskEl.hide(); // always..
20374         this.fireEvent('hide', this);
20375     }
20376     
20377 });
20378
20379
20380 Roo.apply(Roo.bootstrap.Popover, {
20381
20382     alignment : {
20383         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20384         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20385         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20386         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20387     },
20388     
20389     zIndex : 20001,
20390
20391     clickHander : false,
20392     
20393
20394     onMouseDown : function(e)
20395     {
20396         if (!e.getTarget(".roo-popover")) {
20397             this.hideAll();
20398         }
20399          
20400     },
20401     
20402     popups : [],
20403     
20404     register : function(popup)
20405     {
20406         if (!Roo.bootstrap.Popover.clickHandler) {
20407             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20408         }
20409         // hide other popups.
20410         this.hideAll();
20411         this.popups.push(popup);
20412     },
20413     hideAll : function()
20414     {
20415         this.popups.forEach(function(p) {
20416             p.hide();
20417         });
20418     }
20419
20420 });/*
20421  * - LGPL
20422  *
20423  * Card header - holder for the card header elements.
20424  * 
20425  */
20426
20427 /**
20428  * @class Roo.bootstrap.PopoverNav
20429  * @extends Roo.bootstrap.NavGroup
20430  * Bootstrap Popover header navigation class
20431  * @constructor
20432  * Create a new Popover Header Navigation 
20433  * @param {Object} config The config object
20434  */
20435
20436 Roo.bootstrap.PopoverNav = function(config){
20437     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20438 };
20439
20440 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20441     
20442     
20443     container_method : 'getPopoverHeader' 
20444     
20445      
20446     
20447     
20448    
20449 });
20450
20451  
20452
20453  /*
20454  * - LGPL
20455  *
20456  * Progress
20457  * 
20458  */
20459
20460 /**
20461  * @class Roo.bootstrap.Progress
20462  * @extends Roo.bootstrap.Component
20463  * Bootstrap Progress class
20464  * @cfg {Boolean} striped striped of the progress bar
20465  * @cfg {Boolean} active animated of the progress bar
20466  * 
20467  * 
20468  * @constructor
20469  * Create a new Progress
20470  * @param {Object} config The config object
20471  */
20472
20473 Roo.bootstrap.Progress = function(config){
20474     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20475 };
20476
20477 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20478     
20479     striped : false,
20480     active: false,
20481     
20482     getAutoCreate : function(){
20483         var cfg = {
20484             tag: 'div',
20485             cls: 'progress'
20486         };
20487         
20488         
20489         if(this.striped){
20490             cfg.cls += ' progress-striped';
20491         }
20492       
20493         if(this.active){
20494             cfg.cls += ' active';
20495         }
20496         
20497         
20498         return cfg;
20499     }
20500    
20501 });
20502
20503  
20504
20505  /*
20506  * - LGPL
20507  *
20508  * ProgressBar
20509  * 
20510  */
20511
20512 /**
20513  * @class Roo.bootstrap.ProgressBar
20514  * @extends Roo.bootstrap.Component
20515  * Bootstrap ProgressBar class
20516  * @cfg {Number} aria_valuenow aria-value now
20517  * @cfg {Number} aria_valuemin aria-value min
20518  * @cfg {Number} aria_valuemax aria-value max
20519  * @cfg {String} label label for the progress bar
20520  * @cfg {String} panel (success | info | warning | danger )
20521  * @cfg {String} role role of the progress bar
20522  * @cfg {String} sr_only text
20523  * 
20524  * 
20525  * @constructor
20526  * Create a new ProgressBar
20527  * @param {Object} config The config object
20528  */
20529
20530 Roo.bootstrap.ProgressBar = function(config){
20531     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20532 };
20533
20534 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20535     
20536     aria_valuenow : 0,
20537     aria_valuemin : 0,
20538     aria_valuemax : 100,
20539     label : false,
20540     panel : false,
20541     role : false,
20542     sr_only: false,
20543     
20544     getAutoCreate : function()
20545     {
20546         
20547         var cfg = {
20548             tag: 'div',
20549             cls: 'progress-bar',
20550             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20551         };
20552         
20553         if(this.sr_only){
20554             cfg.cn = {
20555                 tag: 'span',
20556                 cls: 'sr-only',
20557                 html: this.sr_only
20558             }
20559         }
20560         
20561         if(this.role){
20562             cfg.role = this.role;
20563         }
20564         
20565         if(this.aria_valuenow){
20566             cfg['aria-valuenow'] = this.aria_valuenow;
20567         }
20568         
20569         if(this.aria_valuemin){
20570             cfg['aria-valuemin'] = this.aria_valuemin;
20571         }
20572         
20573         if(this.aria_valuemax){
20574             cfg['aria-valuemax'] = this.aria_valuemax;
20575         }
20576         
20577         if(this.label && !this.sr_only){
20578             cfg.html = this.label;
20579         }
20580         
20581         if(this.panel){
20582             cfg.cls += ' progress-bar-' + this.panel;
20583         }
20584         
20585         return cfg;
20586     },
20587     
20588     update : function(aria_valuenow)
20589     {
20590         this.aria_valuenow = aria_valuenow;
20591         
20592         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20593     }
20594    
20595 });
20596
20597  
20598
20599  /*
20600  * - LGPL
20601  *
20602  * column
20603  * 
20604  */
20605
20606 /**
20607  * @class Roo.bootstrap.TabGroup
20608  * @extends Roo.bootstrap.Column
20609  * Bootstrap Column class
20610  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20611  * @cfg {Boolean} carousel true to make the group behave like a carousel
20612  * @cfg {Boolean} bullets show bullets for the panels
20613  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20614  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20615  * @cfg {Boolean} showarrow (true|false) show arrow default true
20616  * 
20617  * @constructor
20618  * Create a new TabGroup
20619  * @param {Object} config The config object
20620  */
20621
20622 Roo.bootstrap.TabGroup = function(config){
20623     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20624     if (!this.navId) {
20625         this.navId = Roo.id();
20626     }
20627     this.tabs = [];
20628     Roo.bootstrap.TabGroup.register(this);
20629     
20630 };
20631
20632 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20633     
20634     carousel : false,
20635     transition : false,
20636     bullets : 0,
20637     timer : 0,
20638     autoslide : false,
20639     slideFn : false,
20640     slideOnTouch : false,
20641     showarrow : true,
20642     
20643     getAutoCreate : function()
20644     {
20645         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20646         
20647         cfg.cls += ' tab-content';
20648         
20649         if (this.carousel) {
20650             cfg.cls += ' carousel slide';
20651             
20652             cfg.cn = [{
20653                cls : 'carousel-inner',
20654                cn : []
20655             }];
20656         
20657             if(this.bullets  && !Roo.isTouch){
20658                 
20659                 var bullets = {
20660                     cls : 'carousel-bullets',
20661                     cn : []
20662                 };
20663                
20664                 if(this.bullets_cls){
20665                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20666                 }
20667                 
20668                 bullets.cn.push({
20669                     cls : 'clear'
20670                 });
20671                 
20672                 cfg.cn[0].cn.push(bullets);
20673             }
20674             
20675             if(this.showarrow){
20676                 cfg.cn[0].cn.push({
20677                     tag : 'div',
20678                     class : 'carousel-arrow',
20679                     cn : [
20680                         {
20681                             tag : 'div',
20682                             class : 'carousel-prev',
20683                             cn : [
20684                                 {
20685                                     tag : 'i',
20686                                     class : 'fa fa-chevron-left'
20687                                 }
20688                             ]
20689                         },
20690                         {
20691                             tag : 'div',
20692                             class : 'carousel-next',
20693                             cn : [
20694                                 {
20695                                     tag : 'i',
20696                                     class : 'fa fa-chevron-right'
20697                                 }
20698                             ]
20699                         }
20700                     ]
20701                 });
20702             }
20703             
20704         }
20705         
20706         return cfg;
20707     },
20708     
20709     initEvents:  function()
20710     {
20711 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20712 //            this.el.on("touchstart", this.onTouchStart, this);
20713 //        }
20714         
20715         if(this.autoslide){
20716             var _this = this;
20717             
20718             this.slideFn = window.setInterval(function() {
20719                 _this.showPanelNext();
20720             }, this.timer);
20721         }
20722         
20723         if(this.showarrow){
20724             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20725             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20726         }
20727         
20728         
20729     },
20730     
20731 //    onTouchStart : function(e, el, o)
20732 //    {
20733 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20734 //            return;
20735 //        }
20736 //        
20737 //        this.showPanelNext();
20738 //    },
20739     
20740     
20741     getChildContainer : function()
20742     {
20743         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20744     },
20745     
20746     /**
20747     * register a Navigation item
20748     * @param {Roo.bootstrap.NavItem} the navitem to add
20749     */
20750     register : function(item)
20751     {
20752         this.tabs.push( item);
20753         item.navId = this.navId; // not really needed..
20754         this.addBullet();
20755     
20756     },
20757     
20758     getActivePanel : function()
20759     {
20760         var r = false;
20761         Roo.each(this.tabs, function(t) {
20762             if (t.active) {
20763                 r = t;
20764                 return false;
20765             }
20766             return null;
20767         });
20768         return r;
20769         
20770     },
20771     getPanelByName : function(n)
20772     {
20773         var r = false;
20774         Roo.each(this.tabs, function(t) {
20775             if (t.tabId == n) {
20776                 r = t;
20777                 return false;
20778             }
20779             return null;
20780         });
20781         return r;
20782     },
20783     indexOfPanel : function(p)
20784     {
20785         var r = false;
20786         Roo.each(this.tabs, function(t,i) {
20787             if (t.tabId == p.tabId) {
20788                 r = i;
20789                 return false;
20790             }
20791             return null;
20792         });
20793         return r;
20794     },
20795     /**
20796      * show a specific panel
20797      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20798      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20799      */
20800     showPanel : function (pan)
20801     {
20802         if(this.transition || typeof(pan) == 'undefined'){
20803             Roo.log("waiting for the transitionend");
20804             return false;
20805         }
20806         
20807         if (typeof(pan) == 'number') {
20808             pan = this.tabs[pan];
20809         }
20810         
20811         if (typeof(pan) == 'string') {
20812             pan = this.getPanelByName(pan);
20813         }
20814         
20815         var cur = this.getActivePanel();
20816         
20817         if(!pan || !cur){
20818             Roo.log('pan or acitve pan is undefined');
20819             return false;
20820         }
20821         
20822         if (pan.tabId == this.getActivePanel().tabId) {
20823             return true;
20824         }
20825         
20826         if (false === cur.fireEvent('beforedeactivate')) {
20827             return false;
20828         }
20829         
20830         if(this.bullets > 0 && !Roo.isTouch){
20831             this.setActiveBullet(this.indexOfPanel(pan));
20832         }
20833         
20834         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20835             
20836             //class="carousel-item carousel-item-next carousel-item-left"
20837             
20838             this.transition = true;
20839             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20840             var lr = dir == 'next' ? 'left' : 'right';
20841             pan.el.addClass(dir); // or prev
20842             pan.el.addClass('carousel-item-' + dir); // or prev
20843             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20844             cur.el.addClass(lr); // or right
20845             pan.el.addClass(lr);
20846             cur.el.addClass('carousel-item-' +lr); // or right
20847             pan.el.addClass('carousel-item-' +lr);
20848             
20849             
20850             var _this = this;
20851             cur.el.on('transitionend', function() {
20852                 Roo.log("trans end?");
20853                 
20854                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20855                 pan.setActive(true);
20856                 
20857                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20858                 cur.setActive(false);
20859                 
20860                 _this.transition = false;
20861                 
20862             }, this, { single:  true } );
20863             
20864             return true;
20865         }
20866         
20867         cur.setActive(false);
20868         pan.setActive(true);
20869         
20870         return true;
20871         
20872     },
20873     showPanelNext : function()
20874     {
20875         var i = this.indexOfPanel(this.getActivePanel());
20876         
20877         if (i >= this.tabs.length - 1 && !this.autoslide) {
20878             return;
20879         }
20880         
20881         if (i >= this.tabs.length - 1 && this.autoslide) {
20882             i = -1;
20883         }
20884         
20885         this.showPanel(this.tabs[i+1]);
20886     },
20887     
20888     showPanelPrev : function()
20889     {
20890         var i = this.indexOfPanel(this.getActivePanel());
20891         
20892         if (i  < 1 && !this.autoslide) {
20893             return;
20894         }
20895         
20896         if (i < 1 && this.autoslide) {
20897             i = this.tabs.length;
20898         }
20899         
20900         this.showPanel(this.tabs[i-1]);
20901     },
20902     
20903     
20904     addBullet: function()
20905     {
20906         if(!this.bullets || Roo.isTouch){
20907             return;
20908         }
20909         var ctr = this.el.select('.carousel-bullets',true).first();
20910         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20911         var bullet = ctr.createChild({
20912             cls : 'bullet bullet-' + i
20913         },ctr.dom.lastChild);
20914         
20915         
20916         var _this = this;
20917         
20918         bullet.on('click', (function(e, el, o, ii, t){
20919
20920             e.preventDefault();
20921
20922             this.showPanel(ii);
20923
20924             if(this.autoslide && this.slideFn){
20925                 clearInterval(this.slideFn);
20926                 this.slideFn = window.setInterval(function() {
20927                     _this.showPanelNext();
20928                 }, this.timer);
20929             }
20930
20931         }).createDelegate(this, [i, bullet], true));
20932                 
20933         
20934     },
20935      
20936     setActiveBullet : function(i)
20937     {
20938         if(Roo.isTouch){
20939             return;
20940         }
20941         
20942         Roo.each(this.el.select('.bullet', true).elements, function(el){
20943             el.removeClass('selected');
20944         });
20945
20946         var bullet = this.el.select('.bullet-' + i, true).first();
20947         
20948         if(!bullet){
20949             return;
20950         }
20951         
20952         bullet.addClass('selected');
20953     }
20954     
20955     
20956   
20957 });
20958
20959  
20960
20961  
20962  
20963 Roo.apply(Roo.bootstrap.TabGroup, {
20964     
20965     groups: {},
20966      /**
20967     * register a Navigation Group
20968     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20969     */
20970     register : function(navgrp)
20971     {
20972         this.groups[navgrp.navId] = navgrp;
20973         
20974     },
20975     /**
20976     * fetch a Navigation Group based on the navigation ID
20977     * if one does not exist , it will get created.
20978     * @param {string} the navgroup to add
20979     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20980     */
20981     get: function(navId) {
20982         if (typeof(this.groups[navId]) == 'undefined') {
20983             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20984         }
20985         return this.groups[navId] ;
20986     }
20987     
20988     
20989     
20990 });
20991
20992  /*
20993  * - LGPL
20994  *
20995  * TabPanel
20996  * 
20997  */
20998
20999 /**
21000  * @class Roo.bootstrap.TabPanel
21001  * @extends Roo.bootstrap.Component
21002  * Bootstrap TabPanel class
21003  * @cfg {Boolean} active panel active
21004  * @cfg {String} html panel content
21005  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21006  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21007  * @cfg {String} href click to link..
21008  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21009  * 
21010  * 
21011  * @constructor
21012  * Create a new TabPanel
21013  * @param {Object} config The config object
21014  */
21015
21016 Roo.bootstrap.TabPanel = function(config){
21017     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21018     this.addEvents({
21019         /**
21020              * @event changed
21021              * Fires when the active status changes
21022              * @param {Roo.bootstrap.TabPanel} this
21023              * @param {Boolean} state the new state
21024             
21025          */
21026         'changed': true,
21027         /**
21028              * @event beforedeactivate
21029              * Fires before a tab is de-activated - can be used to do validation on a form.
21030              * @param {Roo.bootstrap.TabPanel} this
21031              * @return {Boolean} false if there is an error
21032             
21033          */
21034         'beforedeactivate': true
21035      });
21036     
21037     this.tabId = this.tabId || Roo.id();
21038   
21039 };
21040
21041 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21042     
21043     active: false,
21044     html: false,
21045     tabId: false,
21046     navId : false,
21047     href : '',
21048     touchSlide : false,
21049     getAutoCreate : function(){
21050         
21051         
21052         var cfg = {
21053             tag: 'div',
21054             // item is needed for carousel - not sure if it has any effect otherwise
21055             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21056             html: this.html || ''
21057         };
21058         
21059         if(this.active){
21060             cfg.cls += ' active';
21061         }
21062         
21063         if(this.tabId){
21064             cfg.tabId = this.tabId;
21065         }
21066         
21067         
21068         
21069         return cfg;
21070     },
21071     
21072     initEvents:  function()
21073     {
21074         var p = this.parent();
21075         
21076         this.navId = this.navId || p.navId;
21077         
21078         if (typeof(this.navId) != 'undefined') {
21079             // not really needed.. but just in case.. parent should be a NavGroup.
21080             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21081             
21082             tg.register(this);
21083             
21084             var i = tg.tabs.length - 1;
21085             
21086             if(this.active && tg.bullets > 0 && i < tg.bullets){
21087                 tg.setActiveBullet(i);
21088             }
21089         }
21090         
21091         this.el.on('click', this.onClick, this);
21092         
21093         if(Roo.isTouch && this.touchSlide){
21094             this.el.on("touchstart", this.onTouchStart, this);
21095             this.el.on("touchmove", this.onTouchMove, this);
21096             this.el.on("touchend", this.onTouchEnd, this);
21097         }
21098         
21099     },
21100     
21101     onRender : function(ct, position)
21102     {
21103         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21104     },
21105     
21106     setActive : function(state)
21107     {
21108         Roo.log("panel - set active " + this.tabId + "=" + state);
21109         
21110         this.active = state;
21111         if (!state) {
21112             this.el.removeClass('active');
21113             
21114         } else  if (!this.el.hasClass('active')) {
21115             this.el.addClass('active');
21116         }
21117         
21118         this.fireEvent('changed', this, state);
21119     },
21120     
21121     onClick : function(e)
21122     {
21123         e.preventDefault();
21124         
21125         if(!this.href.length){
21126             return;
21127         }
21128         
21129         window.location.href = this.href;
21130     },
21131     
21132     startX : 0,
21133     startY : 0,
21134     endX : 0,
21135     endY : 0,
21136     swiping : false,
21137     
21138     onTouchStart : function(e)
21139     {
21140         this.swiping = false;
21141         
21142         this.startX = e.browserEvent.touches[0].clientX;
21143         this.startY = e.browserEvent.touches[0].clientY;
21144     },
21145     
21146     onTouchMove : function(e)
21147     {
21148         this.swiping = true;
21149         
21150         this.endX = e.browserEvent.touches[0].clientX;
21151         this.endY = e.browserEvent.touches[0].clientY;
21152     },
21153     
21154     onTouchEnd : function(e)
21155     {
21156         if(!this.swiping){
21157             this.onClick(e);
21158             return;
21159         }
21160         
21161         var tabGroup = this.parent();
21162         
21163         if(this.endX > this.startX){ // swiping right
21164             tabGroup.showPanelPrev();
21165             return;
21166         }
21167         
21168         if(this.startX > this.endX){ // swiping left
21169             tabGroup.showPanelNext();
21170             return;
21171         }
21172     }
21173     
21174     
21175 });
21176  
21177
21178  
21179
21180  /*
21181  * - LGPL
21182  *
21183  * DateField
21184  * 
21185  */
21186
21187 /**
21188  * @class Roo.bootstrap.DateField
21189  * @extends Roo.bootstrap.Input
21190  * Bootstrap DateField class
21191  * @cfg {Number} weekStart default 0
21192  * @cfg {String} viewMode default empty, (months|years)
21193  * @cfg {String} minViewMode default empty, (months|years)
21194  * @cfg {Number} startDate default -Infinity
21195  * @cfg {Number} endDate default Infinity
21196  * @cfg {Boolean} todayHighlight default false
21197  * @cfg {Boolean} todayBtn default false
21198  * @cfg {Boolean} calendarWeeks default false
21199  * @cfg {Object} daysOfWeekDisabled default empty
21200  * @cfg {Boolean} singleMode default false (true | false)
21201  * 
21202  * @cfg {Boolean} keyboardNavigation default true
21203  * @cfg {String} language default en
21204  * 
21205  * @constructor
21206  * Create a new DateField
21207  * @param {Object} config The config object
21208  */
21209
21210 Roo.bootstrap.DateField = function(config){
21211     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21212      this.addEvents({
21213             /**
21214              * @event show
21215              * Fires when this field show.
21216              * @param {Roo.bootstrap.DateField} this
21217              * @param {Mixed} date The date value
21218              */
21219             show : true,
21220             /**
21221              * @event show
21222              * Fires when this field hide.
21223              * @param {Roo.bootstrap.DateField} this
21224              * @param {Mixed} date The date value
21225              */
21226             hide : true,
21227             /**
21228              * @event select
21229              * Fires when select a date.
21230              * @param {Roo.bootstrap.DateField} this
21231              * @param {Mixed} date The date value
21232              */
21233             select : true,
21234             /**
21235              * @event beforeselect
21236              * Fires when before select a date.
21237              * @param {Roo.bootstrap.DateField} this
21238              * @param {Mixed} date The date value
21239              */
21240             beforeselect : true
21241         });
21242 };
21243
21244 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21245     
21246     /**
21247      * @cfg {String} format
21248      * The default date format string which can be overriden for localization support.  The format must be
21249      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21250      */
21251     format : "m/d/y",
21252     /**
21253      * @cfg {String} altFormats
21254      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21255      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21256      */
21257     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21258     
21259     weekStart : 0,
21260     
21261     viewMode : '',
21262     
21263     minViewMode : '',
21264     
21265     todayHighlight : false,
21266     
21267     todayBtn: false,
21268     
21269     language: 'en',
21270     
21271     keyboardNavigation: true,
21272     
21273     calendarWeeks: false,
21274     
21275     startDate: -Infinity,
21276     
21277     endDate: Infinity,
21278     
21279     daysOfWeekDisabled: [],
21280     
21281     _events: [],
21282     
21283     singleMode : false,
21284     
21285     UTCDate: function()
21286     {
21287         return new Date(Date.UTC.apply(Date, arguments));
21288     },
21289     
21290     UTCToday: function()
21291     {
21292         var today = new Date();
21293         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21294     },
21295     
21296     getDate: function() {
21297             var d = this.getUTCDate();
21298             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21299     },
21300     
21301     getUTCDate: function() {
21302             return this.date;
21303     },
21304     
21305     setDate: function(d) {
21306             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21307     },
21308     
21309     setUTCDate: function(d) {
21310             this.date = d;
21311             this.setValue(this.formatDate(this.date));
21312     },
21313         
21314     onRender: function(ct, position)
21315     {
21316         
21317         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21318         
21319         this.language = this.language || 'en';
21320         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21321         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21322         
21323         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21324         this.format = this.format || 'm/d/y';
21325         this.isInline = false;
21326         this.isInput = true;
21327         this.component = this.el.select('.add-on', true).first() || false;
21328         this.component = (this.component && this.component.length === 0) ? false : this.component;
21329         this.hasInput = this.component && this.inputEl().length;
21330         
21331         if (typeof(this.minViewMode === 'string')) {
21332             switch (this.minViewMode) {
21333                 case 'months':
21334                     this.minViewMode = 1;
21335                     break;
21336                 case 'years':
21337                     this.minViewMode = 2;
21338                     break;
21339                 default:
21340                     this.minViewMode = 0;
21341                     break;
21342             }
21343         }
21344         
21345         if (typeof(this.viewMode === 'string')) {
21346             switch (this.viewMode) {
21347                 case 'months':
21348                     this.viewMode = 1;
21349                     break;
21350                 case 'years':
21351                     this.viewMode = 2;
21352                     break;
21353                 default:
21354                     this.viewMode = 0;
21355                     break;
21356             }
21357         }
21358                 
21359         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21360         
21361 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21362         
21363         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21364         
21365         this.picker().on('mousedown', this.onMousedown, this);
21366         this.picker().on('click', this.onClick, this);
21367         
21368         this.picker().addClass('datepicker-dropdown');
21369         
21370         this.startViewMode = this.viewMode;
21371         
21372         if(this.singleMode){
21373             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21374                 v.setVisibilityMode(Roo.Element.DISPLAY);
21375                 v.hide();
21376             });
21377             
21378             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21379                 v.setStyle('width', '189px');
21380             });
21381         }
21382         
21383         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21384             if(!this.calendarWeeks){
21385                 v.remove();
21386                 return;
21387             }
21388             
21389             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21390             v.attr('colspan', function(i, val){
21391                 return parseInt(val) + 1;
21392             });
21393         });
21394                         
21395         
21396         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21397         
21398         this.setStartDate(this.startDate);
21399         this.setEndDate(this.endDate);
21400         
21401         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21402         
21403         this.fillDow();
21404         this.fillMonths();
21405         this.update();
21406         this.showMode();
21407         
21408         if(this.isInline) {
21409             this.showPopup();
21410         }
21411     },
21412     
21413     picker : function()
21414     {
21415         return this.pickerEl;
21416 //        return this.el.select('.datepicker', true).first();
21417     },
21418     
21419     fillDow: function()
21420     {
21421         var dowCnt = this.weekStart;
21422         
21423         var dow = {
21424             tag: 'tr',
21425             cn: [
21426                 
21427             ]
21428         };
21429         
21430         if(this.calendarWeeks){
21431             dow.cn.push({
21432                 tag: 'th',
21433                 cls: 'cw',
21434                 html: '&nbsp;'
21435             })
21436         }
21437         
21438         while (dowCnt < this.weekStart + 7) {
21439             dow.cn.push({
21440                 tag: 'th',
21441                 cls: 'dow',
21442                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21443             });
21444         }
21445         
21446         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21447     },
21448     
21449     fillMonths: function()
21450     {    
21451         var i = 0;
21452         var months = this.picker().select('>.datepicker-months td', true).first();
21453         
21454         months.dom.innerHTML = '';
21455         
21456         while (i < 12) {
21457             var month = {
21458                 tag: 'span',
21459                 cls: 'month',
21460                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21461             };
21462             
21463             months.createChild(month);
21464         }
21465         
21466     },
21467     
21468     update: function()
21469     {
21470         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;
21471         
21472         if (this.date < this.startDate) {
21473             this.viewDate = new Date(this.startDate);
21474         } else if (this.date > this.endDate) {
21475             this.viewDate = new Date(this.endDate);
21476         } else {
21477             this.viewDate = new Date(this.date);
21478         }
21479         
21480         this.fill();
21481     },
21482     
21483     fill: function() 
21484     {
21485         var d = new Date(this.viewDate),
21486                 year = d.getUTCFullYear(),
21487                 month = d.getUTCMonth(),
21488                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21489                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21490                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21491                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21492                 currentDate = this.date && this.date.valueOf(),
21493                 today = this.UTCToday();
21494         
21495         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21496         
21497 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21498         
21499 //        this.picker.select('>tfoot th.today').
21500 //                                              .text(dates[this.language].today)
21501 //                                              .toggle(this.todayBtn !== false);
21502     
21503         this.updateNavArrows();
21504         this.fillMonths();
21505                                                 
21506         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21507         
21508         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21509          
21510         prevMonth.setUTCDate(day);
21511         
21512         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21513         
21514         var nextMonth = new Date(prevMonth);
21515         
21516         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21517         
21518         nextMonth = nextMonth.valueOf();
21519         
21520         var fillMonths = false;
21521         
21522         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21523         
21524         while(prevMonth.valueOf() <= nextMonth) {
21525             var clsName = '';
21526             
21527             if (prevMonth.getUTCDay() === this.weekStart) {
21528                 if(fillMonths){
21529                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21530                 }
21531                     
21532                 fillMonths = {
21533                     tag: 'tr',
21534                     cn: []
21535                 };
21536                 
21537                 if(this.calendarWeeks){
21538                     // ISO 8601: First week contains first thursday.
21539                     // ISO also states week starts on Monday, but we can be more abstract here.
21540                     var
21541                     // Start of current week: based on weekstart/current date
21542                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21543                     // Thursday of this week
21544                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21545                     // First Thursday of year, year from thursday
21546                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21547                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21548                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21549                     
21550                     fillMonths.cn.push({
21551                         tag: 'td',
21552                         cls: 'cw',
21553                         html: calWeek
21554                     });
21555                 }
21556             }
21557             
21558             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21559                 clsName += ' old';
21560             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21561                 clsName += ' new';
21562             }
21563             if (this.todayHighlight &&
21564                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21565                 prevMonth.getUTCMonth() == today.getMonth() &&
21566                 prevMonth.getUTCDate() == today.getDate()) {
21567                 clsName += ' today';
21568             }
21569             
21570             if (currentDate && prevMonth.valueOf() === currentDate) {
21571                 clsName += ' active';
21572             }
21573             
21574             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21575                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21576                     clsName += ' disabled';
21577             }
21578             
21579             fillMonths.cn.push({
21580                 tag: 'td',
21581                 cls: 'day ' + clsName,
21582                 html: prevMonth.getDate()
21583             });
21584             
21585             prevMonth.setDate(prevMonth.getDate()+1);
21586         }
21587           
21588         var currentYear = this.date && this.date.getUTCFullYear();
21589         var currentMonth = this.date && this.date.getUTCMonth();
21590         
21591         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21592         
21593         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21594             v.removeClass('active');
21595             
21596             if(currentYear === year && k === currentMonth){
21597                 v.addClass('active');
21598             }
21599             
21600             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21601                 v.addClass('disabled');
21602             }
21603             
21604         });
21605         
21606         
21607         year = parseInt(year/10, 10) * 10;
21608         
21609         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21610         
21611         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21612         
21613         year -= 1;
21614         for (var i = -1; i < 11; i++) {
21615             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21616                 tag: 'span',
21617                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21618                 html: year
21619             });
21620             
21621             year += 1;
21622         }
21623     },
21624     
21625     showMode: function(dir) 
21626     {
21627         if (dir) {
21628             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21629         }
21630         
21631         Roo.each(this.picker().select('>div',true).elements, function(v){
21632             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21633             v.hide();
21634         });
21635         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21636     },
21637     
21638     place: function()
21639     {
21640         if(this.isInline) {
21641             return;
21642         }
21643         
21644         this.picker().removeClass(['bottom', 'top']);
21645         
21646         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21647             /*
21648              * place to the top of element!
21649              *
21650              */
21651             
21652             this.picker().addClass('top');
21653             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21654             
21655             return;
21656         }
21657         
21658         this.picker().addClass('bottom');
21659         
21660         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21661     },
21662     
21663     parseDate : function(value)
21664     {
21665         if(!value || value instanceof Date){
21666             return value;
21667         }
21668         var v = Date.parseDate(value, this.format);
21669         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21670             v = Date.parseDate(value, 'Y-m-d');
21671         }
21672         if(!v && this.altFormats){
21673             if(!this.altFormatsArray){
21674                 this.altFormatsArray = this.altFormats.split("|");
21675             }
21676             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21677                 v = Date.parseDate(value, this.altFormatsArray[i]);
21678             }
21679         }
21680         return v;
21681     },
21682     
21683     formatDate : function(date, fmt)
21684     {   
21685         return (!date || !(date instanceof Date)) ?
21686         date : date.dateFormat(fmt || this.format);
21687     },
21688     
21689     onFocus : function()
21690     {
21691         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21692         this.showPopup();
21693     },
21694     
21695     onBlur : function()
21696     {
21697         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21698         
21699         var d = this.inputEl().getValue();
21700         
21701         this.setValue(d);
21702                 
21703         this.hidePopup();
21704     },
21705     
21706     showPopup : function()
21707     {
21708         this.picker().show();
21709         this.update();
21710         this.place();
21711         
21712         this.fireEvent('showpopup', this, this.date);
21713     },
21714     
21715     hidePopup : function()
21716     {
21717         if(this.isInline) {
21718             return;
21719         }
21720         this.picker().hide();
21721         this.viewMode = this.startViewMode;
21722         this.showMode();
21723         
21724         this.fireEvent('hidepopup', this, this.date);
21725         
21726     },
21727     
21728     onMousedown: function(e)
21729     {
21730         e.stopPropagation();
21731         e.preventDefault();
21732     },
21733     
21734     keyup: function(e)
21735     {
21736         Roo.bootstrap.DateField.superclass.keyup.call(this);
21737         this.update();
21738     },
21739
21740     setValue: function(v)
21741     {
21742         if(this.fireEvent('beforeselect', this, v) !== false){
21743             var d = new Date(this.parseDate(v) ).clearTime();
21744         
21745             if(isNaN(d.getTime())){
21746                 this.date = this.viewDate = '';
21747                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21748                 return;
21749             }
21750
21751             v = this.formatDate(d);
21752
21753             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21754
21755             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21756
21757             this.update();
21758
21759             this.fireEvent('select', this, this.date);
21760         }
21761     },
21762     
21763     getValue: function()
21764     {
21765         return this.formatDate(this.date);
21766     },
21767     
21768     fireKey: function(e)
21769     {
21770         if (!this.picker().isVisible()){
21771             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21772                 this.showPopup();
21773             }
21774             return;
21775         }
21776         
21777         var dateChanged = false,
21778         dir, day, month,
21779         newDate, newViewDate;
21780         
21781         switch(e.keyCode){
21782             case 27: // escape
21783                 this.hidePopup();
21784                 e.preventDefault();
21785                 break;
21786             case 37: // left
21787             case 39: // right
21788                 if (!this.keyboardNavigation) {
21789                     break;
21790                 }
21791                 dir = e.keyCode == 37 ? -1 : 1;
21792                 
21793                 if (e.ctrlKey){
21794                     newDate = this.moveYear(this.date, dir);
21795                     newViewDate = this.moveYear(this.viewDate, dir);
21796                 } else if (e.shiftKey){
21797                     newDate = this.moveMonth(this.date, dir);
21798                     newViewDate = this.moveMonth(this.viewDate, dir);
21799                 } else {
21800                     newDate = new Date(this.date);
21801                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21802                     newViewDate = new Date(this.viewDate);
21803                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21804                 }
21805                 if (this.dateWithinRange(newDate)){
21806                     this.date = newDate;
21807                     this.viewDate = newViewDate;
21808                     this.setValue(this.formatDate(this.date));
21809 //                    this.update();
21810                     e.preventDefault();
21811                     dateChanged = true;
21812                 }
21813                 break;
21814             case 38: // up
21815             case 40: // down
21816                 if (!this.keyboardNavigation) {
21817                     break;
21818                 }
21819                 dir = e.keyCode == 38 ? -1 : 1;
21820                 if (e.ctrlKey){
21821                     newDate = this.moveYear(this.date, dir);
21822                     newViewDate = this.moveYear(this.viewDate, dir);
21823                 } else if (e.shiftKey){
21824                     newDate = this.moveMonth(this.date, dir);
21825                     newViewDate = this.moveMonth(this.viewDate, dir);
21826                 } else {
21827                     newDate = new Date(this.date);
21828                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21829                     newViewDate = new Date(this.viewDate);
21830                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21831                 }
21832                 if (this.dateWithinRange(newDate)){
21833                     this.date = newDate;
21834                     this.viewDate = newViewDate;
21835                     this.setValue(this.formatDate(this.date));
21836 //                    this.update();
21837                     e.preventDefault();
21838                     dateChanged = true;
21839                 }
21840                 break;
21841             case 13: // enter
21842                 this.setValue(this.formatDate(this.date));
21843                 this.hidePopup();
21844                 e.preventDefault();
21845                 break;
21846             case 9: // tab
21847                 this.setValue(this.formatDate(this.date));
21848                 this.hidePopup();
21849                 break;
21850             case 16: // shift
21851             case 17: // ctrl
21852             case 18: // alt
21853                 break;
21854             default :
21855                 this.hidePopup();
21856                 
21857         }
21858     },
21859     
21860     
21861     onClick: function(e) 
21862     {
21863         e.stopPropagation();
21864         e.preventDefault();
21865         
21866         var target = e.getTarget();
21867         
21868         if(target.nodeName.toLowerCase() === 'i'){
21869             target = Roo.get(target).dom.parentNode;
21870         }
21871         
21872         var nodeName = target.nodeName;
21873         var className = target.className;
21874         var html = target.innerHTML;
21875         //Roo.log(nodeName);
21876         
21877         switch(nodeName.toLowerCase()) {
21878             case 'th':
21879                 switch(className) {
21880                     case 'switch':
21881                         this.showMode(1);
21882                         break;
21883                     case 'prev':
21884                     case 'next':
21885                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21886                         switch(this.viewMode){
21887                                 case 0:
21888                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21889                                         break;
21890                                 case 1:
21891                                 case 2:
21892                                         this.viewDate = this.moveYear(this.viewDate, dir);
21893                                         break;
21894                         }
21895                         this.fill();
21896                         break;
21897                     case 'today':
21898                         var date = new Date();
21899                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21900 //                        this.fill()
21901                         this.setValue(this.formatDate(this.date));
21902                         
21903                         this.hidePopup();
21904                         break;
21905                 }
21906                 break;
21907             case 'span':
21908                 if (className.indexOf('disabled') < 0) {
21909                     this.viewDate.setUTCDate(1);
21910                     if (className.indexOf('month') > -1) {
21911                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21912                     } else {
21913                         var year = parseInt(html, 10) || 0;
21914                         this.viewDate.setUTCFullYear(year);
21915                         
21916                     }
21917                     
21918                     if(this.singleMode){
21919                         this.setValue(this.formatDate(this.viewDate));
21920                         this.hidePopup();
21921                         return;
21922                     }
21923                     
21924                     this.showMode(-1);
21925                     this.fill();
21926                 }
21927                 break;
21928                 
21929             case 'td':
21930                 //Roo.log(className);
21931                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21932                     var day = parseInt(html, 10) || 1;
21933                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21934                         month = (this.viewDate || new Date()).getUTCMonth();
21935
21936                     if (className.indexOf('old') > -1) {
21937                         if(month === 0 ){
21938                             month = 11;
21939                             year -= 1;
21940                         }else{
21941                             month -= 1;
21942                         }
21943                     } else if (className.indexOf('new') > -1) {
21944                         if (month == 11) {
21945                             month = 0;
21946                             year += 1;
21947                         } else {
21948                             month += 1;
21949                         }
21950                     }
21951                     //Roo.log([year,month,day]);
21952                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21953                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21954 //                    this.fill();
21955                     //Roo.log(this.formatDate(this.date));
21956                     this.setValue(this.formatDate(this.date));
21957                     this.hidePopup();
21958                 }
21959                 break;
21960         }
21961     },
21962     
21963     setStartDate: function(startDate)
21964     {
21965         this.startDate = startDate || -Infinity;
21966         if (this.startDate !== -Infinity) {
21967             this.startDate = this.parseDate(this.startDate);
21968         }
21969         this.update();
21970         this.updateNavArrows();
21971     },
21972
21973     setEndDate: function(endDate)
21974     {
21975         this.endDate = endDate || Infinity;
21976         if (this.endDate !== Infinity) {
21977             this.endDate = this.parseDate(this.endDate);
21978         }
21979         this.update();
21980         this.updateNavArrows();
21981     },
21982     
21983     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21984     {
21985         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21986         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21987             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21988         }
21989         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21990             return parseInt(d, 10);
21991         });
21992         this.update();
21993         this.updateNavArrows();
21994     },
21995     
21996     updateNavArrows: function() 
21997     {
21998         if(this.singleMode){
21999             return;
22000         }
22001         
22002         var d = new Date(this.viewDate),
22003         year = d.getUTCFullYear(),
22004         month = d.getUTCMonth();
22005         
22006         Roo.each(this.picker().select('.prev', true).elements, function(v){
22007             v.show();
22008             switch (this.viewMode) {
22009                 case 0:
22010
22011                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22012                         v.hide();
22013                     }
22014                     break;
22015                 case 1:
22016                 case 2:
22017                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22018                         v.hide();
22019                     }
22020                     break;
22021             }
22022         });
22023         
22024         Roo.each(this.picker().select('.next', true).elements, function(v){
22025             v.show();
22026             switch (this.viewMode) {
22027                 case 0:
22028
22029                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22030                         v.hide();
22031                     }
22032                     break;
22033                 case 1:
22034                 case 2:
22035                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22036                         v.hide();
22037                     }
22038                     break;
22039             }
22040         })
22041     },
22042     
22043     moveMonth: function(date, dir)
22044     {
22045         if (!dir) {
22046             return date;
22047         }
22048         var new_date = new Date(date.valueOf()),
22049         day = new_date.getUTCDate(),
22050         month = new_date.getUTCMonth(),
22051         mag = Math.abs(dir),
22052         new_month, test;
22053         dir = dir > 0 ? 1 : -1;
22054         if (mag == 1){
22055             test = dir == -1
22056             // If going back one month, make sure month is not current month
22057             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22058             ? function(){
22059                 return new_date.getUTCMonth() == month;
22060             }
22061             // If going forward one month, make sure month is as expected
22062             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22063             : function(){
22064                 return new_date.getUTCMonth() != new_month;
22065             };
22066             new_month = month + dir;
22067             new_date.setUTCMonth(new_month);
22068             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22069             if (new_month < 0 || new_month > 11) {
22070                 new_month = (new_month + 12) % 12;
22071             }
22072         } else {
22073             // For magnitudes >1, move one month at a time...
22074             for (var i=0; i<mag; i++) {
22075                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22076                 new_date = this.moveMonth(new_date, dir);
22077             }
22078             // ...then reset the day, keeping it in the new month
22079             new_month = new_date.getUTCMonth();
22080             new_date.setUTCDate(day);
22081             test = function(){
22082                 return new_month != new_date.getUTCMonth();
22083             };
22084         }
22085         // Common date-resetting loop -- if date is beyond end of month, make it
22086         // end of month
22087         while (test()){
22088             new_date.setUTCDate(--day);
22089             new_date.setUTCMonth(new_month);
22090         }
22091         return new_date;
22092     },
22093
22094     moveYear: function(date, dir)
22095     {
22096         return this.moveMonth(date, dir*12);
22097     },
22098
22099     dateWithinRange: function(date)
22100     {
22101         return date >= this.startDate && date <= this.endDate;
22102     },
22103
22104     
22105     remove: function() 
22106     {
22107         this.picker().remove();
22108     },
22109     
22110     validateValue : function(value)
22111     {
22112         if(this.getVisibilityEl().hasClass('hidden')){
22113             return true;
22114         }
22115         
22116         if(value.length < 1)  {
22117             if(this.allowBlank){
22118                 return true;
22119             }
22120             return false;
22121         }
22122         
22123         if(value.length < this.minLength){
22124             return false;
22125         }
22126         if(value.length > this.maxLength){
22127             return false;
22128         }
22129         if(this.vtype){
22130             var vt = Roo.form.VTypes;
22131             if(!vt[this.vtype](value, this)){
22132                 return false;
22133             }
22134         }
22135         if(typeof this.validator == "function"){
22136             var msg = this.validator(value);
22137             if(msg !== true){
22138                 return false;
22139             }
22140         }
22141         
22142         if(this.regex && !this.regex.test(value)){
22143             return false;
22144         }
22145         
22146         if(typeof(this.parseDate(value)) == 'undefined'){
22147             return false;
22148         }
22149         
22150         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22151             return false;
22152         }      
22153         
22154         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22155             return false;
22156         } 
22157         
22158         
22159         return true;
22160     },
22161     
22162     reset : function()
22163     {
22164         this.date = this.viewDate = '';
22165         
22166         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22167     }
22168    
22169 });
22170
22171 Roo.apply(Roo.bootstrap.DateField,  {
22172     
22173     head : {
22174         tag: 'thead',
22175         cn: [
22176         {
22177             tag: 'tr',
22178             cn: [
22179             {
22180                 tag: 'th',
22181                 cls: 'prev',
22182                 html: '<i class="fa fa-arrow-left"/>'
22183             },
22184             {
22185                 tag: 'th',
22186                 cls: 'switch',
22187                 colspan: '5'
22188             },
22189             {
22190                 tag: 'th',
22191                 cls: 'next',
22192                 html: '<i class="fa fa-arrow-right"/>'
22193             }
22194
22195             ]
22196         }
22197         ]
22198     },
22199     
22200     content : {
22201         tag: 'tbody',
22202         cn: [
22203         {
22204             tag: 'tr',
22205             cn: [
22206             {
22207                 tag: 'td',
22208                 colspan: '7'
22209             }
22210             ]
22211         }
22212         ]
22213     },
22214     
22215     footer : {
22216         tag: 'tfoot',
22217         cn: [
22218         {
22219             tag: 'tr',
22220             cn: [
22221             {
22222                 tag: 'th',
22223                 colspan: '7',
22224                 cls: 'today'
22225             }
22226                     
22227             ]
22228         }
22229         ]
22230     },
22231     
22232     dates:{
22233         en: {
22234             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22235             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22236             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22237             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22238             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22239             today: "Today"
22240         }
22241     },
22242     
22243     modes: [
22244     {
22245         clsName: 'days',
22246         navFnc: 'Month',
22247         navStep: 1
22248     },
22249     {
22250         clsName: 'months',
22251         navFnc: 'FullYear',
22252         navStep: 1
22253     },
22254     {
22255         clsName: 'years',
22256         navFnc: 'FullYear',
22257         navStep: 10
22258     }]
22259 });
22260
22261 Roo.apply(Roo.bootstrap.DateField,  {
22262   
22263     template : {
22264         tag: 'div',
22265         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22266         cn: [
22267         {
22268             tag: 'div',
22269             cls: 'datepicker-days',
22270             cn: [
22271             {
22272                 tag: 'table',
22273                 cls: 'table-condensed',
22274                 cn:[
22275                 Roo.bootstrap.DateField.head,
22276                 {
22277                     tag: 'tbody'
22278                 },
22279                 Roo.bootstrap.DateField.footer
22280                 ]
22281             }
22282             ]
22283         },
22284         {
22285             tag: 'div',
22286             cls: 'datepicker-months',
22287             cn: [
22288             {
22289                 tag: 'table',
22290                 cls: 'table-condensed',
22291                 cn:[
22292                 Roo.bootstrap.DateField.head,
22293                 Roo.bootstrap.DateField.content,
22294                 Roo.bootstrap.DateField.footer
22295                 ]
22296             }
22297             ]
22298         },
22299         {
22300             tag: 'div',
22301             cls: 'datepicker-years',
22302             cn: [
22303             {
22304                 tag: 'table',
22305                 cls: 'table-condensed',
22306                 cn:[
22307                 Roo.bootstrap.DateField.head,
22308                 Roo.bootstrap.DateField.content,
22309                 Roo.bootstrap.DateField.footer
22310                 ]
22311             }
22312             ]
22313         }
22314         ]
22315     }
22316 });
22317
22318  
22319
22320  /*
22321  * - LGPL
22322  *
22323  * TimeField
22324  * 
22325  */
22326
22327 /**
22328  * @class Roo.bootstrap.TimeField
22329  * @extends Roo.bootstrap.Input
22330  * Bootstrap DateField class
22331  * 
22332  * 
22333  * @constructor
22334  * Create a new TimeField
22335  * @param {Object} config The config object
22336  */
22337
22338 Roo.bootstrap.TimeField = function(config){
22339     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22340     this.addEvents({
22341             /**
22342              * @event show
22343              * Fires when this field show.
22344              * @param {Roo.bootstrap.DateField} thisthis
22345              * @param {Mixed} date The date value
22346              */
22347             show : true,
22348             /**
22349              * @event show
22350              * Fires when this field hide.
22351              * @param {Roo.bootstrap.DateField} this
22352              * @param {Mixed} date The date value
22353              */
22354             hide : true,
22355             /**
22356              * @event select
22357              * Fires when select a date.
22358              * @param {Roo.bootstrap.DateField} this
22359              * @param {Mixed} date The date value
22360              */
22361             select : true
22362         });
22363 };
22364
22365 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22366     
22367     /**
22368      * @cfg {String} format
22369      * The default time format string which can be overriden for localization support.  The format must be
22370      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22371      */
22372     format : "H:i",
22373
22374     getAutoCreate : function()
22375     {
22376         this.after = '<i class="fa far fa-clock"></i>';
22377         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22378         
22379          
22380     },
22381     onRender: function(ct, position)
22382     {
22383         
22384         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22385                 
22386         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22387         
22388         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22389         
22390         this.pop = this.picker().select('>.datepicker-time',true).first();
22391         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22392         
22393         this.picker().on('mousedown', this.onMousedown, this);
22394         this.picker().on('click', this.onClick, this);
22395         
22396         this.picker().addClass('datepicker-dropdown');
22397     
22398         this.fillTime();
22399         this.update();
22400             
22401         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22402         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22403         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22404         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22405         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22406         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22407
22408     },
22409     
22410     fireKey: function(e){
22411         if (!this.picker().isVisible()){
22412             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22413                 this.show();
22414             }
22415             return;
22416         }
22417
22418         e.preventDefault();
22419         
22420         switch(e.keyCode){
22421             case 27: // escape
22422                 this.hide();
22423                 break;
22424             case 37: // left
22425             case 39: // right
22426                 this.onTogglePeriod();
22427                 break;
22428             case 38: // up
22429                 this.onIncrementMinutes();
22430                 break;
22431             case 40: // down
22432                 this.onDecrementMinutes();
22433                 break;
22434             case 13: // enter
22435             case 9: // tab
22436                 this.setTime();
22437                 break;
22438         }
22439     },
22440     
22441     onClick: function(e) {
22442         e.stopPropagation();
22443         e.preventDefault();
22444     },
22445     
22446     picker : function()
22447     {
22448         return this.pickerEl;
22449     },
22450     
22451     fillTime: function()
22452     {    
22453         var time = this.pop.select('tbody', true).first();
22454         
22455         time.dom.innerHTML = '';
22456         
22457         time.createChild({
22458             tag: 'tr',
22459             cn: [
22460                 {
22461                     tag: 'td',
22462                     cn: [
22463                         {
22464                             tag: 'a',
22465                             href: '#',
22466                             cls: 'btn',
22467                             cn: [
22468                                 {
22469                                     tag: 'i',
22470                                     cls: 'hours-up fa fas fa-chevron-up'
22471                                 }
22472                             ]
22473                         } 
22474                     ]
22475                 },
22476                 {
22477                     tag: 'td',
22478                     cls: 'separator'
22479                 },
22480                 {
22481                     tag: 'td',
22482                     cn: [
22483                         {
22484                             tag: 'a',
22485                             href: '#',
22486                             cls: 'btn',
22487                             cn: [
22488                                 {
22489                                     tag: 'i',
22490                                     cls: 'minutes-up fa fas fa-chevron-up'
22491                                 }
22492                             ]
22493                         }
22494                     ]
22495                 },
22496                 {
22497                     tag: 'td',
22498                     cls: 'separator'
22499                 }
22500             ]
22501         });
22502         
22503         time.createChild({
22504             tag: 'tr',
22505             cn: [
22506                 {
22507                     tag: 'td',
22508                     cn: [
22509                         {
22510                             tag: 'span',
22511                             cls: 'timepicker-hour',
22512                             html: '00'
22513                         }  
22514                     ]
22515                 },
22516                 {
22517                     tag: 'td',
22518                     cls: 'separator',
22519                     html: ':'
22520                 },
22521                 {
22522                     tag: 'td',
22523                     cn: [
22524                         {
22525                             tag: 'span',
22526                             cls: 'timepicker-minute',
22527                             html: '00'
22528                         }  
22529                     ]
22530                 },
22531                 {
22532                     tag: 'td',
22533                     cls: 'separator'
22534                 },
22535                 {
22536                     tag: 'td',
22537                     cn: [
22538                         {
22539                             tag: 'button',
22540                             type: 'button',
22541                             cls: 'btn btn-primary period',
22542                             html: 'AM'
22543                             
22544                         }
22545                     ]
22546                 }
22547             ]
22548         });
22549         
22550         time.createChild({
22551             tag: 'tr',
22552             cn: [
22553                 {
22554                     tag: 'td',
22555                     cn: [
22556                         {
22557                             tag: 'a',
22558                             href: '#',
22559                             cls: 'btn',
22560                             cn: [
22561                                 {
22562                                     tag: 'span',
22563                                     cls: 'hours-down fa fas fa-chevron-down'
22564                                 }
22565                             ]
22566                         }
22567                     ]
22568                 },
22569                 {
22570                     tag: 'td',
22571                     cls: 'separator'
22572                 },
22573                 {
22574                     tag: 'td',
22575                     cn: [
22576                         {
22577                             tag: 'a',
22578                             href: '#',
22579                             cls: 'btn',
22580                             cn: [
22581                                 {
22582                                     tag: 'span',
22583                                     cls: 'minutes-down fa fas fa-chevron-down'
22584                                 }
22585                             ]
22586                         }
22587                     ]
22588                 },
22589                 {
22590                     tag: 'td',
22591                     cls: 'separator'
22592                 }
22593             ]
22594         });
22595         
22596     },
22597     
22598     update: function()
22599     {
22600         
22601         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22602         
22603         this.fill();
22604     },
22605     
22606     fill: function() 
22607     {
22608         var hours = this.time.getHours();
22609         var minutes = this.time.getMinutes();
22610         var period = 'AM';
22611         
22612         if(hours > 11){
22613             period = 'PM';
22614         }
22615         
22616         if(hours == 0){
22617             hours = 12;
22618         }
22619         
22620         
22621         if(hours > 12){
22622             hours = hours - 12;
22623         }
22624         
22625         if(hours < 10){
22626             hours = '0' + hours;
22627         }
22628         
22629         if(minutes < 10){
22630             minutes = '0' + minutes;
22631         }
22632         
22633         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22634         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22635         this.pop.select('button', true).first().dom.innerHTML = period;
22636         
22637     },
22638     
22639     place: function()
22640     {   
22641         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22642         
22643         var cls = ['bottom'];
22644         
22645         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22646             cls.pop();
22647             cls.push('top');
22648         }
22649         
22650         cls.push('right');
22651         
22652         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22653             cls.pop();
22654             cls.push('left');
22655         }
22656         //this.picker().setXY(20000,20000);
22657         this.picker().addClass(cls.join('-'));
22658         
22659         var _this = this;
22660         
22661         Roo.each(cls, function(c){
22662             if(c == 'bottom'){
22663                 (function() {
22664                  //  
22665                 }).defer(200);
22666                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22667                 //_this.picker().setTop(_this.inputEl().getHeight());
22668                 return;
22669             }
22670             if(c == 'top'){
22671                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22672                 
22673                 //_this.picker().setTop(0 - _this.picker().getHeight());
22674                 return;
22675             }
22676             /*
22677             if(c == 'left'){
22678                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22679                 return;
22680             }
22681             if(c == 'right'){
22682                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22683                 return;
22684             }
22685             */
22686         });
22687         
22688     },
22689   
22690     onFocus : function()
22691     {
22692         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22693         this.show();
22694     },
22695     
22696     onBlur : function()
22697     {
22698         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22699         this.hide();
22700     },
22701     
22702     show : function()
22703     {
22704         this.picker().show();
22705         this.pop.show();
22706         this.update();
22707         this.place();
22708         
22709         this.fireEvent('show', this, this.date);
22710     },
22711     
22712     hide : function()
22713     {
22714         this.picker().hide();
22715         this.pop.hide();
22716         
22717         this.fireEvent('hide', this, this.date);
22718     },
22719     
22720     setTime : function()
22721     {
22722         this.hide();
22723         this.setValue(this.time.format(this.format));
22724         
22725         this.fireEvent('select', this, this.date);
22726         
22727         
22728     },
22729     
22730     onMousedown: function(e){
22731         e.stopPropagation();
22732         e.preventDefault();
22733     },
22734     
22735     onIncrementHours: function()
22736     {
22737         Roo.log('onIncrementHours');
22738         this.time = this.time.add(Date.HOUR, 1);
22739         this.update();
22740         
22741     },
22742     
22743     onDecrementHours: function()
22744     {
22745         Roo.log('onDecrementHours');
22746         this.time = this.time.add(Date.HOUR, -1);
22747         this.update();
22748     },
22749     
22750     onIncrementMinutes: function()
22751     {
22752         Roo.log('onIncrementMinutes');
22753         this.time = this.time.add(Date.MINUTE, 1);
22754         this.update();
22755     },
22756     
22757     onDecrementMinutes: function()
22758     {
22759         Roo.log('onDecrementMinutes');
22760         this.time = this.time.add(Date.MINUTE, -1);
22761         this.update();
22762     },
22763     
22764     onTogglePeriod: function()
22765     {
22766         Roo.log('onTogglePeriod');
22767         this.time = this.time.add(Date.HOUR, 12);
22768         this.update();
22769     }
22770     
22771    
22772 });
22773  
22774
22775 Roo.apply(Roo.bootstrap.TimeField,  {
22776   
22777     template : {
22778         tag: 'div',
22779         cls: 'datepicker dropdown-menu',
22780         cn: [
22781             {
22782                 tag: 'div',
22783                 cls: 'datepicker-time',
22784                 cn: [
22785                 {
22786                     tag: 'table',
22787                     cls: 'table-condensed',
22788                     cn:[
22789                         {
22790                             tag: 'tbody',
22791                             cn: [
22792                                 {
22793                                     tag: 'tr',
22794                                     cn: [
22795                                     {
22796                                         tag: 'td',
22797                                         colspan: '7'
22798                                     }
22799                                     ]
22800                                 }
22801                             ]
22802                         },
22803                         {
22804                             tag: 'tfoot',
22805                             cn: [
22806                                 {
22807                                     tag: 'tr',
22808                                     cn: [
22809                                     {
22810                                         tag: 'th',
22811                                         colspan: '7',
22812                                         cls: '',
22813                                         cn: [
22814                                             {
22815                                                 tag: 'button',
22816                                                 cls: 'btn btn-info ok',
22817                                                 html: 'OK'
22818                                             }
22819                                         ]
22820                                     }
22821                     
22822                                     ]
22823                                 }
22824                             ]
22825                         }
22826                     ]
22827                 }
22828                 ]
22829             }
22830         ]
22831     }
22832 });
22833
22834  
22835
22836  /*
22837  * - LGPL
22838  *
22839  * MonthField
22840  * 
22841  */
22842
22843 /**
22844  * @class Roo.bootstrap.MonthField
22845  * @extends Roo.bootstrap.Input
22846  * Bootstrap MonthField class
22847  * 
22848  * @cfg {String} language default en
22849  * 
22850  * @constructor
22851  * Create a new MonthField
22852  * @param {Object} config The config object
22853  */
22854
22855 Roo.bootstrap.MonthField = function(config){
22856     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22857     
22858     this.addEvents({
22859         /**
22860          * @event show
22861          * Fires when this field show.
22862          * @param {Roo.bootstrap.MonthField} this
22863          * @param {Mixed} date The date value
22864          */
22865         show : true,
22866         /**
22867          * @event show
22868          * Fires when this field hide.
22869          * @param {Roo.bootstrap.MonthField} this
22870          * @param {Mixed} date The date value
22871          */
22872         hide : true,
22873         /**
22874          * @event select
22875          * Fires when select a date.
22876          * @param {Roo.bootstrap.MonthField} this
22877          * @param {String} oldvalue The old value
22878          * @param {String} newvalue The new value
22879          */
22880         select : true
22881     });
22882 };
22883
22884 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22885     
22886     onRender: function(ct, position)
22887     {
22888         
22889         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22890         
22891         this.language = this.language || 'en';
22892         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22893         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22894         
22895         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22896         this.isInline = false;
22897         this.isInput = true;
22898         this.component = this.el.select('.add-on', true).first() || false;
22899         this.component = (this.component && this.component.length === 0) ? false : this.component;
22900         this.hasInput = this.component && this.inputEL().length;
22901         
22902         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22903         
22904         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22905         
22906         this.picker().on('mousedown', this.onMousedown, this);
22907         this.picker().on('click', this.onClick, this);
22908         
22909         this.picker().addClass('datepicker-dropdown');
22910         
22911         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22912             v.setStyle('width', '189px');
22913         });
22914         
22915         this.fillMonths();
22916         
22917         this.update();
22918         
22919         if(this.isInline) {
22920             this.show();
22921         }
22922         
22923     },
22924     
22925     setValue: function(v, suppressEvent)
22926     {   
22927         var o = this.getValue();
22928         
22929         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22930         
22931         this.update();
22932
22933         if(suppressEvent !== true){
22934             this.fireEvent('select', this, o, v);
22935         }
22936         
22937     },
22938     
22939     getValue: function()
22940     {
22941         return this.value;
22942     },
22943     
22944     onClick: function(e) 
22945     {
22946         e.stopPropagation();
22947         e.preventDefault();
22948         
22949         var target = e.getTarget();
22950         
22951         if(target.nodeName.toLowerCase() === 'i'){
22952             target = Roo.get(target).dom.parentNode;
22953         }
22954         
22955         var nodeName = target.nodeName;
22956         var className = target.className;
22957         var html = target.innerHTML;
22958         
22959         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22960             return;
22961         }
22962         
22963         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22964         
22965         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22966         
22967         this.hide();
22968                         
22969     },
22970     
22971     picker : function()
22972     {
22973         return this.pickerEl;
22974     },
22975     
22976     fillMonths: function()
22977     {    
22978         var i = 0;
22979         var months = this.picker().select('>.datepicker-months td', true).first();
22980         
22981         months.dom.innerHTML = '';
22982         
22983         while (i < 12) {
22984             var month = {
22985                 tag: 'span',
22986                 cls: 'month',
22987                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22988             };
22989             
22990             months.createChild(month);
22991         }
22992         
22993     },
22994     
22995     update: function()
22996     {
22997         var _this = this;
22998         
22999         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23000             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23001         }
23002         
23003         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23004             e.removeClass('active');
23005             
23006             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23007                 e.addClass('active');
23008             }
23009         })
23010     },
23011     
23012     place: function()
23013     {
23014         if(this.isInline) {
23015             return;
23016         }
23017         
23018         this.picker().removeClass(['bottom', 'top']);
23019         
23020         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23021             /*
23022              * place to the top of element!
23023              *
23024              */
23025             
23026             this.picker().addClass('top');
23027             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23028             
23029             return;
23030         }
23031         
23032         this.picker().addClass('bottom');
23033         
23034         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23035     },
23036     
23037     onFocus : function()
23038     {
23039         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23040         this.show();
23041     },
23042     
23043     onBlur : function()
23044     {
23045         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23046         
23047         var d = this.inputEl().getValue();
23048         
23049         this.setValue(d);
23050                 
23051         this.hide();
23052     },
23053     
23054     show : function()
23055     {
23056         this.picker().show();
23057         this.picker().select('>.datepicker-months', true).first().show();
23058         this.update();
23059         this.place();
23060         
23061         this.fireEvent('show', this, this.date);
23062     },
23063     
23064     hide : function()
23065     {
23066         if(this.isInline) {
23067             return;
23068         }
23069         this.picker().hide();
23070         this.fireEvent('hide', this, this.date);
23071         
23072     },
23073     
23074     onMousedown: function(e)
23075     {
23076         e.stopPropagation();
23077         e.preventDefault();
23078     },
23079     
23080     keyup: function(e)
23081     {
23082         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23083         this.update();
23084     },
23085
23086     fireKey: function(e)
23087     {
23088         if (!this.picker().isVisible()){
23089             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23090                 this.show();
23091             }
23092             return;
23093         }
23094         
23095         var dir;
23096         
23097         switch(e.keyCode){
23098             case 27: // escape
23099                 this.hide();
23100                 e.preventDefault();
23101                 break;
23102             case 37: // left
23103             case 39: // right
23104                 dir = e.keyCode == 37 ? -1 : 1;
23105                 
23106                 this.vIndex = this.vIndex + dir;
23107                 
23108                 if(this.vIndex < 0){
23109                     this.vIndex = 0;
23110                 }
23111                 
23112                 if(this.vIndex > 11){
23113                     this.vIndex = 11;
23114                 }
23115                 
23116                 if(isNaN(this.vIndex)){
23117                     this.vIndex = 0;
23118                 }
23119                 
23120                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23121                 
23122                 break;
23123             case 38: // up
23124             case 40: // down
23125                 
23126                 dir = e.keyCode == 38 ? -1 : 1;
23127                 
23128                 this.vIndex = this.vIndex + dir * 4;
23129                 
23130                 if(this.vIndex < 0){
23131                     this.vIndex = 0;
23132                 }
23133                 
23134                 if(this.vIndex > 11){
23135                     this.vIndex = 11;
23136                 }
23137                 
23138                 if(isNaN(this.vIndex)){
23139                     this.vIndex = 0;
23140                 }
23141                 
23142                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23143                 break;
23144                 
23145             case 13: // enter
23146                 
23147                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23148                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23149                 }
23150                 
23151                 this.hide();
23152                 e.preventDefault();
23153                 break;
23154             case 9: // tab
23155                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23156                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23157                 }
23158                 this.hide();
23159                 break;
23160             case 16: // shift
23161             case 17: // ctrl
23162             case 18: // alt
23163                 break;
23164             default :
23165                 this.hide();
23166                 
23167         }
23168     },
23169     
23170     remove: function() 
23171     {
23172         this.picker().remove();
23173     }
23174    
23175 });
23176
23177 Roo.apply(Roo.bootstrap.MonthField,  {
23178     
23179     content : {
23180         tag: 'tbody',
23181         cn: [
23182         {
23183             tag: 'tr',
23184             cn: [
23185             {
23186                 tag: 'td',
23187                 colspan: '7'
23188             }
23189             ]
23190         }
23191         ]
23192     },
23193     
23194     dates:{
23195         en: {
23196             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23197             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23198         }
23199     }
23200 });
23201
23202 Roo.apply(Roo.bootstrap.MonthField,  {
23203   
23204     template : {
23205         tag: 'div',
23206         cls: 'datepicker dropdown-menu roo-dynamic',
23207         cn: [
23208             {
23209                 tag: 'div',
23210                 cls: 'datepicker-months',
23211                 cn: [
23212                 {
23213                     tag: 'table',
23214                     cls: 'table-condensed',
23215                     cn:[
23216                         Roo.bootstrap.DateField.content
23217                     ]
23218                 }
23219                 ]
23220             }
23221         ]
23222     }
23223 });
23224
23225  
23226
23227  
23228  /*
23229  * - LGPL
23230  *
23231  * CheckBox
23232  * 
23233  */
23234
23235 /**
23236  * @class Roo.bootstrap.CheckBox
23237  * @extends Roo.bootstrap.Input
23238  * Bootstrap CheckBox class
23239  * 
23240  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23241  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23242  * @cfg {String} boxLabel The text that appears beside the checkbox
23243  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23244  * @cfg {Boolean} checked initnal the element
23245  * @cfg {Boolean} inline inline the element (default false)
23246  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23247  * @cfg {String} tooltip label tooltip
23248  * 
23249  * @constructor
23250  * Create a new CheckBox
23251  * @param {Object} config The config object
23252  */
23253
23254 Roo.bootstrap.CheckBox = function(config){
23255     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23256    
23257     this.addEvents({
23258         /**
23259         * @event check
23260         * Fires when the element is checked or unchecked.
23261         * @param {Roo.bootstrap.CheckBox} this This input
23262         * @param {Boolean} checked The new checked value
23263         */
23264        check : true,
23265        /**
23266         * @event click
23267         * Fires when the element is click.
23268         * @param {Roo.bootstrap.CheckBox} this This input
23269         */
23270        click : true
23271     });
23272     
23273 };
23274
23275 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23276   
23277     inputType: 'checkbox',
23278     inputValue: 1,
23279     valueOff: 0,
23280     boxLabel: false,
23281     checked: false,
23282     weight : false,
23283     inline: false,
23284     tooltip : '',
23285     
23286     // checkbox success does not make any sense really.. 
23287     invalidClass : "",
23288     validClass : "",
23289     
23290     
23291     getAutoCreate : function()
23292     {
23293         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23294         
23295         var id = Roo.id();
23296         
23297         var cfg = {};
23298         
23299         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23300         
23301         if(this.inline){
23302             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23303         }
23304         
23305         var input =  {
23306             tag: 'input',
23307             id : id,
23308             type : this.inputType,
23309             value : this.inputValue,
23310             cls : 'roo-' + this.inputType, //'form-box',
23311             placeholder : this.placeholder || ''
23312             
23313         };
23314         
23315         if(this.inputType != 'radio'){
23316             var hidden =  {
23317                 tag: 'input',
23318                 type : 'hidden',
23319                 cls : 'roo-hidden-value',
23320                 value : this.checked ? this.inputValue : this.valueOff
23321             };
23322         }
23323         
23324             
23325         if (this.weight) { // Validity check?
23326             cfg.cls += " " + this.inputType + "-" + this.weight;
23327         }
23328         
23329         if (this.disabled) {
23330             input.disabled=true;
23331         }
23332         
23333         if(this.checked){
23334             input.checked = this.checked;
23335         }
23336         
23337         if (this.name) {
23338             
23339             input.name = this.name;
23340             
23341             if(this.inputType != 'radio'){
23342                 hidden.name = this.name;
23343                 input.name = '_hidden_' + this.name;
23344             }
23345         }
23346         
23347         if (this.size) {
23348             input.cls += ' input-' + this.size;
23349         }
23350         
23351         var settings=this;
23352         
23353         ['xs','sm','md','lg'].map(function(size){
23354             if (settings[size]) {
23355                 cfg.cls += ' col-' + size + '-' + settings[size];
23356             }
23357         });
23358         
23359         var inputblock = input;
23360          
23361         if (this.before || this.after) {
23362             
23363             inputblock = {
23364                 cls : 'input-group',
23365                 cn :  [] 
23366             };
23367             
23368             if (this.before) {
23369                 inputblock.cn.push({
23370                     tag :'span',
23371                     cls : 'input-group-addon',
23372                     html : this.before
23373                 });
23374             }
23375             
23376             inputblock.cn.push(input);
23377             
23378             if(this.inputType != 'radio'){
23379                 inputblock.cn.push(hidden);
23380             }
23381             
23382             if (this.after) {
23383                 inputblock.cn.push({
23384                     tag :'span',
23385                     cls : 'input-group-addon',
23386                     html : this.after
23387                 });
23388             }
23389             
23390         }
23391         var boxLabelCfg = false;
23392         
23393         if(this.boxLabel){
23394            
23395             boxLabelCfg = {
23396                 tag: 'label',
23397                 //'for': id, // box label is handled by onclick - so no for...
23398                 cls: 'box-label',
23399                 html: this.boxLabel
23400             };
23401             if(this.tooltip){
23402                 boxLabelCfg.tooltip = this.tooltip;
23403             }
23404              
23405         }
23406         
23407         
23408         if (align ==='left' && this.fieldLabel.length) {
23409 //                Roo.log("left and has label");
23410             cfg.cn = [
23411                 {
23412                     tag: 'label',
23413                     'for' :  id,
23414                     cls : 'control-label',
23415                     html : this.fieldLabel
23416                 },
23417                 {
23418                     cls : "", 
23419                     cn: [
23420                         inputblock
23421                     ]
23422                 }
23423             ];
23424             
23425             if (boxLabelCfg) {
23426                 cfg.cn[1].cn.push(boxLabelCfg);
23427             }
23428             
23429             if(this.labelWidth > 12){
23430                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23431             }
23432             
23433             if(this.labelWidth < 13 && this.labelmd == 0){
23434                 this.labelmd = this.labelWidth;
23435             }
23436             
23437             if(this.labellg > 0){
23438                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23439                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23440             }
23441             
23442             if(this.labelmd > 0){
23443                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23444                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23445             }
23446             
23447             if(this.labelsm > 0){
23448                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23449                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23450             }
23451             
23452             if(this.labelxs > 0){
23453                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23454                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23455             }
23456             
23457         } else if ( this.fieldLabel.length) {
23458 //                Roo.log(" label");
23459                 cfg.cn = [
23460                    
23461                     {
23462                         tag: this.boxLabel ? 'span' : 'label',
23463                         'for': id,
23464                         cls: 'control-label box-input-label',
23465                         //cls : 'input-group-addon',
23466                         html : this.fieldLabel
23467                     },
23468                     
23469                     inputblock
23470                     
23471                 ];
23472                 if (boxLabelCfg) {
23473                     cfg.cn.push(boxLabelCfg);
23474                 }
23475
23476         } else {
23477             
23478 //                Roo.log(" no label && no align");
23479                 cfg.cn = [  inputblock ] ;
23480                 if (boxLabelCfg) {
23481                     cfg.cn.push(boxLabelCfg);
23482                 }
23483
23484                 
23485         }
23486         
23487        
23488         
23489         if(this.inputType != 'radio'){
23490             cfg.cn.push(hidden);
23491         }
23492         
23493         return cfg;
23494         
23495     },
23496     
23497     /**
23498      * return the real input element.
23499      */
23500     inputEl: function ()
23501     {
23502         return this.el.select('input.roo-' + this.inputType,true).first();
23503     },
23504     hiddenEl: function ()
23505     {
23506         return this.el.select('input.roo-hidden-value',true).first();
23507     },
23508     
23509     labelEl: function()
23510     {
23511         return this.el.select('label.control-label',true).first();
23512     },
23513     /* depricated... */
23514     
23515     label: function()
23516     {
23517         return this.labelEl();
23518     },
23519     
23520     boxLabelEl: function()
23521     {
23522         return this.el.select('label.box-label',true).first();
23523     },
23524     
23525     initEvents : function()
23526     {
23527 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23528         
23529         this.inputEl().on('click', this.onClick,  this);
23530         
23531         if (this.boxLabel) { 
23532             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23533         }
23534         
23535         this.startValue = this.getValue();
23536         
23537         if(this.groupId){
23538             Roo.bootstrap.CheckBox.register(this);
23539         }
23540     },
23541     
23542     onClick : function(e)
23543     {   
23544         if(this.fireEvent('click', this, e) !== false){
23545             this.setChecked(!this.checked);
23546         }
23547         
23548     },
23549     
23550     setChecked : function(state,suppressEvent)
23551     {
23552         this.startValue = this.getValue();
23553
23554         if(this.inputType == 'radio'){
23555             
23556             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23557                 e.dom.checked = false;
23558             });
23559             
23560             this.inputEl().dom.checked = true;
23561             
23562             this.inputEl().dom.value = this.inputValue;
23563             
23564             if(suppressEvent !== true){
23565                 this.fireEvent('check', this, true);
23566             }
23567             
23568             this.validate();
23569             
23570             return;
23571         }
23572         
23573         this.checked = state;
23574         
23575         this.inputEl().dom.checked = state;
23576         
23577         
23578         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23579         
23580         if(suppressEvent !== true){
23581             this.fireEvent('check', this, state);
23582         }
23583         
23584         this.validate();
23585     },
23586     
23587     getValue : function()
23588     {
23589         if(this.inputType == 'radio'){
23590             return this.getGroupValue();
23591         }
23592         
23593         return this.hiddenEl().dom.value;
23594         
23595     },
23596     
23597     getGroupValue : function()
23598     {
23599         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23600             return '';
23601         }
23602         
23603         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23604     },
23605     
23606     setValue : function(v,suppressEvent)
23607     {
23608         if(this.inputType == 'radio'){
23609             this.setGroupValue(v, suppressEvent);
23610             return;
23611         }
23612         
23613         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23614         
23615         this.validate();
23616     },
23617     
23618     setGroupValue : function(v, suppressEvent)
23619     {
23620         this.startValue = this.getValue();
23621         
23622         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23623             e.dom.checked = false;
23624             
23625             if(e.dom.value == v){
23626                 e.dom.checked = true;
23627             }
23628         });
23629         
23630         if(suppressEvent !== true){
23631             this.fireEvent('check', this, true);
23632         }
23633
23634         this.validate();
23635         
23636         return;
23637     },
23638     
23639     validate : function()
23640     {
23641         if(this.getVisibilityEl().hasClass('hidden')){
23642             return true;
23643         }
23644         
23645         if(
23646                 this.disabled || 
23647                 (this.inputType == 'radio' && this.validateRadio()) ||
23648                 (this.inputType == 'checkbox' && this.validateCheckbox())
23649         ){
23650             this.markValid();
23651             return true;
23652         }
23653         
23654         this.markInvalid();
23655         return false;
23656     },
23657     
23658     validateRadio : function()
23659     {
23660         if(this.getVisibilityEl().hasClass('hidden')){
23661             return true;
23662         }
23663         
23664         if(this.allowBlank){
23665             return true;
23666         }
23667         
23668         var valid = false;
23669         
23670         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23671             if(!e.dom.checked){
23672                 return;
23673             }
23674             
23675             valid = true;
23676             
23677             return false;
23678         });
23679         
23680         return valid;
23681     },
23682     
23683     validateCheckbox : function()
23684     {
23685         if(!this.groupId){
23686             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23687             //return (this.getValue() == this.inputValue) ? true : false;
23688         }
23689         
23690         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23691         
23692         if(!group){
23693             return false;
23694         }
23695         
23696         var r = false;
23697         
23698         for(var i in group){
23699             if(group[i].el.isVisible(true)){
23700                 r = false;
23701                 break;
23702             }
23703             
23704             r = true;
23705         }
23706         
23707         for(var i in group){
23708             if(r){
23709                 break;
23710             }
23711             
23712             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23713         }
23714         
23715         return r;
23716     },
23717     
23718     /**
23719      * Mark this field as valid
23720      */
23721     markValid : function()
23722     {
23723         var _this = this;
23724         
23725         this.fireEvent('valid', this);
23726         
23727         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23728         
23729         if(this.groupId){
23730             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23731         }
23732         
23733         if(label){
23734             label.markValid();
23735         }
23736
23737         if(this.inputType == 'radio'){
23738             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23739                 var fg = e.findParent('.form-group', false, true);
23740                 if (Roo.bootstrap.version == 3) {
23741                     fg.removeClass([_this.invalidClass, _this.validClass]);
23742                     fg.addClass(_this.validClass);
23743                 } else {
23744                     fg.removeClass(['is-valid', 'is-invalid']);
23745                     fg.addClass('is-valid');
23746                 }
23747             });
23748             
23749             return;
23750         }
23751
23752         if(!this.groupId){
23753             var fg = this.el.findParent('.form-group', false, true);
23754             if (Roo.bootstrap.version == 3) {
23755                 fg.removeClass([this.invalidClass, this.validClass]);
23756                 fg.addClass(this.validClass);
23757             } else {
23758                 fg.removeClass(['is-valid', 'is-invalid']);
23759                 fg.addClass('is-valid');
23760             }
23761             return;
23762         }
23763         
23764         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23765         
23766         if(!group){
23767             return;
23768         }
23769         
23770         for(var i in group){
23771             var fg = group[i].el.findParent('.form-group', false, true);
23772             if (Roo.bootstrap.version == 3) {
23773                 fg.removeClass([this.invalidClass, this.validClass]);
23774                 fg.addClass(this.validClass);
23775             } else {
23776                 fg.removeClass(['is-valid', 'is-invalid']);
23777                 fg.addClass('is-valid');
23778             }
23779         }
23780     },
23781     
23782      /**
23783      * Mark this field as invalid
23784      * @param {String} msg The validation message
23785      */
23786     markInvalid : function(msg)
23787     {
23788         if(this.allowBlank){
23789             return;
23790         }
23791         
23792         var _this = this;
23793         
23794         this.fireEvent('invalid', this, msg);
23795         
23796         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23797         
23798         if(this.groupId){
23799             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23800         }
23801         
23802         if(label){
23803             label.markInvalid();
23804         }
23805             
23806         if(this.inputType == 'radio'){
23807             
23808             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23809                 var fg = e.findParent('.form-group', false, true);
23810                 if (Roo.bootstrap.version == 3) {
23811                     fg.removeClass([_this.invalidClass, _this.validClass]);
23812                     fg.addClass(_this.invalidClass);
23813                 } else {
23814                     fg.removeClass(['is-invalid', 'is-valid']);
23815                     fg.addClass('is-invalid');
23816                 }
23817             });
23818             
23819             return;
23820         }
23821         
23822         if(!this.groupId){
23823             var fg = this.el.findParent('.form-group', false, true);
23824             if (Roo.bootstrap.version == 3) {
23825                 fg.removeClass([_this.invalidClass, _this.validClass]);
23826                 fg.addClass(_this.invalidClass);
23827             } else {
23828                 fg.removeClass(['is-invalid', 'is-valid']);
23829                 fg.addClass('is-invalid');
23830             }
23831             return;
23832         }
23833         
23834         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23835         
23836         if(!group){
23837             return;
23838         }
23839         
23840         for(var i in group){
23841             var fg = group[i].el.findParent('.form-group', false, true);
23842             if (Roo.bootstrap.version == 3) {
23843                 fg.removeClass([_this.invalidClass, _this.validClass]);
23844                 fg.addClass(_this.invalidClass);
23845             } else {
23846                 fg.removeClass(['is-invalid', 'is-valid']);
23847                 fg.addClass('is-invalid');
23848             }
23849         }
23850         
23851     },
23852     
23853     clearInvalid : function()
23854     {
23855         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23856         
23857         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23858         
23859         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23860         
23861         if (label && label.iconEl) {
23862             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23863             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23864         }
23865     },
23866     
23867     disable : function()
23868     {
23869         if(this.inputType != 'radio'){
23870             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23871             return;
23872         }
23873         
23874         var _this = this;
23875         
23876         if(this.rendered){
23877             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23878                 _this.getActionEl().addClass(this.disabledClass);
23879                 e.dom.disabled = true;
23880             });
23881         }
23882         
23883         this.disabled = true;
23884         this.fireEvent("disable", this);
23885         return this;
23886     },
23887
23888     enable : function()
23889     {
23890         if(this.inputType != 'radio'){
23891             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23892             return;
23893         }
23894         
23895         var _this = this;
23896         
23897         if(this.rendered){
23898             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23899                 _this.getActionEl().removeClass(this.disabledClass);
23900                 e.dom.disabled = false;
23901             });
23902         }
23903         
23904         this.disabled = false;
23905         this.fireEvent("enable", this);
23906         return this;
23907     },
23908     
23909     setBoxLabel : function(v)
23910     {
23911         this.boxLabel = v;
23912         
23913         if(this.rendered){
23914             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23915         }
23916     }
23917
23918 });
23919
23920 Roo.apply(Roo.bootstrap.CheckBox, {
23921     
23922     groups: {},
23923     
23924      /**
23925     * register a CheckBox Group
23926     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23927     */
23928     register : function(checkbox)
23929     {
23930         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23931             this.groups[checkbox.groupId] = {};
23932         }
23933         
23934         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23935             return;
23936         }
23937         
23938         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23939         
23940     },
23941     /**
23942     * fetch a CheckBox Group based on the group ID
23943     * @param {string} the group ID
23944     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23945     */
23946     get: function(groupId) {
23947         if (typeof(this.groups[groupId]) == 'undefined') {
23948             return false;
23949         }
23950         
23951         return this.groups[groupId] ;
23952     }
23953     
23954     
23955 });
23956 /*
23957  * - LGPL
23958  *
23959  * RadioItem
23960  * 
23961  */
23962
23963 /**
23964  * @class Roo.bootstrap.Radio
23965  * @extends Roo.bootstrap.Component
23966  * Bootstrap Radio class
23967  * @cfg {String} boxLabel - the label associated
23968  * @cfg {String} value - the value of radio
23969  * 
23970  * @constructor
23971  * Create a new Radio
23972  * @param {Object} config The config object
23973  */
23974 Roo.bootstrap.Radio = function(config){
23975     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23976     
23977 };
23978
23979 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23980     
23981     boxLabel : '',
23982     
23983     value : '',
23984     
23985     getAutoCreate : function()
23986     {
23987         var cfg = {
23988             tag : 'div',
23989             cls : 'form-group radio',
23990             cn : [
23991                 {
23992                     tag : 'label',
23993                     cls : 'box-label',
23994                     html : this.boxLabel
23995                 }
23996             ]
23997         };
23998         
23999         return cfg;
24000     },
24001     
24002     initEvents : function() 
24003     {
24004         this.parent().register(this);
24005         
24006         this.el.on('click', this.onClick, this);
24007         
24008     },
24009     
24010     onClick : function(e)
24011     {
24012         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24013             this.setChecked(true);
24014         }
24015     },
24016     
24017     setChecked : function(state, suppressEvent)
24018     {
24019         this.parent().setValue(this.value, suppressEvent);
24020         
24021     },
24022     
24023     setBoxLabel : function(v)
24024     {
24025         this.boxLabel = v;
24026         
24027         if(this.rendered){
24028             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24029         }
24030     }
24031     
24032 });
24033  
24034
24035  /*
24036  * - LGPL
24037  *
24038  * Input
24039  * 
24040  */
24041
24042 /**
24043  * @class Roo.bootstrap.SecurePass
24044  * @extends Roo.bootstrap.Input
24045  * Bootstrap SecurePass class
24046  *
24047  * 
24048  * @constructor
24049  * Create a new SecurePass
24050  * @param {Object} config The config object
24051  */
24052  
24053 Roo.bootstrap.SecurePass = function (config) {
24054     // these go here, so the translation tool can replace them..
24055     this.errors = {
24056         PwdEmpty: "Please type a password, and then retype it to confirm.",
24057         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24058         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24059         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24060         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24061         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24062         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24063         TooWeak: "Your password is Too Weak."
24064     },
24065     this.meterLabel = "Password strength:";
24066     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24067     this.meterClass = [
24068         "roo-password-meter-tooweak", 
24069         "roo-password-meter-weak", 
24070         "roo-password-meter-medium", 
24071         "roo-password-meter-strong", 
24072         "roo-password-meter-grey"
24073     ];
24074     
24075     this.errors = {};
24076     
24077     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24078 }
24079
24080 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24081     /**
24082      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24083      * {
24084      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24085      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24086      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24087      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24088      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24089      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24090      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24091      * })
24092      */
24093     // private
24094     
24095     meterWidth: 300,
24096     errorMsg :'',    
24097     errors: false,
24098     imageRoot: '/',
24099     /**
24100      * @cfg {String/Object} Label for the strength meter (defaults to
24101      * 'Password strength:')
24102      */
24103     // private
24104     meterLabel: '',
24105     /**
24106      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24107      * ['Weak', 'Medium', 'Strong'])
24108      */
24109     // private    
24110     pwdStrengths: false,    
24111     // private
24112     strength: 0,
24113     // private
24114     _lastPwd: null,
24115     // private
24116     kCapitalLetter: 0,
24117     kSmallLetter: 1,
24118     kDigit: 2,
24119     kPunctuation: 3,
24120     
24121     insecure: false,
24122     // private
24123     initEvents: function ()
24124     {
24125         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24126
24127         if (this.el.is('input[type=password]') && Roo.isSafari) {
24128             this.el.on('keydown', this.SafariOnKeyDown, this);
24129         }
24130
24131         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24132     },
24133     // private
24134     onRender: function (ct, position)
24135     {
24136         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24137         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24138         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24139
24140         this.trigger.createChild({
24141                    cn: [
24142                     {
24143                     //id: 'PwdMeter',
24144                     tag: 'div',
24145                     cls: 'roo-password-meter-grey col-xs-12',
24146                     style: {
24147                         //width: 0,
24148                         //width: this.meterWidth + 'px'                                                
24149                         }
24150                     },
24151                     {                            
24152                          cls: 'roo-password-meter-text'                          
24153                     }
24154                 ]            
24155         });
24156
24157          
24158         if (this.hideTrigger) {
24159             this.trigger.setDisplayed(false);
24160         }
24161         this.setSize(this.width || '', this.height || '');
24162     },
24163     // private
24164     onDestroy: function ()
24165     {
24166         if (this.trigger) {
24167             this.trigger.removeAllListeners();
24168             this.trigger.remove();
24169         }
24170         if (this.wrap) {
24171             this.wrap.remove();
24172         }
24173         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24174     },
24175     // private
24176     checkStrength: function ()
24177     {
24178         var pwd = this.inputEl().getValue();
24179         if (pwd == this._lastPwd) {
24180             return;
24181         }
24182
24183         var strength;
24184         if (this.ClientSideStrongPassword(pwd)) {
24185             strength = 3;
24186         } else if (this.ClientSideMediumPassword(pwd)) {
24187             strength = 2;
24188         } else if (this.ClientSideWeakPassword(pwd)) {
24189             strength = 1;
24190         } else {
24191             strength = 0;
24192         }
24193         
24194         Roo.log('strength1: ' + strength);
24195         
24196         //var pm = this.trigger.child('div/div/div').dom;
24197         var pm = this.trigger.child('div/div');
24198         pm.removeClass(this.meterClass);
24199         pm.addClass(this.meterClass[strength]);
24200                 
24201         
24202         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24203                 
24204         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24205         
24206         this._lastPwd = pwd;
24207     },
24208     reset: function ()
24209     {
24210         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24211         
24212         this._lastPwd = '';
24213         
24214         var pm = this.trigger.child('div/div');
24215         pm.removeClass(this.meterClass);
24216         pm.addClass('roo-password-meter-grey');        
24217         
24218         
24219         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24220         
24221         pt.innerHTML = '';
24222         this.inputEl().dom.type='password';
24223     },
24224     // private
24225     validateValue: function (value)
24226     {
24227         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24228             return false;
24229         }
24230         if (value.length == 0) {
24231             if (this.allowBlank) {
24232                 this.clearInvalid();
24233                 return true;
24234             }
24235
24236             this.markInvalid(this.errors.PwdEmpty);
24237             this.errorMsg = this.errors.PwdEmpty;
24238             return false;
24239         }
24240         
24241         if(this.insecure){
24242             return true;
24243         }
24244         
24245         if (!value.match(/[\x21-\x7e]+/)) {
24246             this.markInvalid(this.errors.PwdBadChar);
24247             this.errorMsg = this.errors.PwdBadChar;
24248             return false;
24249         }
24250         if (value.length < 6) {
24251             this.markInvalid(this.errors.PwdShort);
24252             this.errorMsg = this.errors.PwdShort;
24253             return false;
24254         }
24255         if (value.length > 16) {
24256             this.markInvalid(this.errors.PwdLong);
24257             this.errorMsg = this.errors.PwdLong;
24258             return false;
24259         }
24260         var strength;
24261         if (this.ClientSideStrongPassword(value)) {
24262             strength = 3;
24263         } else if (this.ClientSideMediumPassword(value)) {
24264             strength = 2;
24265         } else if (this.ClientSideWeakPassword(value)) {
24266             strength = 1;
24267         } else {
24268             strength = 0;
24269         }
24270
24271         
24272         if (strength < 2) {
24273             //this.markInvalid(this.errors.TooWeak);
24274             this.errorMsg = this.errors.TooWeak;
24275             //return false;
24276         }
24277         
24278         
24279         console.log('strength2: ' + strength);
24280         
24281         //var pm = this.trigger.child('div/div/div').dom;
24282         
24283         var pm = this.trigger.child('div/div');
24284         pm.removeClass(this.meterClass);
24285         pm.addClass(this.meterClass[strength]);
24286                 
24287         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24288                 
24289         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24290         
24291         this.errorMsg = ''; 
24292         return true;
24293     },
24294     // private
24295     CharacterSetChecks: function (type)
24296     {
24297         this.type = type;
24298         this.fResult = false;
24299     },
24300     // private
24301     isctype: function (character, type)
24302     {
24303         switch (type) {  
24304             case this.kCapitalLetter:
24305                 if (character >= 'A' && character <= 'Z') {
24306                     return true;
24307                 }
24308                 break;
24309             
24310             case this.kSmallLetter:
24311                 if (character >= 'a' && character <= 'z') {
24312                     return true;
24313                 }
24314                 break;
24315             
24316             case this.kDigit:
24317                 if (character >= '0' && character <= '9') {
24318                     return true;
24319                 }
24320                 break;
24321             
24322             case this.kPunctuation:
24323                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24324                     return true;
24325                 }
24326                 break;
24327             
24328             default:
24329                 return false;
24330         }
24331
24332     },
24333     // private
24334     IsLongEnough: function (pwd, size)
24335     {
24336         return !(pwd == null || isNaN(size) || pwd.length < size);
24337     },
24338     // private
24339     SpansEnoughCharacterSets: function (word, nb)
24340     {
24341         if (!this.IsLongEnough(word, nb))
24342         {
24343             return false;
24344         }
24345
24346         var characterSetChecks = new Array(
24347             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24348             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24349         );
24350         
24351         for (var index = 0; index < word.length; ++index) {
24352             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24353                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24354                     characterSetChecks[nCharSet].fResult = true;
24355                     break;
24356                 }
24357             }
24358         }
24359
24360         var nCharSets = 0;
24361         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24362             if (characterSetChecks[nCharSet].fResult) {
24363                 ++nCharSets;
24364             }
24365         }
24366
24367         if (nCharSets < nb) {
24368             return false;
24369         }
24370         return true;
24371     },
24372     // private
24373     ClientSideStrongPassword: function (pwd)
24374     {
24375         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24376     },
24377     // private
24378     ClientSideMediumPassword: function (pwd)
24379     {
24380         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24381     },
24382     // private
24383     ClientSideWeakPassword: function (pwd)
24384     {
24385         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24386     }
24387           
24388 })//<script type="text/javascript">
24389
24390 /*
24391  * Based  Ext JS Library 1.1.1
24392  * Copyright(c) 2006-2007, Ext JS, LLC.
24393  * LGPL
24394  *
24395  */
24396  
24397 /**
24398  * @class Roo.HtmlEditorCore
24399  * @extends Roo.Component
24400  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24401  *
24402  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24403  */
24404
24405 Roo.HtmlEditorCore = function(config){
24406     
24407     
24408     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24409     
24410     
24411     this.addEvents({
24412         /**
24413          * @event initialize
24414          * Fires when the editor is fully initialized (including the iframe)
24415          * @param {Roo.HtmlEditorCore} this
24416          */
24417         initialize: true,
24418         /**
24419          * @event activate
24420          * Fires when the editor is first receives the focus. Any insertion must wait
24421          * until after this event.
24422          * @param {Roo.HtmlEditorCore} this
24423          */
24424         activate: true,
24425          /**
24426          * @event beforesync
24427          * Fires before the textarea is updated with content from the editor iframe. Return false
24428          * to cancel the sync.
24429          * @param {Roo.HtmlEditorCore} this
24430          * @param {String} html
24431          */
24432         beforesync: true,
24433          /**
24434          * @event beforepush
24435          * Fires before the iframe editor is updated with content from the textarea. Return false
24436          * to cancel the push.
24437          * @param {Roo.HtmlEditorCore} this
24438          * @param {String} html
24439          */
24440         beforepush: true,
24441          /**
24442          * @event sync
24443          * Fires when the textarea is updated with content from the editor iframe.
24444          * @param {Roo.HtmlEditorCore} this
24445          * @param {String} html
24446          */
24447         sync: true,
24448          /**
24449          * @event push
24450          * Fires when the iframe editor is updated with content from the textarea.
24451          * @param {Roo.HtmlEditorCore} this
24452          * @param {String} html
24453          */
24454         push: true,
24455         
24456         /**
24457          * @event editorevent
24458          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24459          * @param {Roo.HtmlEditorCore} this
24460          */
24461         editorevent: true
24462         
24463     });
24464     
24465     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24466     
24467     // defaults : white / black...
24468     this.applyBlacklists();
24469     
24470     
24471     
24472 };
24473
24474
24475 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24476
24477
24478      /**
24479      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24480      */
24481     
24482     owner : false,
24483     
24484      /**
24485      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24486      *                        Roo.resizable.
24487      */
24488     resizable : false,
24489      /**
24490      * @cfg {Number} height (in pixels)
24491      */   
24492     height: 300,
24493    /**
24494      * @cfg {Number} width (in pixels)
24495      */   
24496     width: 500,
24497     
24498     /**
24499      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24500      * 
24501      */
24502     stylesheets: false,
24503     
24504     // id of frame..
24505     frameId: false,
24506     
24507     // private properties
24508     validationEvent : false,
24509     deferHeight: true,
24510     initialized : false,
24511     activated : false,
24512     sourceEditMode : false,
24513     onFocus : Roo.emptyFn,
24514     iframePad:3,
24515     hideMode:'offsets',
24516     
24517     clearUp: true,
24518     
24519     // blacklist + whitelisted elements..
24520     black: false,
24521     white: false,
24522      
24523     bodyCls : '',
24524
24525     /**
24526      * Protected method that will not generally be called directly. It
24527      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24528      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24529      */
24530     getDocMarkup : function(){
24531         // body styles..
24532         var st = '';
24533         
24534         // inherit styels from page...?? 
24535         if (this.stylesheets === false) {
24536             
24537             Roo.get(document.head).select('style').each(function(node) {
24538                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24539             });
24540             
24541             Roo.get(document.head).select('link').each(function(node) { 
24542                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24543             });
24544             
24545         } else if (!this.stylesheets.length) {
24546                 // simple..
24547                 st = '<style type="text/css">' +
24548                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24549                    '</style>';
24550         } else {
24551             for (var i in this.stylesheets) { 
24552                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24553             }
24554             
24555         }
24556         
24557         st +=  '<style type="text/css">' +
24558             'IMG { cursor: pointer } ' +
24559         '</style>';
24560
24561         var cls = 'roo-htmleditor-body';
24562         
24563         if(this.bodyCls.length){
24564             cls += ' ' + this.bodyCls;
24565         }
24566         
24567         return '<html><head>' + st  +
24568             //<style type="text/css">' +
24569             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24570             //'</style>' +
24571             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24572     },
24573
24574     // private
24575     onRender : function(ct, position)
24576     {
24577         var _t = this;
24578         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24579         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24580         
24581         
24582         this.el.dom.style.border = '0 none';
24583         this.el.dom.setAttribute('tabIndex', -1);
24584         this.el.addClass('x-hidden hide');
24585         
24586         
24587         
24588         if(Roo.isIE){ // fix IE 1px bogus margin
24589             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24590         }
24591        
24592         
24593         this.frameId = Roo.id();
24594         
24595          
24596         
24597         var iframe = this.owner.wrap.createChild({
24598             tag: 'iframe',
24599             cls: 'form-control', // bootstrap..
24600             id: this.frameId,
24601             name: this.frameId,
24602             frameBorder : 'no',
24603             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24604         }, this.el
24605         );
24606         
24607         
24608         this.iframe = iframe.dom;
24609
24610          this.assignDocWin();
24611         
24612         this.doc.designMode = 'on';
24613        
24614         this.doc.open();
24615         this.doc.write(this.getDocMarkup());
24616         this.doc.close();
24617
24618         
24619         var task = { // must defer to wait for browser to be ready
24620             run : function(){
24621                 //console.log("run task?" + this.doc.readyState);
24622                 this.assignDocWin();
24623                 if(this.doc.body || this.doc.readyState == 'complete'){
24624                     try {
24625                         this.doc.designMode="on";
24626                     } catch (e) {
24627                         return;
24628                     }
24629                     Roo.TaskMgr.stop(task);
24630                     this.initEditor.defer(10, this);
24631                 }
24632             },
24633             interval : 10,
24634             duration: 10000,
24635             scope: this
24636         };
24637         Roo.TaskMgr.start(task);
24638
24639     },
24640
24641     // private
24642     onResize : function(w, h)
24643     {
24644          Roo.log('resize: ' +w + ',' + h );
24645         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24646         if(!this.iframe){
24647             return;
24648         }
24649         if(typeof w == 'number'){
24650             
24651             this.iframe.style.width = w + 'px';
24652         }
24653         if(typeof h == 'number'){
24654             
24655             this.iframe.style.height = h + 'px';
24656             if(this.doc){
24657                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24658             }
24659         }
24660         
24661     },
24662
24663     /**
24664      * Toggles the editor between standard and source edit mode.
24665      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24666      */
24667     toggleSourceEdit : function(sourceEditMode){
24668         
24669         this.sourceEditMode = sourceEditMode === true;
24670         
24671         if(this.sourceEditMode){
24672  
24673             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24674             
24675         }else{
24676             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24677             //this.iframe.className = '';
24678             this.deferFocus();
24679         }
24680         //this.setSize(this.owner.wrap.getSize());
24681         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24682     },
24683
24684     
24685   
24686
24687     /**
24688      * Protected method that will not generally be called directly. If you need/want
24689      * custom HTML cleanup, this is the method you should override.
24690      * @param {String} html The HTML to be cleaned
24691      * return {String} The cleaned HTML
24692      */
24693     cleanHtml : function(html){
24694         html = String(html);
24695         if(html.length > 5){
24696             if(Roo.isSafari){ // strip safari nonsense
24697                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24698             }
24699         }
24700         if(html == '&nbsp;'){
24701             html = '';
24702         }
24703         return html;
24704     },
24705
24706     /**
24707      * HTML Editor -> Textarea
24708      * Protected method that will not generally be called directly. Syncs the contents
24709      * of the editor iframe with the textarea.
24710      */
24711     syncValue : function(){
24712         if(this.initialized){
24713             var bd = (this.doc.body || this.doc.documentElement);
24714             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24715             var html = bd.innerHTML;
24716             if(Roo.isSafari){
24717                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24718                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24719                 if(m && m[1]){
24720                     html = '<div style="'+m[0]+'">' + html + '</div>';
24721                 }
24722             }
24723             html = this.cleanHtml(html);
24724             // fix up the special chars.. normaly like back quotes in word...
24725             // however we do not want to do this with chinese..
24726             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24727                 
24728                 var cc = match.charCodeAt();
24729
24730                 // Get the character value, handling surrogate pairs
24731                 if (match.length == 2) {
24732                     // It's a surrogate pair, calculate the Unicode code point
24733                     var high = match.charCodeAt(0) - 0xD800;
24734                     var low  = match.charCodeAt(1) - 0xDC00;
24735                     cc = (high * 0x400) + low + 0x10000;
24736                 }  else if (
24737                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24738                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24739                     (cc >= 0xf900 && cc < 0xfb00 )
24740                 ) {
24741                         return match;
24742                 }  
24743          
24744                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24745                 return "&#" + cc + ";";
24746                 
24747                 
24748             });
24749             
24750             
24751              
24752             if(this.owner.fireEvent('beforesync', this, html) !== false){
24753                 this.el.dom.value = html;
24754                 this.owner.fireEvent('sync', this, html);
24755             }
24756         }
24757     },
24758
24759     /**
24760      * Protected method that will not generally be called directly. Pushes the value of the textarea
24761      * into the iframe editor.
24762      */
24763     pushValue : function(){
24764         if(this.initialized){
24765             var v = this.el.dom.value.trim();
24766             
24767 //            if(v.length < 1){
24768 //                v = '&#160;';
24769 //            }
24770             
24771             if(this.owner.fireEvent('beforepush', this, v) !== false){
24772                 var d = (this.doc.body || this.doc.documentElement);
24773                 d.innerHTML = v;
24774                 this.cleanUpPaste();
24775                 this.el.dom.value = d.innerHTML;
24776                 this.owner.fireEvent('push', this, v);
24777             }
24778         }
24779     },
24780
24781     // private
24782     deferFocus : function(){
24783         this.focus.defer(10, this);
24784     },
24785
24786     // doc'ed in Field
24787     focus : function(){
24788         if(this.win && !this.sourceEditMode){
24789             this.win.focus();
24790         }else{
24791             this.el.focus();
24792         }
24793     },
24794     
24795     assignDocWin: function()
24796     {
24797         var iframe = this.iframe;
24798         
24799          if(Roo.isIE){
24800             this.doc = iframe.contentWindow.document;
24801             this.win = iframe.contentWindow;
24802         } else {
24803 //            if (!Roo.get(this.frameId)) {
24804 //                return;
24805 //            }
24806 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24807 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24808             
24809             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24810                 return;
24811             }
24812             
24813             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24814             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24815         }
24816     },
24817     
24818     // private
24819     initEditor : function(){
24820         //console.log("INIT EDITOR");
24821         this.assignDocWin();
24822         
24823         
24824         
24825         this.doc.designMode="on";
24826         this.doc.open();
24827         this.doc.write(this.getDocMarkup());
24828         this.doc.close();
24829         
24830         var dbody = (this.doc.body || this.doc.documentElement);
24831         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24832         // this copies styles from the containing element into thsi one..
24833         // not sure why we need all of this..
24834         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24835         
24836         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24837         //ss['background-attachment'] = 'fixed'; // w3c
24838         dbody.bgProperties = 'fixed'; // ie
24839         //Roo.DomHelper.applyStyles(dbody, ss);
24840         Roo.EventManager.on(this.doc, {
24841             //'mousedown': this.onEditorEvent,
24842             'mouseup': this.onEditorEvent,
24843             'dblclick': this.onEditorEvent,
24844             'click': this.onEditorEvent,
24845             'keyup': this.onEditorEvent,
24846             buffer:100,
24847             scope: this
24848         });
24849         if(Roo.isGecko){
24850             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24851         }
24852         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24853             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24854         }
24855         this.initialized = true;
24856
24857         this.owner.fireEvent('initialize', this);
24858         this.pushValue();
24859     },
24860
24861     // private
24862     onDestroy : function(){
24863         
24864         
24865         
24866         if(this.rendered){
24867             
24868             //for (var i =0; i < this.toolbars.length;i++) {
24869             //    // fixme - ask toolbars for heights?
24870             //    this.toolbars[i].onDestroy();
24871            // }
24872             
24873             //this.wrap.dom.innerHTML = '';
24874             //this.wrap.remove();
24875         }
24876     },
24877
24878     // private
24879     onFirstFocus : function(){
24880         
24881         this.assignDocWin();
24882         
24883         
24884         this.activated = true;
24885          
24886     
24887         if(Roo.isGecko){ // prevent silly gecko errors
24888             this.win.focus();
24889             var s = this.win.getSelection();
24890             if(!s.focusNode || s.focusNode.nodeType != 3){
24891                 var r = s.getRangeAt(0);
24892                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24893                 r.collapse(true);
24894                 this.deferFocus();
24895             }
24896             try{
24897                 this.execCmd('useCSS', true);
24898                 this.execCmd('styleWithCSS', false);
24899             }catch(e){}
24900         }
24901         this.owner.fireEvent('activate', this);
24902     },
24903
24904     // private
24905     adjustFont: function(btn){
24906         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24907         //if(Roo.isSafari){ // safari
24908         //    adjust *= 2;
24909        // }
24910         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24911         if(Roo.isSafari){ // safari
24912             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24913             v =  (v < 10) ? 10 : v;
24914             v =  (v > 48) ? 48 : v;
24915             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24916             
24917         }
24918         
24919         
24920         v = Math.max(1, v+adjust);
24921         
24922         this.execCmd('FontSize', v  );
24923     },
24924
24925     onEditorEvent : function(e)
24926     {
24927         this.owner.fireEvent('editorevent', this, e);
24928       //  this.updateToolbar();
24929         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24930     },
24931
24932     insertTag : function(tg)
24933     {
24934         // could be a bit smarter... -> wrap the current selected tRoo..
24935         if (tg.toLowerCase() == 'span' ||
24936             tg.toLowerCase() == 'code' ||
24937             tg.toLowerCase() == 'sup' ||
24938             tg.toLowerCase() == 'sub' 
24939             ) {
24940             
24941             range = this.createRange(this.getSelection());
24942             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24943             wrappingNode.appendChild(range.extractContents());
24944             range.insertNode(wrappingNode);
24945
24946             return;
24947             
24948             
24949             
24950         }
24951         this.execCmd("formatblock",   tg);
24952         
24953     },
24954     
24955     insertText : function(txt)
24956     {
24957         
24958         
24959         var range = this.createRange();
24960         range.deleteContents();
24961                //alert(Sender.getAttribute('label'));
24962                
24963         range.insertNode(this.doc.createTextNode(txt));
24964     } ,
24965     
24966      
24967
24968     /**
24969      * Executes a Midas editor command on the editor document and performs necessary focus and
24970      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24971      * @param {String} cmd The Midas command
24972      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24973      */
24974     relayCmd : function(cmd, value){
24975         this.win.focus();
24976         this.execCmd(cmd, value);
24977         this.owner.fireEvent('editorevent', this);
24978         //this.updateToolbar();
24979         this.owner.deferFocus();
24980     },
24981
24982     /**
24983      * Executes a Midas editor command directly on the editor document.
24984      * For visual commands, you should use {@link #relayCmd} instead.
24985      * <b>This should only be called after the editor is initialized.</b>
24986      * @param {String} cmd The Midas command
24987      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24988      */
24989     execCmd : function(cmd, value){
24990         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24991         this.syncValue();
24992     },
24993  
24994  
24995    
24996     /**
24997      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24998      * to insert tRoo.
24999      * @param {String} text | dom node.. 
25000      */
25001     insertAtCursor : function(text)
25002     {
25003         
25004         if(!this.activated){
25005             return;
25006         }
25007         /*
25008         if(Roo.isIE){
25009             this.win.focus();
25010             var r = this.doc.selection.createRange();
25011             if(r){
25012                 r.collapse(true);
25013                 r.pasteHTML(text);
25014                 this.syncValue();
25015                 this.deferFocus();
25016             
25017             }
25018             return;
25019         }
25020         */
25021         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25022             this.win.focus();
25023             
25024             
25025             // from jquery ui (MIT licenced)
25026             var range, node;
25027             var win = this.win;
25028             
25029             if (win.getSelection && win.getSelection().getRangeAt) {
25030                 range = win.getSelection().getRangeAt(0);
25031                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25032                 range.insertNode(node);
25033             } else if (win.document.selection && win.document.selection.createRange) {
25034                 // no firefox support
25035                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25036                 win.document.selection.createRange().pasteHTML(txt);
25037             } else {
25038                 // no firefox support
25039                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25040                 this.execCmd('InsertHTML', txt);
25041             } 
25042             
25043             this.syncValue();
25044             
25045             this.deferFocus();
25046         }
25047     },
25048  // private
25049     mozKeyPress : function(e){
25050         if(e.ctrlKey){
25051             var c = e.getCharCode(), cmd;
25052           
25053             if(c > 0){
25054                 c = String.fromCharCode(c).toLowerCase();
25055                 switch(c){
25056                     case 'b':
25057                         cmd = 'bold';
25058                         break;
25059                     case 'i':
25060                         cmd = 'italic';
25061                         break;
25062                     
25063                     case 'u':
25064                         cmd = 'underline';
25065                         break;
25066                     
25067                     case 'v':
25068                         this.cleanUpPaste.defer(100, this);
25069                         return;
25070                         
25071                 }
25072                 if(cmd){
25073                     this.win.focus();
25074                     this.execCmd(cmd);
25075                     this.deferFocus();
25076                     e.preventDefault();
25077                 }
25078                 
25079             }
25080         }
25081     },
25082
25083     // private
25084     fixKeys : function(){ // load time branching for fastest keydown performance
25085         if(Roo.isIE){
25086             return function(e){
25087                 var k = e.getKey(), r;
25088                 if(k == e.TAB){
25089                     e.stopEvent();
25090                     r = this.doc.selection.createRange();
25091                     if(r){
25092                         r.collapse(true);
25093                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25094                         this.deferFocus();
25095                     }
25096                     return;
25097                 }
25098                 
25099                 if(k == e.ENTER){
25100                     r = this.doc.selection.createRange();
25101                     if(r){
25102                         var target = r.parentElement();
25103                         if(!target || target.tagName.toLowerCase() != 'li'){
25104                             e.stopEvent();
25105                             r.pasteHTML('<br />');
25106                             r.collapse(false);
25107                             r.select();
25108                         }
25109                     }
25110                 }
25111                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25112                     this.cleanUpPaste.defer(100, this);
25113                     return;
25114                 }
25115                 
25116                 
25117             };
25118         }else if(Roo.isOpera){
25119             return function(e){
25120                 var k = e.getKey();
25121                 if(k == e.TAB){
25122                     e.stopEvent();
25123                     this.win.focus();
25124                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25125                     this.deferFocus();
25126                 }
25127                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25128                     this.cleanUpPaste.defer(100, this);
25129                     return;
25130                 }
25131                 
25132             };
25133         }else if(Roo.isSafari){
25134             return function(e){
25135                 var k = e.getKey();
25136                 
25137                 if(k == e.TAB){
25138                     e.stopEvent();
25139                     this.execCmd('InsertText','\t');
25140                     this.deferFocus();
25141                     return;
25142                 }
25143                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25144                     this.cleanUpPaste.defer(100, this);
25145                     return;
25146                 }
25147                 
25148              };
25149         }
25150     }(),
25151     
25152     getAllAncestors: function()
25153     {
25154         var p = this.getSelectedNode();
25155         var a = [];
25156         if (!p) {
25157             a.push(p); // push blank onto stack..
25158             p = this.getParentElement();
25159         }
25160         
25161         
25162         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25163             a.push(p);
25164             p = p.parentNode;
25165         }
25166         a.push(this.doc.body);
25167         return a;
25168     },
25169     lastSel : false,
25170     lastSelNode : false,
25171     
25172     
25173     getSelection : function() 
25174     {
25175         this.assignDocWin();
25176         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25177     },
25178     
25179     getSelectedNode: function() 
25180     {
25181         // this may only work on Gecko!!!
25182         
25183         // should we cache this!!!!
25184         
25185         
25186         
25187          
25188         var range = this.createRange(this.getSelection()).cloneRange();
25189         
25190         if (Roo.isIE) {
25191             var parent = range.parentElement();
25192             while (true) {
25193                 var testRange = range.duplicate();
25194                 testRange.moveToElementText(parent);
25195                 if (testRange.inRange(range)) {
25196                     break;
25197                 }
25198                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25199                     break;
25200                 }
25201                 parent = parent.parentElement;
25202             }
25203             return parent;
25204         }
25205         
25206         // is ancestor a text element.
25207         var ac =  range.commonAncestorContainer;
25208         if (ac.nodeType == 3) {
25209             ac = ac.parentNode;
25210         }
25211         
25212         var ar = ac.childNodes;
25213          
25214         var nodes = [];
25215         var other_nodes = [];
25216         var has_other_nodes = false;
25217         for (var i=0;i<ar.length;i++) {
25218             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25219                 continue;
25220             }
25221             // fullly contained node.
25222             
25223             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25224                 nodes.push(ar[i]);
25225                 continue;
25226             }
25227             
25228             // probably selected..
25229             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25230                 other_nodes.push(ar[i]);
25231                 continue;
25232             }
25233             // outer..
25234             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25235                 continue;
25236             }
25237             
25238             
25239             has_other_nodes = true;
25240         }
25241         if (!nodes.length && other_nodes.length) {
25242             nodes= other_nodes;
25243         }
25244         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25245             return false;
25246         }
25247         
25248         return nodes[0];
25249     },
25250     createRange: function(sel)
25251     {
25252         // this has strange effects when using with 
25253         // top toolbar - not sure if it's a great idea.
25254         //this.editor.contentWindow.focus();
25255         if (typeof sel != "undefined") {
25256             try {
25257                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25258             } catch(e) {
25259                 return this.doc.createRange();
25260             }
25261         } else {
25262             return this.doc.createRange();
25263         }
25264     },
25265     getParentElement: function()
25266     {
25267         
25268         this.assignDocWin();
25269         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25270         
25271         var range = this.createRange(sel);
25272          
25273         try {
25274             var p = range.commonAncestorContainer;
25275             while (p.nodeType == 3) { // text node
25276                 p = p.parentNode;
25277             }
25278             return p;
25279         } catch (e) {
25280             return null;
25281         }
25282     
25283     },
25284     /***
25285      *
25286      * Range intersection.. the hard stuff...
25287      *  '-1' = before
25288      *  '0' = hits..
25289      *  '1' = after.
25290      *         [ -- selected range --- ]
25291      *   [fail]                        [fail]
25292      *
25293      *    basically..
25294      *      if end is before start or  hits it. fail.
25295      *      if start is after end or hits it fail.
25296      *
25297      *   if either hits (but other is outside. - then it's not 
25298      *   
25299      *    
25300      **/
25301     
25302     
25303     // @see http://www.thismuchiknow.co.uk/?p=64.
25304     rangeIntersectsNode : function(range, node)
25305     {
25306         var nodeRange = node.ownerDocument.createRange();
25307         try {
25308             nodeRange.selectNode(node);
25309         } catch (e) {
25310             nodeRange.selectNodeContents(node);
25311         }
25312     
25313         var rangeStartRange = range.cloneRange();
25314         rangeStartRange.collapse(true);
25315     
25316         var rangeEndRange = range.cloneRange();
25317         rangeEndRange.collapse(false);
25318     
25319         var nodeStartRange = nodeRange.cloneRange();
25320         nodeStartRange.collapse(true);
25321     
25322         var nodeEndRange = nodeRange.cloneRange();
25323         nodeEndRange.collapse(false);
25324     
25325         return rangeStartRange.compareBoundaryPoints(
25326                  Range.START_TO_START, nodeEndRange) == -1 &&
25327                rangeEndRange.compareBoundaryPoints(
25328                  Range.START_TO_START, nodeStartRange) == 1;
25329         
25330          
25331     },
25332     rangeCompareNode : function(range, node)
25333     {
25334         var nodeRange = node.ownerDocument.createRange();
25335         try {
25336             nodeRange.selectNode(node);
25337         } catch (e) {
25338             nodeRange.selectNodeContents(node);
25339         }
25340         
25341         
25342         range.collapse(true);
25343     
25344         nodeRange.collapse(true);
25345      
25346         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25347         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25348          
25349         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25350         
25351         var nodeIsBefore   =  ss == 1;
25352         var nodeIsAfter    = ee == -1;
25353         
25354         if (nodeIsBefore && nodeIsAfter) {
25355             return 0; // outer
25356         }
25357         if (!nodeIsBefore && nodeIsAfter) {
25358             return 1; //right trailed.
25359         }
25360         
25361         if (nodeIsBefore && !nodeIsAfter) {
25362             return 2;  // left trailed.
25363         }
25364         // fully contined.
25365         return 3;
25366     },
25367
25368     // private? - in a new class?
25369     cleanUpPaste :  function()
25370     {
25371         // cleans up the whole document..
25372         Roo.log('cleanuppaste');
25373         
25374         this.cleanUpChildren(this.doc.body);
25375         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25376         if (clean != this.doc.body.innerHTML) {
25377             this.doc.body.innerHTML = clean;
25378         }
25379         
25380     },
25381     
25382     cleanWordChars : function(input) {// change the chars to hex code
25383         var he = Roo.HtmlEditorCore;
25384         
25385         var output = input;
25386         Roo.each(he.swapCodes, function(sw) { 
25387             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25388             
25389             output = output.replace(swapper, sw[1]);
25390         });
25391         
25392         return output;
25393     },
25394     
25395     
25396     cleanUpChildren : function (n)
25397     {
25398         if (!n.childNodes.length) {
25399             return;
25400         }
25401         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25402            this.cleanUpChild(n.childNodes[i]);
25403         }
25404     },
25405     
25406     
25407         
25408     
25409     cleanUpChild : function (node)
25410     {
25411         var ed = this;
25412         //console.log(node);
25413         if (node.nodeName == "#text") {
25414             // clean up silly Windows -- stuff?
25415             return; 
25416         }
25417         if (node.nodeName == "#comment") {
25418             node.parentNode.removeChild(node);
25419             // clean up silly Windows -- stuff?
25420             return; 
25421         }
25422         var lcname = node.tagName.toLowerCase();
25423         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25424         // whitelist of tags..
25425         
25426         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25427             // remove node.
25428             node.parentNode.removeChild(node);
25429             return;
25430             
25431         }
25432         
25433         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25434         
25435         // spans with no attributes - just remove them..
25436         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25437             remove_keep_children = true;
25438         }
25439         
25440         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25441         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25442         
25443         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25444         //    remove_keep_children = true;
25445         //}
25446         
25447         if (remove_keep_children) {
25448             this.cleanUpChildren(node);
25449             // inserts everything just before this node...
25450             while (node.childNodes.length) {
25451                 var cn = node.childNodes[0];
25452                 node.removeChild(cn);
25453                 node.parentNode.insertBefore(cn, node);
25454             }
25455             node.parentNode.removeChild(node);
25456             return;
25457         }
25458         
25459         if (!node.attributes || !node.attributes.length) {
25460             
25461           
25462             
25463             
25464             this.cleanUpChildren(node);
25465             return;
25466         }
25467         
25468         function cleanAttr(n,v)
25469         {
25470             
25471             if (v.match(/^\./) || v.match(/^\//)) {
25472                 return;
25473             }
25474             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25475                 return;
25476             }
25477             if (v.match(/^#/)) {
25478                 return;
25479             }
25480             if (v.match(/^\{/)) { // allow template editing.
25481                 return;
25482             }
25483 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25484             node.removeAttribute(n);
25485             
25486         }
25487         
25488         var cwhite = this.cwhite;
25489         var cblack = this.cblack;
25490             
25491         function cleanStyle(n,v)
25492         {
25493             if (v.match(/expression/)) { //XSS?? should we even bother..
25494                 node.removeAttribute(n);
25495                 return;
25496             }
25497             
25498             var parts = v.split(/;/);
25499             var clean = [];
25500             
25501             Roo.each(parts, function(p) {
25502                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25503                 if (!p.length) {
25504                     return true;
25505                 }
25506                 var l = p.split(':').shift().replace(/\s+/g,'');
25507                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25508                 
25509                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25510 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25511                     //node.removeAttribute(n);
25512                     return true;
25513                 }
25514                 //Roo.log()
25515                 // only allow 'c whitelisted system attributes'
25516                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25517 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25518                     //node.removeAttribute(n);
25519                     return true;
25520                 }
25521                 
25522                 
25523                  
25524                 
25525                 clean.push(p);
25526                 return true;
25527             });
25528             if (clean.length) { 
25529                 node.setAttribute(n, clean.join(';'));
25530             } else {
25531                 node.removeAttribute(n);
25532             }
25533             
25534         }
25535         
25536         
25537         for (var i = node.attributes.length-1; i > -1 ; i--) {
25538             var a = node.attributes[i];
25539             //console.log(a);
25540             
25541             if (a.name.toLowerCase().substr(0,2)=='on')  {
25542                 node.removeAttribute(a.name);
25543                 continue;
25544             }
25545             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25546                 node.removeAttribute(a.name);
25547                 continue;
25548             }
25549             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25550                 cleanAttr(a.name,a.value); // fixme..
25551                 continue;
25552             }
25553             if (a.name == 'style') {
25554                 cleanStyle(a.name,a.value);
25555                 continue;
25556             }
25557             /// clean up MS crap..
25558             // tecnically this should be a list of valid class'es..
25559             
25560             
25561             if (a.name == 'class') {
25562                 if (a.value.match(/^Mso/)) {
25563                     node.removeAttribute('class');
25564                 }
25565                 
25566                 if (a.value.match(/^body$/)) {
25567                     node.removeAttribute('class');
25568                 }
25569                 continue;
25570             }
25571             
25572             // style cleanup!?
25573             // class cleanup?
25574             
25575         }
25576         
25577         
25578         this.cleanUpChildren(node);
25579         
25580         
25581     },
25582     
25583     /**
25584      * Clean up MS wordisms...
25585      */
25586     cleanWord : function(node)
25587     {
25588         if (!node) {
25589             this.cleanWord(this.doc.body);
25590             return;
25591         }
25592         
25593         if(
25594                 node.nodeName == 'SPAN' &&
25595                 !node.hasAttributes() &&
25596                 node.childNodes.length == 1 &&
25597                 node.firstChild.nodeName == "#text"  
25598         ) {
25599             var textNode = node.firstChild;
25600             node.removeChild(textNode);
25601             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25602                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25603             }
25604             node.parentNode.insertBefore(textNode, node);
25605             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25606                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25607             }
25608             node.parentNode.removeChild(node);
25609         }
25610         
25611         if (node.nodeName == "#text") {
25612             // clean up silly Windows -- stuff?
25613             return; 
25614         }
25615         if (node.nodeName == "#comment") {
25616             node.parentNode.removeChild(node);
25617             // clean up silly Windows -- stuff?
25618             return; 
25619         }
25620         
25621         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25622             node.parentNode.removeChild(node);
25623             return;
25624         }
25625         //Roo.log(node.tagName);
25626         // remove - but keep children..
25627         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25628             //Roo.log('-- removed');
25629             while (node.childNodes.length) {
25630                 var cn = node.childNodes[0];
25631                 node.removeChild(cn);
25632                 node.parentNode.insertBefore(cn, node);
25633                 // move node to parent - and clean it..
25634                 this.cleanWord(cn);
25635             }
25636             node.parentNode.removeChild(node);
25637             /// no need to iterate chidlren = it's got none..
25638             //this.iterateChildren(node, this.cleanWord);
25639             return;
25640         }
25641         // clean styles
25642         if (node.className.length) {
25643             
25644             var cn = node.className.split(/\W+/);
25645             var cna = [];
25646             Roo.each(cn, function(cls) {
25647                 if (cls.match(/Mso[a-zA-Z]+/)) {
25648                     return;
25649                 }
25650                 cna.push(cls);
25651             });
25652             node.className = cna.length ? cna.join(' ') : '';
25653             if (!cna.length) {
25654                 node.removeAttribute("class");
25655             }
25656         }
25657         
25658         if (node.hasAttribute("lang")) {
25659             node.removeAttribute("lang");
25660         }
25661         
25662         if (node.hasAttribute("style")) {
25663             
25664             var styles = node.getAttribute("style").split(";");
25665             var nstyle = [];
25666             Roo.each(styles, function(s) {
25667                 if (!s.match(/:/)) {
25668                     return;
25669                 }
25670                 var kv = s.split(":");
25671                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25672                     return;
25673                 }
25674                 // what ever is left... we allow.
25675                 nstyle.push(s);
25676             });
25677             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25678             if (!nstyle.length) {
25679                 node.removeAttribute('style');
25680             }
25681         }
25682         this.iterateChildren(node, this.cleanWord);
25683         
25684         
25685         
25686     },
25687     /**
25688      * iterateChildren of a Node, calling fn each time, using this as the scole..
25689      * @param {DomNode} node node to iterate children of.
25690      * @param {Function} fn method of this class to call on each item.
25691      */
25692     iterateChildren : function(node, fn)
25693     {
25694         if (!node.childNodes.length) {
25695                 return;
25696         }
25697         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25698            fn.call(this, node.childNodes[i])
25699         }
25700     },
25701     
25702     
25703     /**
25704      * cleanTableWidths.
25705      *
25706      * Quite often pasting from word etc.. results in tables with column and widths.
25707      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25708      *
25709      */
25710     cleanTableWidths : function(node)
25711     {
25712          
25713          
25714         if (!node) {
25715             this.cleanTableWidths(this.doc.body);
25716             return;
25717         }
25718         
25719         // ignore list...
25720         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25721             return; 
25722         }
25723         Roo.log(node.tagName);
25724         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25725             this.iterateChildren(node, this.cleanTableWidths);
25726             return;
25727         }
25728         if (node.hasAttribute('width')) {
25729             node.removeAttribute('width');
25730         }
25731         
25732          
25733         if (node.hasAttribute("style")) {
25734             // pretty basic...
25735             
25736             var styles = node.getAttribute("style").split(";");
25737             var nstyle = [];
25738             Roo.each(styles, function(s) {
25739                 if (!s.match(/:/)) {
25740                     return;
25741                 }
25742                 var kv = s.split(":");
25743                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25744                     return;
25745                 }
25746                 // what ever is left... we allow.
25747                 nstyle.push(s);
25748             });
25749             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25750             if (!nstyle.length) {
25751                 node.removeAttribute('style');
25752             }
25753         }
25754         
25755         this.iterateChildren(node, this.cleanTableWidths);
25756         
25757         
25758     },
25759     
25760     
25761     
25762     
25763     domToHTML : function(currentElement, depth, nopadtext) {
25764         
25765         depth = depth || 0;
25766         nopadtext = nopadtext || false;
25767     
25768         if (!currentElement) {
25769             return this.domToHTML(this.doc.body);
25770         }
25771         
25772         //Roo.log(currentElement);
25773         var j;
25774         var allText = false;
25775         var nodeName = currentElement.nodeName;
25776         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25777         
25778         if  (nodeName == '#text') {
25779             
25780             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25781         }
25782         
25783         
25784         var ret = '';
25785         if (nodeName != 'BODY') {
25786              
25787             var i = 0;
25788             // Prints the node tagName, such as <A>, <IMG>, etc
25789             if (tagName) {
25790                 var attr = [];
25791                 for(i = 0; i < currentElement.attributes.length;i++) {
25792                     // quoting?
25793                     var aname = currentElement.attributes.item(i).name;
25794                     if (!currentElement.attributes.item(i).value.length) {
25795                         continue;
25796                     }
25797                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25798                 }
25799                 
25800                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25801             } 
25802             else {
25803                 
25804                 // eack
25805             }
25806         } else {
25807             tagName = false;
25808         }
25809         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25810             return ret;
25811         }
25812         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25813             nopadtext = true;
25814         }
25815         
25816         
25817         // Traverse the tree
25818         i = 0;
25819         var currentElementChild = currentElement.childNodes.item(i);
25820         var allText = true;
25821         var innerHTML  = '';
25822         lastnode = '';
25823         while (currentElementChild) {
25824             // Formatting code (indent the tree so it looks nice on the screen)
25825             var nopad = nopadtext;
25826             if (lastnode == 'SPAN') {
25827                 nopad  = true;
25828             }
25829             // text
25830             if  (currentElementChild.nodeName == '#text') {
25831                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25832                 toadd = nopadtext ? toadd : toadd.trim();
25833                 if (!nopad && toadd.length > 80) {
25834                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25835                 }
25836                 innerHTML  += toadd;
25837                 
25838                 i++;
25839                 currentElementChild = currentElement.childNodes.item(i);
25840                 lastNode = '';
25841                 continue;
25842             }
25843             allText = false;
25844             
25845             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25846                 
25847             // Recursively traverse the tree structure of the child node
25848             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25849             lastnode = currentElementChild.nodeName;
25850             i++;
25851             currentElementChild=currentElement.childNodes.item(i);
25852         }
25853         
25854         ret += innerHTML;
25855         
25856         if (!allText) {
25857                 // The remaining code is mostly for formatting the tree
25858             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25859         }
25860         
25861         
25862         if (tagName) {
25863             ret+= "</"+tagName+">";
25864         }
25865         return ret;
25866         
25867     },
25868         
25869     applyBlacklists : function()
25870     {
25871         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25872         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25873         
25874         this.white = [];
25875         this.black = [];
25876         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25877             if (b.indexOf(tag) > -1) {
25878                 return;
25879             }
25880             this.white.push(tag);
25881             
25882         }, this);
25883         
25884         Roo.each(w, function(tag) {
25885             if (b.indexOf(tag) > -1) {
25886                 return;
25887             }
25888             if (this.white.indexOf(tag) > -1) {
25889                 return;
25890             }
25891             this.white.push(tag);
25892             
25893         }, this);
25894         
25895         
25896         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25897             if (w.indexOf(tag) > -1) {
25898                 return;
25899             }
25900             this.black.push(tag);
25901             
25902         }, this);
25903         
25904         Roo.each(b, function(tag) {
25905             if (w.indexOf(tag) > -1) {
25906                 return;
25907             }
25908             if (this.black.indexOf(tag) > -1) {
25909                 return;
25910             }
25911             this.black.push(tag);
25912             
25913         }, this);
25914         
25915         
25916         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25917         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25918         
25919         this.cwhite = [];
25920         this.cblack = [];
25921         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25922             if (b.indexOf(tag) > -1) {
25923                 return;
25924             }
25925             this.cwhite.push(tag);
25926             
25927         }, this);
25928         
25929         Roo.each(w, function(tag) {
25930             if (b.indexOf(tag) > -1) {
25931                 return;
25932             }
25933             if (this.cwhite.indexOf(tag) > -1) {
25934                 return;
25935             }
25936             this.cwhite.push(tag);
25937             
25938         }, this);
25939         
25940         
25941         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25942             if (w.indexOf(tag) > -1) {
25943                 return;
25944             }
25945             this.cblack.push(tag);
25946             
25947         }, this);
25948         
25949         Roo.each(b, function(tag) {
25950             if (w.indexOf(tag) > -1) {
25951                 return;
25952             }
25953             if (this.cblack.indexOf(tag) > -1) {
25954                 return;
25955             }
25956             this.cblack.push(tag);
25957             
25958         }, this);
25959     },
25960     
25961     setStylesheets : function(stylesheets)
25962     {
25963         if(typeof(stylesheets) == 'string'){
25964             Roo.get(this.iframe.contentDocument.head).createChild({
25965                 tag : 'link',
25966                 rel : 'stylesheet',
25967                 type : 'text/css',
25968                 href : stylesheets
25969             });
25970             
25971             return;
25972         }
25973         var _this = this;
25974      
25975         Roo.each(stylesheets, function(s) {
25976             if(!s.length){
25977                 return;
25978             }
25979             
25980             Roo.get(_this.iframe.contentDocument.head).createChild({
25981                 tag : 'link',
25982                 rel : 'stylesheet',
25983                 type : 'text/css',
25984                 href : s
25985             });
25986         });
25987
25988         
25989     },
25990     
25991     removeStylesheets : function()
25992     {
25993         var _this = this;
25994         
25995         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25996             s.remove();
25997         });
25998     },
25999     
26000     setStyle : function(style)
26001     {
26002         Roo.get(this.iframe.contentDocument.head).createChild({
26003             tag : 'style',
26004             type : 'text/css',
26005             html : style
26006         });
26007
26008         return;
26009     }
26010     
26011     // hide stuff that is not compatible
26012     /**
26013      * @event blur
26014      * @hide
26015      */
26016     /**
26017      * @event change
26018      * @hide
26019      */
26020     /**
26021      * @event focus
26022      * @hide
26023      */
26024     /**
26025      * @event specialkey
26026      * @hide
26027      */
26028     /**
26029      * @cfg {String} fieldClass @hide
26030      */
26031     /**
26032      * @cfg {String} focusClass @hide
26033      */
26034     /**
26035      * @cfg {String} autoCreate @hide
26036      */
26037     /**
26038      * @cfg {String} inputType @hide
26039      */
26040     /**
26041      * @cfg {String} invalidClass @hide
26042      */
26043     /**
26044      * @cfg {String} invalidText @hide
26045      */
26046     /**
26047      * @cfg {String} msgFx @hide
26048      */
26049     /**
26050      * @cfg {String} validateOnBlur @hide
26051      */
26052 });
26053
26054 Roo.HtmlEditorCore.white = [
26055         'area', 'br', 'img', 'input', 'hr', 'wbr',
26056         
26057        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26058        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26059        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26060        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26061        'table',   'ul',         'xmp', 
26062        
26063        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26064       'thead',   'tr', 
26065      
26066       'dir', 'menu', 'ol', 'ul', 'dl',
26067        
26068       'embed',  'object'
26069 ];
26070
26071
26072 Roo.HtmlEditorCore.black = [
26073     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26074         'applet', // 
26075         'base',   'basefont', 'bgsound', 'blink',  'body', 
26076         'frame',  'frameset', 'head',    'html',   'ilayer', 
26077         'iframe', 'layer',  'link',     'meta',    'object',   
26078         'script', 'style' ,'title',  'xml' // clean later..
26079 ];
26080 Roo.HtmlEditorCore.clean = [
26081     'script', 'style', 'title', 'xml'
26082 ];
26083 Roo.HtmlEditorCore.remove = [
26084     'font'
26085 ];
26086 // attributes..
26087
26088 Roo.HtmlEditorCore.ablack = [
26089     'on'
26090 ];
26091     
26092 Roo.HtmlEditorCore.aclean = [ 
26093     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26094 ];
26095
26096 // protocols..
26097 Roo.HtmlEditorCore.pwhite= [
26098         'http',  'https',  'mailto'
26099 ];
26100
26101 // white listed style attributes.
26102 Roo.HtmlEditorCore.cwhite= [
26103       //  'text-align', /// default is to allow most things..
26104       
26105          
26106 //        'font-size'//??
26107 ];
26108
26109 // black listed style attributes.
26110 Roo.HtmlEditorCore.cblack= [
26111       //  'font-size' -- this can be set by the project 
26112 ];
26113
26114
26115 Roo.HtmlEditorCore.swapCodes   =[ 
26116     [    8211, "&#8211;" ], 
26117     [    8212, "&#8212;" ], 
26118     [    8216,  "'" ],  
26119     [    8217, "'" ],  
26120     [    8220, '"' ],  
26121     [    8221, '"' ],  
26122     [    8226, "*" ],  
26123     [    8230, "..." ]
26124 ]; 
26125
26126     /*
26127  * - LGPL
26128  *
26129  * HtmlEditor
26130  * 
26131  */
26132
26133 /**
26134  * @class Roo.bootstrap.HtmlEditor
26135  * @extends Roo.bootstrap.TextArea
26136  * Bootstrap HtmlEditor class
26137
26138  * @constructor
26139  * Create a new HtmlEditor
26140  * @param {Object} config The config object
26141  */
26142
26143 Roo.bootstrap.HtmlEditor = function(config){
26144     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26145     if (!this.toolbars) {
26146         this.toolbars = [];
26147     }
26148     
26149     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26150     this.addEvents({
26151             /**
26152              * @event initialize
26153              * Fires when the editor is fully initialized (including the iframe)
26154              * @param {HtmlEditor} this
26155              */
26156             initialize: true,
26157             /**
26158              * @event activate
26159              * Fires when the editor is first receives the focus. Any insertion must wait
26160              * until after this event.
26161              * @param {HtmlEditor} this
26162              */
26163             activate: true,
26164              /**
26165              * @event beforesync
26166              * Fires before the textarea is updated with content from the editor iframe. Return false
26167              * to cancel the sync.
26168              * @param {HtmlEditor} this
26169              * @param {String} html
26170              */
26171             beforesync: true,
26172              /**
26173              * @event beforepush
26174              * Fires before the iframe editor is updated with content from the textarea. Return false
26175              * to cancel the push.
26176              * @param {HtmlEditor} this
26177              * @param {String} html
26178              */
26179             beforepush: true,
26180              /**
26181              * @event sync
26182              * Fires when the textarea is updated with content from the editor iframe.
26183              * @param {HtmlEditor} this
26184              * @param {String} html
26185              */
26186             sync: true,
26187              /**
26188              * @event push
26189              * Fires when the iframe editor is updated with content from the textarea.
26190              * @param {HtmlEditor} this
26191              * @param {String} html
26192              */
26193             push: true,
26194              /**
26195              * @event editmodechange
26196              * Fires when the editor switches edit modes
26197              * @param {HtmlEditor} this
26198              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26199              */
26200             editmodechange: true,
26201             /**
26202              * @event editorevent
26203              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26204              * @param {HtmlEditor} this
26205              */
26206             editorevent: true,
26207             /**
26208              * @event firstfocus
26209              * Fires when on first focus - needed by toolbars..
26210              * @param {HtmlEditor} this
26211              */
26212             firstfocus: true,
26213             /**
26214              * @event autosave
26215              * Auto save the htmlEditor value as a file into Events
26216              * @param {HtmlEditor} this
26217              */
26218             autosave: true,
26219             /**
26220              * @event savedpreview
26221              * preview the saved version of htmlEditor
26222              * @param {HtmlEditor} this
26223              */
26224             savedpreview: true
26225         });
26226 };
26227
26228
26229 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26230     
26231     
26232       /**
26233      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26234      */
26235     toolbars : false,
26236     
26237      /**
26238     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26239     */
26240     btns : [],
26241    
26242      /**
26243      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26244      *                        Roo.resizable.
26245      */
26246     resizable : false,
26247      /**
26248      * @cfg {Number} height (in pixels)
26249      */   
26250     height: 300,
26251    /**
26252      * @cfg {Number} width (in pixels)
26253      */   
26254     width: false,
26255     
26256     /**
26257      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26258      * 
26259      */
26260     stylesheets: false,
26261     
26262     // id of frame..
26263     frameId: false,
26264     
26265     // private properties
26266     validationEvent : false,
26267     deferHeight: true,
26268     initialized : false,
26269     activated : false,
26270     
26271     onFocus : Roo.emptyFn,
26272     iframePad:3,
26273     hideMode:'offsets',
26274     
26275     tbContainer : false,
26276     
26277     bodyCls : '',
26278     
26279     toolbarContainer :function() {
26280         return this.wrap.select('.x-html-editor-tb',true).first();
26281     },
26282
26283     /**
26284      * Protected method that will not generally be called directly. It
26285      * is called when the editor creates its toolbar. Override this method if you need to
26286      * add custom toolbar buttons.
26287      * @param {HtmlEditor} editor
26288      */
26289     createToolbar : function(){
26290         Roo.log('renewing');
26291         Roo.log("create toolbars");
26292         
26293         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26294         this.toolbars[0].render(this.toolbarContainer());
26295         
26296         return;
26297         
26298 //        if (!editor.toolbars || !editor.toolbars.length) {
26299 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26300 //        }
26301 //        
26302 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26303 //            editor.toolbars[i] = Roo.factory(
26304 //                    typeof(editor.toolbars[i]) == 'string' ?
26305 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26306 //                Roo.bootstrap.HtmlEditor);
26307 //            editor.toolbars[i].init(editor);
26308 //        }
26309     },
26310
26311      
26312     // private
26313     onRender : function(ct, position)
26314     {
26315        // Roo.log("Call onRender: " + this.xtype);
26316         var _t = this;
26317         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26318       
26319         this.wrap = this.inputEl().wrap({
26320             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26321         });
26322         
26323         this.editorcore.onRender(ct, position);
26324          
26325         if (this.resizable) {
26326             this.resizeEl = new Roo.Resizable(this.wrap, {
26327                 pinned : true,
26328                 wrap: true,
26329                 dynamic : true,
26330                 minHeight : this.height,
26331                 height: this.height,
26332                 handles : this.resizable,
26333                 width: this.width,
26334                 listeners : {
26335                     resize : function(r, w, h) {
26336                         _t.onResize(w,h); // -something
26337                     }
26338                 }
26339             });
26340             
26341         }
26342         this.createToolbar(this);
26343        
26344         
26345         if(!this.width && this.resizable){
26346             this.setSize(this.wrap.getSize());
26347         }
26348         if (this.resizeEl) {
26349             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26350             // should trigger onReize..
26351         }
26352         
26353     },
26354
26355     // private
26356     onResize : function(w, h)
26357     {
26358         Roo.log('resize: ' +w + ',' + h );
26359         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26360         var ew = false;
26361         var eh = false;
26362         
26363         if(this.inputEl() ){
26364             if(typeof w == 'number'){
26365                 var aw = w - this.wrap.getFrameWidth('lr');
26366                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26367                 ew = aw;
26368             }
26369             if(typeof h == 'number'){
26370                  var tbh = -11;  // fixme it needs to tool bar size!
26371                 for (var i =0; i < this.toolbars.length;i++) {
26372                     // fixme - ask toolbars for heights?
26373                     tbh += this.toolbars[i].el.getHeight();
26374                     //if (this.toolbars[i].footer) {
26375                     //    tbh += this.toolbars[i].footer.el.getHeight();
26376                     //}
26377                 }
26378               
26379                 
26380                 
26381                 
26382                 
26383                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26384                 ah -= 5; // knock a few pixes off for look..
26385                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26386                 var eh = ah;
26387             }
26388         }
26389         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26390         this.editorcore.onResize(ew,eh);
26391         
26392     },
26393
26394     /**
26395      * Toggles the editor between standard and source edit mode.
26396      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26397      */
26398     toggleSourceEdit : function(sourceEditMode)
26399     {
26400         this.editorcore.toggleSourceEdit(sourceEditMode);
26401         
26402         if(this.editorcore.sourceEditMode){
26403             Roo.log('editor - showing textarea');
26404             
26405 //            Roo.log('in');
26406 //            Roo.log(this.syncValue());
26407             this.syncValue();
26408             this.inputEl().removeClass(['hide', 'x-hidden']);
26409             this.inputEl().dom.removeAttribute('tabIndex');
26410             this.inputEl().focus();
26411         }else{
26412             Roo.log('editor - hiding textarea');
26413 //            Roo.log('out')
26414 //            Roo.log(this.pushValue()); 
26415             this.pushValue();
26416             
26417             this.inputEl().addClass(['hide', 'x-hidden']);
26418             this.inputEl().dom.setAttribute('tabIndex', -1);
26419             //this.deferFocus();
26420         }
26421          
26422         if(this.resizable){
26423             this.setSize(this.wrap.getSize());
26424         }
26425         
26426         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26427     },
26428  
26429     // private (for BoxComponent)
26430     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26431
26432     // private (for BoxComponent)
26433     getResizeEl : function(){
26434         return this.wrap;
26435     },
26436
26437     // private (for BoxComponent)
26438     getPositionEl : function(){
26439         return this.wrap;
26440     },
26441
26442     // private
26443     initEvents : function(){
26444         this.originalValue = this.getValue();
26445     },
26446
26447 //    /**
26448 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26449 //     * @method
26450 //     */
26451 //    markInvalid : Roo.emptyFn,
26452 //    /**
26453 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26454 //     * @method
26455 //     */
26456 //    clearInvalid : Roo.emptyFn,
26457
26458     setValue : function(v){
26459         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26460         this.editorcore.pushValue();
26461     },
26462
26463      
26464     // private
26465     deferFocus : function(){
26466         this.focus.defer(10, this);
26467     },
26468
26469     // doc'ed in Field
26470     focus : function(){
26471         this.editorcore.focus();
26472         
26473     },
26474       
26475
26476     // private
26477     onDestroy : function(){
26478         
26479         
26480         
26481         if(this.rendered){
26482             
26483             for (var i =0; i < this.toolbars.length;i++) {
26484                 // fixme - ask toolbars for heights?
26485                 this.toolbars[i].onDestroy();
26486             }
26487             
26488             this.wrap.dom.innerHTML = '';
26489             this.wrap.remove();
26490         }
26491     },
26492
26493     // private
26494     onFirstFocus : function(){
26495         //Roo.log("onFirstFocus");
26496         this.editorcore.onFirstFocus();
26497          for (var i =0; i < this.toolbars.length;i++) {
26498             this.toolbars[i].onFirstFocus();
26499         }
26500         
26501     },
26502     
26503     // private
26504     syncValue : function()
26505     {   
26506         this.editorcore.syncValue();
26507     },
26508     
26509     pushValue : function()
26510     {   
26511         this.editorcore.pushValue();
26512     }
26513      
26514     
26515     // hide stuff that is not compatible
26516     /**
26517      * @event blur
26518      * @hide
26519      */
26520     /**
26521      * @event change
26522      * @hide
26523      */
26524     /**
26525      * @event focus
26526      * @hide
26527      */
26528     /**
26529      * @event specialkey
26530      * @hide
26531      */
26532     /**
26533      * @cfg {String} fieldClass @hide
26534      */
26535     /**
26536      * @cfg {String} focusClass @hide
26537      */
26538     /**
26539      * @cfg {String} autoCreate @hide
26540      */
26541     /**
26542      * @cfg {String} inputType @hide
26543      */
26544      
26545     /**
26546      * @cfg {String} invalidText @hide
26547      */
26548     /**
26549      * @cfg {String} msgFx @hide
26550      */
26551     /**
26552      * @cfg {String} validateOnBlur @hide
26553      */
26554 });
26555  
26556     
26557    
26558    
26559    
26560       
26561 Roo.namespace('Roo.bootstrap.htmleditor');
26562 /**
26563  * @class Roo.bootstrap.HtmlEditorToolbar1
26564  * Basic Toolbar
26565  * 
26566  * @example
26567  * Usage:
26568  *
26569  new Roo.bootstrap.HtmlEditor({
26570     ....
26571     toolbars : [
26572         new Roo.bootstrap.HtmlEditorToolbar1({
26573             disable : { fonts: 1 , format: 1, ..., ... , ...],
26574             btns : [ .... ]
26575         })
26576     }
26577      
26578  * 
26579  * @cfg {Object} disable List of elements to disable..
26580  * @cfg {Array} btns List of additional buttons.
26581  * 
26582  * 
26583  * NEEDS Extra CSS? 
26584  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26585  */
26586  
26587 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26588 {
26589     
26590     Roo.apply(this, config);
26591     
26592     // default disabled, based on 'good practice'..
26593     this.disable = this.disable || {};
26594     Roo.applyIf(this.disable, {
26595         fontSize : true,
26596         colors : true,
26597         specialElements : true
26598     });
26599     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26600     
26601     this.editor = config.editor;
26602     this.editorcore = config.editor.editorcore;
26603     
26604     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26605     
26606     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26607     // dont call parent... till later.
26608 }
26609 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26610      
26611     bar : true,
26612     
26613     editor : false,
26614     editorcore : false,
26615     
26616     
26617     formats : [
26618         "p" ,  
26619         "h1","h2","h3","h4","h5","h6", 
26620         "pre", "code", 
26621         "abbr", "acronym", "address", "cite", "samp", "var",
26622         'div','span'
26623     ],
26624     
26625     onRender : function(ct, position)
26626     {
26627        // Roo.log("Call onRender: " + this.xtype);
26628         
26629        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26630        Roo.log(this.el);
26631        this.el.dom.style.marginBottom = '0';
26632        var _this = this;
26633        var editorcore = this.editorcore;
26634        var editor= this.editor;
26635        
26636        var children = [];
26637        var btn = function(id,cmd , toggle, handler, html){
26638        
26639             var  event = toggle ? 'toggle' : 'click';
26640        
26641             var a = {
26642                 size : 'sm',
26643                 xtype: 'Button',
26644                 xns: Roo.bootstrap,
26645                 //glyphicon : id,
26646                 fa: id,
26647                 cmd : id || cmd,
26648                 enableToggle:toggle !== false,
26649                 html : html || '',
26650                 pressed : toggle ? false : null,
26651                 listeners : {}
26652             };
26653             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26654                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26655             };
26656             children.push(a);
26657             return a;
26658        }
26659        
26660     //    var cb_box = function...
26661         
26662         var style = {
26663                 xtype: 'Button',
26664                 size : 'sm',
26665                 xns: Roo.bootstrap,
26666                 fa : 'font',
26667                 //html : 'submit'
26668                 menu : {
26669                     xtype: 'Menu',
26670                     xns: Roo.bootstrap,
26671                     items:  []
26672                 }
26673         };
26674         Roo.each(this.formats, function(f) {
26675             style.menu.items.push({
26676                 xtype :'MenuItem',
26677                 xns: Roo.bootstrap,
26678                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26679                 tagname : f,
26680                 listeners : {
26681                     click : function()
26682                     {
26683                         editorcore.insertTag(this.tagname);
26684                         editor.focus();
26685                     }
26686                 }
26687                 
26688             });
26689         });
26690         children.push(style);   
26691         
26692         btn('bold',false,true);
26693         btn('italic',false,true);
26694         btn('align-left', 'justifyleft',true);
26695         btn('align-center', 'justifycenter',true);
26696         btn('align-right' , 'justifyright',true);
26697         btn('link', false, false, function(btn) {
26698             //Roo.log("create link?");
26699             var url = prompt(this.createLinkText, this.defaultLinkValue);
26700             if(url && url != 'http:/'+'/'){
26701                 this.editorcore.relayCmd('createlink', url);
26702             }
26703         }),
26704         btn('list','insertunorderedlist',true);
26705         btn('pencil', false,true, function(btn){
26706                 Roo.log(this);
26707                 this.toggleSourceEdit(btn.pressed);
26708         });
26709         
26710         if (this.editor.btns.length > 0) {
26711             for (var i = 0; i<this.editor.btns.length; i++) {
26712                 children.push(this.editor.btns[i]);
26713             }
26714         }
26715         
26716         /*
26717         var cog = {
26718                 xtype: 'Button',
26719                 size : 'sm',
26720                 xns: Roo.bootstrap,
26721                 glyphicon : 'cog',
26722                 //html : 'submit'
26723                 menu : {
26724                     xtype: 'Menu',
26725                     xns: Roo.bootstrap,
26726                     items:  []
26727                 }
26728         };
26729         
26730         cog.menu.items.push({
26731             xtype :'MenuItem',
26732             xns: Roo.bootstrap,
26733             html : Clean styles,
26734             tagname : f,
26735             listeners : {
26736                 click : function()
26737                 {
26738                     editorcore.insertTag(this.tagname);
26739                     editor.focus();
26740                 }
26741             }
26742             
26743         });
26744        */
26745         
26746          
26747        this.xtype = 'NavSimplebar';
26748         
26749         for(var i=0;i< children.length;i++) {
26750             
26751             this.buttons.add(this.addxtypeChild(children[i]));
26752             
26753         }
26754         
26755         editor.on('editorevent', this.updateToolbar, this);
26756     },
26757     onBtnClick : function(id)
26758     {
26759        this.editorcore.relayCmd(id);
26760        this.editorcore.focus();
26761     },
26762     
26763     /**
26764      * Protected method that will not generally be called directly. It triggers
26765      * a toolbar update by reading the markup state of the current selection in the editor.
26766      */
26767     updateToolbar: function(){
26768
26769         if(!this.editorcore.activated){
26770             this.editor.onFirstFocus(); // is this neeed?
26771             return;
26772         }
26773
26774         var btns = this.buttons; 
26775         var doc = this.editorcore.doc;
26776         btns.get('bold').setActive(doc.queryCommandState('bold'));
26777         btns.get('italic').setActive(doc.queryCommandState('italic'));
26778         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26779         
26780         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26781         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26782         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26783         
26784         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26785         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26786          /*
26787         
26788         var ans = this.editorcore.getAllAncestors();
26789         if (this.formatCombo) {
26790             
26791             
26792             var store = this.formatCombo.store;
26793             this.formatCombo.setValue("");
26794             for (var i =0; i < ans.length;i++) {
26795                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26796                     // select it..
26797                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26798                     break;
26799                 }
26800             }
26801         }
26802         
26803         
26804         
26805         // hides menus... - so this cant be on a menu...
26806         Roo.bootstrap.MenuMgr.hideAll();
26807         */
26808         Roo.bootstrap.MenuMgr.hideAll();
26809         //this.editorsyncValue();
26810     },
26811     onFirstFocus: function() {
26812         this.buttons.each(function(item){
26813            item.enable();
26814         });
26815     },
26816     toggleSourceEdit : function(sourceEditMode){
26817         
26818           
26819         if(sourceEditMode){
26820             Roo.log("disabling buttons");
26821            this.buttons.each( function(item){
26822                 if(item.cmd != 'pencil'){
26823                     item.disable();
26824                 }
26825             });
26826           
26827         }else{
26828             Roo.log("enabling buttons");
26829             if(this.editorcore.initialized){
26830                 this.buttons.each( function(item){
26831                     item.enable();
26832                 });
26833             }
26834             
26835         }
26836         Roo.log("calling toggole on editor");
26837         // tell the editor that it's been pressed..
26838         this.editor.toggleSourceEdit(sourceEditMode);
26839        
26840     }
26841 });
26842
26843
26844
26845
26846  
26847 /*
26848  * - LGPL
26849  */
26850
26851 /**
26852  * @class Roo.bootstrap.Markdown
26853  * @extends Roo.bootstrap.TextArea
26854  * Bootstrap Showdown editable area
26855  * @cfg {string} content
26856  * 
26857  * @constructor
26858  * Create a new Showdown
26859  */
26860
26861 Roo.bootstrap.Markdown = function(config){
26862     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26863    
26864 };
26865
26866 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26867     
26868     editing :false,
26869     
26870     initEvents : function()
26871     {
26872         
26873         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26874         this.markdownEl = this.el.createChild({
26875             cls : 'roo-markdown-area'
26876         });
26877         this.inputEl().addClass('d-none');
26878         if (this.getValue() == '') {
26879             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26880             
26881         } else {
26882             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26883         }
26884         this.markdownEl.on('click', this.toggleTextEdit, this);
26885         this.on('blur', this.toggleTextEdit, this);
26886         this.on('specialkey', this.resizeTextArea, this);
26887     },
26888     
26889     toggleTextEdit : function()
26890     {
26891         var sh = this.markdownEl.getHeight();
26892         this.inputEl().addClass('d-none');
26893         this.markdownEl.addClass('d-none');
26894         if (!this.editing) {
26895             // show editor?
26896             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26897             this.inputEl().removeClass('d-none');
26898             this.inputEl().focus();
26899             this.editing = true;
26900             return;
26901         }
26902         // show showdown...
26903         this.updateMarkdown();
26904         this.markdownEl.removeClass('d-none');
26905         this.editing = false;
26906         return;
26907     },
26908     updateMarkdown : function()
26909     {
26910         if (this.getValue() == '') {
26911             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26912             return;
26913         }
26914  
26915         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26916     },
26917     
26918     resizeTextArea: function () {
26919         
26920         var sh = 100;
26921         Roo.log([sh, this.getValue().split("\n").length * 30]);
26922         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26923     },
26924     setValue : function(val)
26925     {
26926         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26927         if (!this.editing) {
26928             this.updateMarkdown();
26929         }
26930         
26931     },
26932     focus : function()
26933     {
26934         if (!this.editing) {
26935             this.toggleTextEdit();
26936         }
26937         
26938     }
26939
26940
26941 });
26942 /**
26943  * @class Roo.bootstrap.Table.AbstractSelectionModel
26944  * @extends Roo.util.Observable
26945  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26946  * implemented by descendant classes.  This class should not be directly instantiated.
26947  * @constructor
26948  */
26949 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26950     this.locked = false;
26951     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26952 };
26953
26954
26955 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26956     /** @ignore Called by the grid automatically. Do not call directly. */
26957     init : function(grid){
26958         this.grid = grid;
26959         this.initEvents();
26960     },
26961
26962     /**
26963      * Locks the selections.
26964      */
26965     lock : function(){
26966         this.locked = true;
26967     },
26968
26969     /**
26970      * Unlocks the selections.
26971      */
26972     unlock : function(){
26973         this.locked = false;
26974     },
26975
26976     /**
26977      * Returns true if the selections are locked.
26978      * @return {Boolean}
26979      */
26980     isLocked : function(){
26981         return this.locked;
26982     },
26983     
26984     
26985     initEvents : function ()
26986     {
26987         
26988     }
26989 });
26990 /**
26991  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26992  * @class Roo.bootstrap.Table.RowSelectionModel
26993  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26994  * It supports multiple selections and keyboard selection/navigation. 
26995  * @constructor
26996  * @param {Object} config
26997  */
26998
26999 Roo.bootstrap.Table.RowSelectionModel = function(config){
27000     Roo.apply(this, config);
27001     this.selections = new Roo.util.MixedCollection(false, function(o){
27002         return o.id;
27003     });
27004
27005     this.last = false;
27006     this.lastActive = false;
27007
27008     this.addEvents({
27009         /**
27010              * @event selectionchange
27011              * Fires when the selection changes
27012              * @param {SelectionModel} this
27013              */
27014             "selectionchange" : true,
27015         /**
27016              * @event afterselectionchange
27017              * Fires after the selection changes (eg. by key press or clicking)
27018              * @param {SelectionModel} this
27019              */
27020             "afterselectionchange" : true,
27021         /**
27022              * @event beforerowselect
27023              * Fires when a row is selected being selected, return false to cancel.
27024              * @param {SelectionModel} this
27025              * @param {Number} rowIndex The selected index
27026              * @param {Boolean} keepExisting False if other selections will be cleared
27027              */
27028             "beforerowselect" : true,
27029         /**
27030              * @event rowselect
27031              * Fires when a row is selected.
27032              * @param {SelectionModel} this
27033              * @param {Number} rowIndex The selected index
27034              * @param {Roo.data.Record} r The record
27035              */
27036             "rowselect" : true,
27037         /**
27038              * @event rowdeselect
27039              * Fires when a row is deselected.
27040              * @param {SelectionModel} this
27041              * @param {Number} rowIndex The selected index
27042              */
27043         "rowdeselect" : true
27044     });
27045     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27046     this.locked = false;
27047  };
27048
27049 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27050     /**
27051      * @cfg {Boolean} singleSelect
27052      * True to allow selection of only one row at a time (defaults to false)
27053      */
27054     singleSelect : false,
27055
27056     // private
27057     initEvents : function()
27058     {
27059
27060         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27061         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27062         //}else{ // allow click to work like normal
27063          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27064         //}
27065         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27066         this.grid.on("rowclick", this.handleMouseDown, this);
27067         
27068         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27069             "up" : function(e){
27070                 if(!e.shiftKey){
27071                     this.selectPrevious(e.shiftKey);
27072                 }else if(this.last !== false && this.lastActive !== false){
27073                     var last = this.last;
27074                     this.selectRange(this.last,  this.lastActive-1);
27075                     this.grid.getView().focusRow(this.lastActive);
27076                     if(last !== false){
27077                         this.last = last;
27078                     }
27079                 }else{
27080                     this.selectFirstRow();
27081                 }
27082                 this.fireEvent("afterselectionchange", this);
27083             },
27084             "down" : function(e){
27085                 if(!e.shiftKey){
27086                     this.selectNext(e.shiftKey);
27087                 }else if(this.last !== false && this.lastActive !== false){
27088                     var last = this.last;
27089                     this.selectRange(this.last,  this.lastActive+1);
27090                     this.grid.getView().focusRow(this.lastActive);
27091                     if(last !== false){
27092                         this.last = last;
27093                     }
27094                 }else{
27095                     this.selectFirstRow();
27096                 }
27097                 this.fireEvent("afterselectionchange", this);
27098             },
27099             scope: this
27100         });
27101         this.grid.store.on('load', function(){
27102             this.selections.clear();
27103         },this);
27104         /*
27105         var view = this.grid.view;
27106         view.on("refresh", this.onRefresh, this);
27107         view.on("rowupdated", this.onRowUpdated, this);
27108         view.on("rowremoved", this.onRemove, this);
27109         */
27110     },
27111
27112     // private
27113     onRefresh : function()
27114     {
27115         var ds = this.grid.store, i, v = this.grid.view;
27116         var s = this.selections;
27117         s.each(function(r){
27118             if((i = ds.indexOfId(r.id)) != -1){
27119                 v.onRowSelect(i);
27120             }else{
27121                 s.remove(r);
27122             }
27123         });
27124     },
27125
27126     // private
27127     onRemove : function(v, index, r){
27128         this.selections.remove(r);
27129     },
27130
27131     // private
27132     onRowUpdated : function(v, index, r){
27133         if(this.isSelected(r)){
27134             v.onRowSelect(index);
27135         }
27136     },
27137
27138     /**
27139      * Select records.
27140      * @param {Array} records The records to select
27141      * @param {Boolean} keepExisting (optional) True to keep existing selections
27142      */
27143     selectRecords : function(records, keepExisting)
27144     {
27145         if(!keepExisting){
27146             this.clearSelections();
27147         }
27148             var ds = this.grid.store;
27149         for(var i = 0, len = records.length; i < len; i++){
27150             this.selectRow(ds.indexOf(records[i]), true);
27151         }
27152     },
27153
27154     /**
27155      * Gets the number of selected rows.
27156      * @return {Number}
27157      */
27158     getCount : function(){
27159         return this.selections.length;
27160     },
27161
27162     /**
27163      * Selects the first row in the grid.
27164      */
27165     selectFirstRow : function(){
27166         this.selectRow(0);
27167     },
27168
27169     /**
27170      * Select the last row.
27171      * @param {Boolean} keepExisting (optional) True to keep existing selections
27172      */
27173     selectLastRow : function(keepExisting){
27174         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27175         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27176     },
27177
27178     /**
27179      * Selects the row immediately following the last selected row.
27180      * @param {Boolean} keepExisting (optional) True to keep existing selections
27181      */
27182     selectNext : function(keepExisting)
27183     {
27184             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27185             this.selectRow(this.last+1, keepExisting);
27186             this.grid.getView().focusRow(this.last);
27187         }
27188     },
27189
27190     /**
27191      * Selects the row that precedes the last selected row.
27192      * @param {Boolean} keepExisting (optional) True to keep existing selections
27193      */
27194     selectPrevious : function(keepExisting){
27195         if(this.last){
27196             this.selectRow(this.last-1, keepExisting);
27197             this.grid.getView().focusRow(this.last);
27198         }
27199     },
27200
27201     /**
27202      * Returns the selected records
27203      * @return {Array} Array of selected records
27204      */
27205     getSelections : function(){
27206         return [].concat(this.selections.items);
27207     },
27208
27209     /**
27210      * Returns the first selected record.
27211      * @return {Record}
27212      */
27213     getSelected : function(){
27214         return this.selections.itemAt(0);
27215     },
27216
27217
27218     /**
27219      * Clears all selections.
27220      */
27221     clearSelections : function(fast)
27222     {
27223         if(this.locked) {
27224             return;
27225         }
27226         if(fast !== true){
27227                 var ds = this.grid.store;
27228             var s = this.selections;
27229             s.each(function(r){
27230                 this.deselectRow(ds.indexOfId(r.id));
27231             }, this);
27232             s.clear();
27233         }else{
27234             this.selections.clear();
27235         }
27236         this.last = false;
27237     },
27238
27239
27240     /**
27241      * Selects all rows.
27242      */
27243     selectAll : function(){
27244         if(this.locked) {
27245             return;
27246         }
27247         this.selections.clear();
27248         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27249             this.selectRow(i, true);
27250         }
27251     },
27252
27253     /**
27254      * Returns True if there is a selection.
27255      * @return {Boolean}
27256      */
27257     hasSelection : function(){
27258         return this.selections.length > 0;
27259     },
27260
27261     /**
27262      * Returns True if the specified row is selected.
27263      * @param {Number/Record} record The record or index of the record to check
27264      * @return {Boolean}
27265      */
27266     isSelected : function(index){
27267             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27268         return (r && this.selections.key(r.id) ? true : false);
27269     },
27270
27271     /**
27272      * Returns True if the specified record id is selected.
27273      * @param {String} id The id of record to check
27274      * @return {Boolean}
27275      */
27276     isIdSelected : function(id){
27277         return (this.selections.key(id) ? true : false);
27278     },
27279
27280
27281     // private
27282     handleMouseDBClick : function(e, t){
27283         
27284     },
27285     // private
27286     handleMouseDown : function(e, t)
27287     {
27288             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27289         if(this.isLocked() || rowIndex < 0 ){
27290             return;
27291         };
27292         if(e.shiftKey && this.last !== false){
27293             var last = this.last;
27294             this.selectRange(last, rowIndex, e.ctrlKey);
27295             this.last = last; // reset the last
27296             t.focus();
27297     
27298         }else{
27299             var isSelected = this.isSelected(rowIndex);
27300             //Roo.log("select row:" + rowIndex);
27301             if(isSelected){
27302                 this.deselectRow(rowIndex);
27303             } else {
27304                         this.selectRow(rowIndex, true);
27305             }
27306     
27307             /*
27308                 if(e.button !== 0 && isSelected){
27309                 alert('rowIndex 2: ' + rowIndex);
27310                     view.focusRow(rowIndex);
27311                 }else if(e.ctrlKey && isSelected){
27312                     this.deselectRow(rowIndex);
27313                 }else if(!isSelected){
27314                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27315                     view.focusRow(rowIndex);
27316                 }
27317             */
27318         }
27319         this.fireEvent("afterselectionchange", this);
27320     },
27321     // private
27322     handleDragableRowClick :  function(grid, rowIndex, e) 
27323     {
27324         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27325             this.selectRow(rowIndex, false);
27326             grid.view.focusRow(rowIndex);
27327              this.fireEvent("afterselectionchange", this);
27328         }
27329     },
27330     
27331     /**
27332      * Selects multiple rows.
27333      * @param {Array} rows Array of the indexes of the row to select
27334      * @param {Boolean} keepExisting (optional) True to keep existing selections
27335      */
27336     selectRows : function(rows, keepExisting){
27337         if(!keepExisting){
27338             this.clearSelections();
27339         }
27340         for(var i = 0, len = rows.length; i < len; i++){
27341             this.selectRow(rows[i], true);
27342         }
27343     },
27344
27345     /**
27346      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27347      * @param {Number} startRow The index of the first row in the range
27348      * @param {Number} endRow The index of the last row in the range
27349      * @param {Boolean} keepExisting (optional) True to retain existing selections
27350      */
27351     selectRange : function(startRow, endRow, keepExisting){
27352         if(this.locked) {
27353             return;
27354         }
27355         if(!keepExisting){
27356             this.clearSelections();
27357         }
27358         if(startRow <= endRow){
27359             for(var i = startRow; i <= endRow; i++){
27360                 this.selectRow(i, true);
27361             }
27362         }else{
27363             for(var i = startRow; i >= endRow; i--){
27364                 this.selectRow(i, true);
27365             }
27366         }
27367     },
27368
27369     /**
27370      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27371      * @param {Number} startRow The index of the first row in the range
27372      * @param {Number} endRow The index of the last row in the range
27373      */
27374     deselectRange : function(startRow, endRow, preventViewNotify){
27375         if(this.locked) {
27376             return;
27377         }
27378         for(var i = startRow; i <= endRow; i++){
27379             this.deselectRow(i, preventViewNotify);
27380         }
27381     },
27382
27383     /**
27384      * Selects a row.
27385      * @param {Number} row The index of the row to select
27386      * @param {Boolean} keepExisting (optional) True to keep existing selections
27387      */
27388     selectRow : function(index, keepExisting, preventViewNotify)
27389     {
27390             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27391             return;
27392         }
27393         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27394             if(!keepExisting || this.singleSelect){
27395                 this.clearSelections();
27396             }
27397             
27398             var r = this.grid.store.getAt(index);
27399             //console.log('selectRow - record id :' + r.id);
27400             
27401             this.selections.add(r);
27402             this.last = this.lastActive = index;
27403             if(!preventViewNotify){
27404                 var proxy = new Roo.Element(
27405                                 this.grid.getRowDom(index)
27406                 );
27407                 proxy.addClass('bg-info info');
27408             }
27409             this.fireEvent("rowselect", this, index, r);
27410             this.fireEvent("selectionchange", this);
27411         }
27412     },
27413
27414     /**
27415      * Deselects a row.
27416      * @param {Number} row The index of the row to deselect
27417      */
27418     deselectRow : function(index, preventViewNotify)
27419     {
27420         if(this.locked) {
27421             return;
27422         }
27423         if(this.last == index){
27424             this.last = false;
27425         }
27426         if(this.lastActive == index){
27427             this.lastActive = false;
27428         }
27429         
27430         var r = this.grid.store.getAt(index);
27431         if (!r) {
27432             return;
27433         }
27434         
27435         this.selections.remove(r);
27436         //.console.log('deselectRow - record id :' + r.id);
27437         if(!preventViewNotify){
27438         
27439             var proxy = new Roo.Element(
27440                 this.grid.getRowDom(index)
27441             );
27442             proxy.removeClass('bg-info info');
27443         }
27444         this.fireEvent("rowdeselect", this, index);
27445         this.fireEvent("selectionchange", this);
27446     },
27447
27448     // private
27449     restoreLast : function(){
27450         if(this._last){
27451             this.last = this._last;
27452         }
27453     },
27454
27455     // private
27456     acceptsNav : function(row, col, cm){
27457         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27458     },
27459
27460     // private
27461     onEditorKey : function(field, e){
27462         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27463         if(k == e.TAB){
27464             e.stopEvent();
27465             ed.completeEdit();
27466             if(e.shiftKey){
27467                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27468             }else{
27469                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27470             }
27471         }else if(k == e.ENTER && !e.ctrlKey){
27472             e.stopEvent();
27473             ed.completeEdit();
27474             if(e.shiftKey){
27475                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27476             }else{
27477                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27478             }
27479         }else if(k == e.ESC){
27480             ed.cancelEdit();
27481         }
27482         if(newCell){
27483             g.startEditing(newCell[0], newCell[1]);
27484         }
27485     }
27486 });
27487 /*
27488  * Based on:
27489  * Ext JS Library 1.1.1
27490  * Copyright(c) 2006-2007, Ext JS, LLC.
27491  *
27492  * Originally Released Under LGPL - original licence link has changed is not relivant.
27493  *
27494  * Fork - LGPL
27495  * <script type="text/javascript">
27496  */
27497  
27498 /**
27499  * @class Roo.bootstrap.PagingToolbar
27500  * @extends Roo.bootstrap.NavSimplebar
27501  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27502  * @constructor
27503  * Create a new PagingToolbar
27504  * @param {Object} config The config object
27505  * @param {Roo.data.Store} store
27506  */
27507 Roo.bootstrap.PagingToolbar = function(config)
27508 {
27509     // old args format still supported... - xtype is prefered..
27510         // created from xtype...
27511     
27512     this.ds = config.dataSource;
27513     
27514     if (config.store && !this.ds) {
27515         this.store= Roo.factory(config.store, Roo.data);
27516         this.ds = this.store;
27517         this.ds.xmodule = this.xmodule || false;
27518     }
27519     
27520     this.toolbarItems = [];
27521     if (config.items) {
27522         this.toolbarItems = config.items;
27523     }
27524     
27525     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27526     
27527     this.cursor = 0;
27528     
27529     if (this.ds) { 
27530         this.bind(this.ds);
27531     }
27532     
27533     if (Roo.bootstrap.version == 4) {
27534         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27535     } else {
27536         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27537     }
27538     
27539 };
27540
27541 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27542     /**
27543      * @cfg {Roo.data.Store} dataSource
27544      * The underlying data store providing the paged data
27545      */
27546     /**
27547      * @cfg {String/HTMLElement/Element} container
27548      * container The id or element that will contain the toolbar
27549      */
27550     /**
27551      * @cfg {Boolean} displayInfo
27552      * True to display the displayMsg (defaults to false)
27553      */
27554     /**
27555      * @cfg {Number} pageSize
27556      * The number of records to display per page (defaults to 20)
27557      */
27558     pageSize: 20,
27559     /**
27560      * @cfg {String} displayMsg
27561      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27562      */
27563     displayMsg : 'Displaying {0} - {1} of {2}',
27564     /**
27565      * @cfg {String} emptyMsg
27566      * The message to display when no records are found (defaults to "No data to display")
27567      */
27568     emptyMsg : 'No data to display',
27569     /**
27570      * Customizable piece of the default paging text (defaults to "Page")
27571      * @type String
27572      */
27573     beforePageText : "Page",
27574     /**
27575      * Customizable piece of the default paging text (defaults to "of %0")
27576      * @type String
27577      */
27578     afterPageText : "of {0}",
27579     /**
27580      * Customizable piece of the default paging text (defaults to "First Page")
27581      * @type String
27582      */
27583     firstText : "First Page",
27584     /**
27585      * Customizable piece of the default paging text (defaults to "Previous Page")
27586      * @type String
27587      */
27588     prevText : "Previous Page",
27589     /**
27590      * Customizable piece of the default paging text (defaults to "Next Page")
27591      * @type String
27592      */
27593     nextText : "Next Page",
27594     /**
27595      * Customizable piece of the default paging text (defaults to "Last Page")
27596      * @type String
27597      */
27598     lastText : "Last Page",
27599     /**
27600      * Customizable piece of the default paging text (defaults to "Refresh")
27601      * @type String
27602      */
27603     refreshText : "Refresh",
27604
27605     buttons : false,
27606     // private
27607     onRender : function(ct, position) 
27608     {
27609         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27610         this.navgroup.parentId = this.id;
27611         this.navgroup.onRender(this.el, null);
27612         // add the buttons to the navgroup
27613         
27614         if(this.displayInfo){
27615             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27616             this.displayEl = this.el.select('.x-paging-info', true).first();
27617 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27618 //            this.displayEl = navel.el.select('span',true).first();
27619         }
27620         
27621         var _this = this;
27622         
27623         if(this.buttons){
27624             Roo.each(_this.buttons, function(e){ // this might need to use render????
27625                Roo.factory(e).render(_this.el);
27626             });
27627         }
27628             
27629         Roo.each(_this.toolbarItems, function(e) {
27630             _this.navgroup.addItem(e);
27631         });
27632         
27633         
27634         this.first = this.navgroup.addItem({
27635             tooltip: this.firstText,
27636             cls: "prev btn-outline-secondary",
27637             html : ' <i class="fa fa-step-backward"></i>',
27638             disabled: true,
27639             preventDefault: true,
27640             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27641         });
27642         
27643         this.prev =  this.navgroup.addItem({
27644             tooltip: this.prevText,
27645             cls: "prev btn-outline-secondary",
27646             html : ' <i class="fa fa-backward"></i>',
27647             disabled: true,
27648             preventDefault: true,
27649             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27650         });
27651     //this.addSeparator();
27652         
27653         
27654         var field = this.navgroup.addItem( {
27655             tagtype : 'span',
27656             cls : 'x-paging-position  btn-outline-secondary',
27657              disabled: true,
27658             html : this.beforePageText  +
27659                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27660                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27661          } ); //?? escaped?
27662         
27663         this.field = field.el.select('input', true).first();
27664         this.field.on("keydown", this.onPagingKeydown, this);
27665         this.field.on("focus", function(){this.dom.select();});
27666     
27667     
27668         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27669         //this.field.setHeight(18);
27670         //this.addSeparator();
27671         this.next = this.navgroup.addItem({
27672             tooltip: this.nextText,
27673             cls: "next btn-outline-secondary",
27674             html : ' <i class="fa fa-forward"></i>',
27675             disabled: true,
27676             preventDefault: true,
27677             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27678         });
27679         this.last = this.navgroup.addItem({
27680             tooltip: this.lastText,
27681             html : ' <i class="fa fa-step-forward"></i>',
27682             cls: "next btn-outline-secondary",
27683             disabled: true,
27684             preventDefault: true,
27685             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27686         });
27687     //this.addSeparator();
27688         this.loading = this.navgroup.addItem({
27689             tooltip: this.refreshText,
27690             cls: "btn-outline-secondary",
27691             html : ' <i class="fa fa-refresh"></i>',
27692             preventDefault: true,
27693             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27694         });
27695         
27696     },
27697
27698     // private
27699     updateInfo : function(){
27700         if(this.displayEl){
27701             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27702             var msg = count == 0 ?
27703                 this.emptyMsg :
27704                 String.format(
27705                     this.displayMsg,
27706                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27707                 );
27708             this.displayEl.update(msg);
27709         }
27710     },
27711
27712     // private
27713     onLoad : function(ds, r, o)
27714     {
27715         this.cursor = o.params && o.params.start ? o.params.start : 0;
27716         
27717         var d = this.getPageData(),
27718             ap = d.activePage,
27719             ps = d.pages;
27720         
27721         
27722         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27723         this.field.dom.value = ap;
27724         this.first.setDisabled(ap == 1);
27725         this.prev.setDisabled(ap == 1);
27726         this.next.setDisabled(ap == ps);
27727         this.last.setDisabled(ap == ps);
27728         this.loading.enable();
27729         this.updateInfo();
27730     },
27731
27732     // private
27733     getPageData : function(){
27734         var total = this.ds.getTotalCount();
27735         return {
27736             total : total,
27737             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27738             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27739         };
27740     },
27741
27742     // private
27743     onLoadError : function(){
27744         this.loading.enable();
27745     },
27746
27747     // private
27748     onPagingKeydown : function(e){
27749         var k = e.getKey();
27750         var d = this.getPageData();
27751         if(k == e.RETURN){
27752             var v = this.field.dom.value, pageNum;
27753             if(!v || isNaN(pageNum = parseInt(v, 10))){
27754                 this.field.dom.value = d.activePage;
27755                 return;
27756             }
27757             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27758             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27759             e.stopEvent();
27760         }
27761         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))
27762         {
27763           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27764           this.field.dom.value = pageNum;
27765           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27766           e.stopEvent();
27767         }
27768         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27769         {
27770           var v = this.field.dom.value, pageNum; 
27771           var increment = (e.shiftKey) ? 10 : 1;
27772           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27773                 increment *= -1;
27774           }
27775           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27776             this.field.dom.value = d.activePage;
27777             return;
27778           }
27779           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27780           {
27781             this.field.dom.value = parseInt(v, 10) + increment;
27782             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27783             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27784           }
27785           e.stopEvent();
27786         }
27787     },
27788
27789     // private
27790     beforeLoad : function(){
27791         if(this.loading){
27792             this.loading.disable();
27793         }
27794     },
27795
27796     // private
27797     onClick : function(which){
27798         
27799         var ds = this.ds;
27800         if (!ds) {
27801             return;
27802         }
27803         
27804         switch(which){
27805             case "first":
27806                 ds.load({params:{start: 0, limit: this.pageSize}});
27807             break;
27808             case "prev":
27809                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27810             break;
27811             case "next":
27812                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27813             break;
27814             case "last":
27815                 var total = ds.getTotalCount();
27816                 var extra = total % this.pageSize;
27817                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27818                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27819             break;
27820             case "refresh":
27821                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27822             break;
27823         }
27824     },
27825
27826     /**
27827      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27828      * @param {Roo.data.Store} store The data store to unbind
27829      */
27830     unbind : function(ds){
27831         ds.un("beforeload", this.beforeLoad, this);
27832         ds.un("load", this.onLoad, this);
27833         ds.un("loadexception", this.onLoadError, this);
27834         ds.un("remove", this.updateInfo, this);
27835         ds.un("add", this.updateInfo, this);
27836         this.ds = undefined;
27837     },
27838
27839     /**
27840      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27841      * @param {Roo.data.Store} store The data store to bind
27842      */
27843     bind : function(ds){
27844         ds.on("beforeload", this.beforeLoad, this);
27845         ds.on("load", this.onLoad, this);
27846         ds.on("loadexception", this.onLoadError, this);
27847         ds.on("remove", this.updateInfo, this);
27848         ds.on("add", this.updateInfo, this);
27849         this.ds = ds;
27850     }
27851 });/*
27852  * - LGPL
27853  *
27854  * element
27855  * 
27856  */
27857
27858 /**
27859  * @class Roo.bootstrap.MessageBar
27860  * @extends Roo.bootstrap.Component
27861  * Bootstrap MessageBar class
27862  * @cfg {String} html contents of the MessageBar
27863  * @cfg {String} weight (info | success | warning | danger) default info
27864  * @cfg {String} beforeClass insert the bar before the given class
27865  * @cfg {Boolean} closable (true | false) default false
27866  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27867  * 
27868  * @constructor
27869  * Create a new Element
27870  * @param {Object} config The config object
27871  */
27872
27873 Roo.bootstrap.MessageBar = function(config){
27874     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27875 };
27876
27877 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27878     
27879     html: '',
27880     weight: 'info',
27881     closable: false,
27882     fixed: false,
27883     beforeClass: 'bootstrap-sticky-wrap',
27884     
27885     getAutoCreate : function(){
27886         
27887         var cfg = {
27888             tag: 'div',
27889             cls: 'alert alert-dismissable alert-' + this.weight,
27890             cn: [
27891                 {
27892                     tag: 'span',
27893                     cls: 'message',
27894                     html: this.html || ''
27895                 }
27896             ]
27897         };
27898         
27899         if(this.fixed){
27900             cfg.cls += ' alert-messages-fixed';
27901         }
27902         
27903         if(this.closable){
27904             cfg.cn.push({
27905                 tag: 'button',
27906                 cls: 'close',
27907                 html: 'x'
27908             });
27909         }
27910         
27911         return cfg;
27912     },
27913     
27914     onRender : function(ct, position)
27915     {
27916         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27917         
27918         if(!this.el){
27919             var cfg = Roo.apply({},  this.getAutoCreate());
27920             cfg.id = Roo.id();
27921             
27922             if (this.cls) {
27923                 cfg.cls += ' ' + this.cls;
27924             }
27925             if (this.style) {
27926                 cfg.style = this.style;
27927             }
27928             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27929             
27930             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27931         }
27932         
27933         this.el.select('>button.close').on('click', this.hide, this);
27934         
27935     },
27936     
27937     show : function()
27938     {
27939         if (!this.rendered) {
27940             this.render();
27941         }
27942         
27943         this.el.show();
27944         
27945         this.fireEvent('show', this);
27946         
27947     },
27948     
27949     hide : function()
27950     {
27951         if (!this.rendered) {
27952             this.render();
27953         }
27954         
27955         this.el.hide();
27956         
27957         this.fireEvent('hide', this);
27958     },
27959     
27960     update : function()
27961     {
27962 //        var e = this.el.dom.firstChild;
27963 //        
27964 //        if(this.closable){
27965 //            e = e.nextSibling;
27966 //        }
27967 //        
27968 //        e.data = this.html || '';
27969
27970         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27971     }
27972    
27973 });
27974
27975  
27976
27977      /*
27978  * - LGPL
27979  *
27980  * Graph
27981  * 
27982  */
27983
27984
27985 /**
27986  * @class Roo.bootstrap.Graph
27987  * @extends Roo.bootstrap.Component
27988  * Bootstrap Graph class
27989 > Prameters
27990  -sm {number} sm 4
27991  -md {number} md 5
27992  @cfg {String} graphtype  bar | vbar | pie
27993  @cfg {number} g_x coodinator | centre x (pie)
27994  @cfg {number} g_y coodinator | centre y (pie)
27995  @cfg {number} g_r radius (pie)
27996  @cfg {number} g_height height of the chart (respected by all elements in the set)
27997  @cfg {number} g_width width of the chart (respected by all elements in the set)
27998  @cfg {Object} title The title of the chart
27999     
28000  -{Array}  values
28001  -opts (object) options for the chart 
28002      o {
28003      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28004      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28005      o vgutter (number)
28006      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.
28007      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28008      o to
28009      o stretch (boolean)
28010      o }
28011  -opts (object) options for the pie
28012      o{
28013      o cut
28014      o startAngle (number)
28015      o endAngle (number)
28016      } 
28017  *
28018  * @constructor
28019  * Create a new Input
28020  * @param {Object} config The config object
28021  */
28022
28023 Roo.bootstrap.Graph = function(config){
28024     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28025     
28026     this.addEvents({
28027         // img events
28028         /**
28029          * @event click
28030          * The img click event for the img.
28031          * @param {Roo.EventObject} e
28032          */
28033         "click" : true
28034     });
28035 };
28036
28037 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28038     
28039     sm: 4,
28040     md: 5,
28041     graphtype: 'bar',
28042     g_height: 250,
28043     g_width: 400,
28044     g_x: 50,
28045     g_y: 50,
28046     g_r: 30,
28047     opts:{
28048         //g_colors: this.colors,
28049         g_type: 'soft',
28050         g_gutter: '20%'
28051
28052     },
28053     title : false,
28054
28055     getAutoCreate : function(){
28056         
28057         var cfg = {
28058             tag: 'div',
28059             html : null
28060         };
28061         
28062         
28063         return  cfg;
28064     },
28065
28066     onRender : function(ct,position){
28067         
28068         
28069         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28070         
28071         if (typeof(Raphael) == 'undefined') {
28072             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28073             return;
28074         }
28075         
28076         this.raphael = Raphael(this.el.dom);
28077         
28078                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28079                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28080                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28081                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28082                 /*
28083                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28084                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28085                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28086                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28087                 
28088                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28089                 r.barchart(330, 10, 300, 220, data1);
28090                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28091                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28092                 */
28093                 
28094                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28095                 // r.barchart(30, 30, 560, 250,  xdata, {
28096                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28097                 //     axis : "0 0 1 1",
28098                 //     axisxlabels :  xdata
28099                 //     //yvalues : cols,
28100                    
28101                 // });
28102 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28103 //        
28104 //        this.load(null,xdata,{
28105 //                axis : "0 0 1 1",
28106 //                axisxlabels :  xdata
28107 //                });
28108
28109     },
28110
28111     load : function(graphtype,xdata,opts)
28112     {
28113         this.raphael.clear();
28114         if(!graphtype) {
28115             graphtype = this.graphtype;
28116         }
28117         if(!opts){
28118             opts = this.opts;
28119         }
28120         var r = this.raphael,
28121             fin = function () {
28122                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28123             },
28124             fout = function () {
28125                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28126             },
28127             pfin = function() {
28128                 this.sector.stop();
28129                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28130
28131                 if (this.label) {
28132                     this.label[0].stop();
28133                     this.label[0].attr({ r: 7.5 });
28134                     this.label[1].attr({ "font-weight": 800 });
28135                 }
28136             },
28137             pfout = function() {
28138                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28139
28140                 if (this.label) {
28141                     this.label[0].animate({ r: 5 }, 500, "bounce");
28142                     this.label[1].attr({ "font-weight": 400 });
28143                 }
28144             };
28145
28146         switch(graphtype){
28147             case 'bar':
28148                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28149                 break;
28150             case 'hbar':
28151                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28152                 break;
28153             case 'pie':
28154 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28155 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28156 //            
28157                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28158                 
28159                 break;
28160
28161         }
28162         
28163         if(this.title){
28164             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28165         }
28166         
28167     },
28168     
28169     setTitle: function(o)
28170     {
28171         this.title = o;
28172     },
28173     
28174     initEvents: function() {
28175         
28176         if(!this.href){
28177             this.el.on('click', this.onClick, this);
28178         }
28179     },
28180     
28181     onClick : function(e)
28182     {
28183         Roo.log('img onclick');
28184         this.fireEvent('click', this, e);
28185     }
28186    
28187 });
28188
28189  
28190 /*
28191  * - LGPL
28192  *
28193  * numberBox
28194  * 
28195  */
28196 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28197
28198 /**
28199  * @class Roo.bootstrap.dash.NumberBox
28200  * @extends Roo.bootstrap.Component
28201  * Bootstrap NumberBox class
28202  * @cfg {String} headline Box headline
28203  * @cfg {String} content Box content
28204  * @cfg {String} icon Box icon
28205  * @cfg {String} footer Footer text
28206  * @cfg {String} fhref Footer href
28207  * 
28208  * @constructor
28209  * Create a new NumberBox
28210  * @param {Object} config The config object
28211  */
28212
28213
28214 Roo.bootstrap.dash.NumberBox = function(config){
28215     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28216     
28217 };
28218
28219 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28220     
28221     headline : '',
28222     content : '',
28223     icon : '',
28224     footer : '',
28225     fhref : '',
28226     ficon : '',
28227     
28228     getAutoCreate : function(){
28229         
28230         var cfg = {
28231             tag : 'div',
28232             cls : 'small-box ',
28233             cn : [
28234                 {
28235                     tag : 'div',
28236                     cls : 'inner',
28237                     cn :[
28238                         {
28239                             tag : 'h3',
28240                             cls : 'roo-headline',
28241                             html : this.headline
28242                         },
28243                         {
28244                             tag : 'p',
28245                             cls : 'roo-content',
28246                             html : this.content
28247                         }
28248                     ]
28249                 }
28250             ]
28251         };
28252         
28253         if(this.icon){
28254             cfg.cn.push({
28255                 tag : 'div',
28256                 cls : 'icon',
28257                 cn :[
28258                     {
28259                         tag : 'i',
28260                         cls : 'ion ' + this.icon
28261                     }
28262                 ]
28263             });
28264         }
28265         
28266         if(this.footer){
28267             var footer = {
28268                 tag : 'a',
28269                 cls : 'small-box-footer',
28270                 href : this.fhref || '#',
28271                 html : this.footer
28272             };
28273             
28274             cfg.cn.push(footer);
28275             
28276         }
28277         
28278         return  cfg;
28279     },
28280
28281     onRender : function(ct,position){
28282         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28283
28284
28285        
28286                 
28287     },
28288
28289     setHeadline: function (value)
28290     {
28291         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28292     },
28293     
28294     setFooter: function (value, href)
28295     {
28296         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28297         
28298         if(href){
28299             this.el.select('a.small-box-footer',true).first().attr('href', href);
28300         }
28301         
28302     },
28303
28304     setContent: function (value)
28305     {
28306         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28307     },
28308
28309     initEvents: function() 
28310     {   
28311         
28312     }
28313     
28314 });
28315
28316  
28317 /*
28318  * - LGPL
28319  *
28320  * TabBox
28321  * 
28322  */
28323 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28324
28325 /**
28326  * @class Roo.bootstrap.dash.TabBox
28327  * @extends Roo.bootstrap.Component
28328  * Bootstrap TabBox class
28329  * @cfg {String} title Title of the TabBox
28330  * @cfg {String} icon Icon of the TabBox
28331  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28332  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28333  * 
28334  * @constructor
28335  * Create a new TabBox
28336  * @param {Object} config The config object
28337  */
28338
28339
28340 Roo.bootstrap.dash.TabBox = function(config){
28341     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28342     this.addEvents({
28343         // raw events
28344         /**
28345          * @event addpane
28346          * When a pane is added
28347          * @param {Roo.bootstrap.dash.TabPane} pane
28348          */
28349         "addpane" : true,
28350         /**
28351          * @event activatepane
28352          * When a pane is activated
28353          * @param {Roo.bootstrap.dash.TabPane} pane
28354          */
28355         "activatepane" : true
28356         
28357          
28358     });
28359     
28360     this.panes = [];
28361 };
28362
28363 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28364
28365     title : '',
28366     icon : false,
28367     showtabs : true,
28368     tabScrollable : false,
28369     
28370     getChildContainer : function()
28371     {
28372         return this.el.select('.tab-content', true).first();
28373     },
28374     
28375     getAutoCreate : function(){
28376         
28377         var header = {
28378             tag: 'li',
28379             cls: 'pull-left header',
28380             html: this.title,
28381             cn : []
28382         };
28383         
28384         if(this.icon){
28385             header.cn.push({
28386                 tag: 'i',
28387                 cls: 'fa ' + this.icon
28388             });
28389         }
28390         
28391         var h = {
28392             tag: 'ul',
28393             cls: 'nav nav-tabs pull-right',
28394             cn: [
28395                 header
28396             ]
28397         };
28398         
28399         if(this.tabScrollable){
28400             h = {
28401                 tag: 'div',
28402                 cls: 'tab-header',
28403                 cn: [
28404                     {
28405                         tag: 'ul',
28406                         cls: 'nav nav-tabs pull-right',
28407                         cn: [
28408                             header
28409                         ]
28410                     }
28411                 ]
28412             };
28413         }
28414         
28415         var cfg = {
28416             tag: 'div',
28417             cls: 'nav-tabs-custom',
28418             cn: [
28419                 h,
28420                 {
28421                     tag: 'div',
28422                     cls: 'tab-content no-padding',
28423                     cn: []
28424                 }
28425             ]
28426         };
28427
28428         return  cfg;
28429     },
28430     initEvents : function()
28431     {
28432         //Roo.log('add add pane handler');
28433         this.on('addpane', this.onAddPane, this);
28434     },
28435      /**
28436      * Updates the box title
28437      * @param {String} html to set the title to.
28438      */
28439     setTitle : function(value)
28440     {
28441         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28442     },
28443     onAddPane : function(pane)
28444     {
28445         this.panes.push(pane);
28446         //Roo.log('addpane');
28447         //Roo.log(pane);
28448         // tabs are rendere left to right..
28449         if(!this.showtabs){
28450             return;
28451         }
28452         
28453         var ctr = this.el.select('.nav-tabs', true).first();
28454          
28455          
28456         var existing = ctr.select('.nav-tab',true);
28457         var qty = existing.getCount();;
28458         
28459         
28460         var tab = ctr.createChild({
28461             tag : 'li',
28462             cls : 'nav-tab' + (qty ? '' : ' active'),
28463             cn : [
28464                 {
28465                     tag : 'a',
28466                     href:'#',
28467                     html : pane.title
28468                 }
28469             ]
28470         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28471         pane.tab = tab;
28472         
28473         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28474         if (!qty) {
28475             pane.el.addClass('active');
28476         }
28477         
28478                 
28479     },
28480     onTabClick : function(ev,un,ob,pane)
28481     {
28482         //Roo.log('tab - prev default');
28483         ev.preventDefault();
28484         
28485         
28486         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28487         pane.tab.addClass('active');
28488         //Roo.log(pane.title);
28489         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28490         // technically we should have a deactivate event.. but maybe add later.
28491         // and it should not de-activate the selected tab...
28492         this.fireEvent('activatepane', pane);
28493         pane.el.addClass('active');
28494         pane.fireEvent('activate');
28495         
28496         
28497     },
28498     
28499     getActivePane : function()
28500     {
28501         var r = false;
28502         Roo.each(this.panes, function(p) {
28503             if(p.el.hasClass('active')){
28504                 r = p;
28505                 return false;
28506             }
28507             
28508             return;
28509         });
28510         
28511         return r;
28512     }
28513     
28514     
28515 });
28516
28517  
28518 /*
28519  * - LGPL
28520  *
28521  * Tab pane
28522  * 
28523  */
28524 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28525 /**
28526  * @class Roo.bootstrap.TabPane
28527  * @extends Roo.bootstrap.Component
28528  * Bootstrap TabPane class
28529  * @cfg {Boolean} active (false | true) Default false
28530  * @cfg {String} title title of panel
28531
28532  * 
28533  * @constructor
28534  * Create a new TabPane
28535  * @param {Object} config The config object
28536  */
28537
28538 Roo.bootstrap.dash.TabPane = function(config){
28539     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28540     
28541     this.addEvents({
28542         // raw events
28543         /**
28544          * @event activate
28545          * When a pane is activated
28546          * @param {Roo.bootstrap.dash.TabPane} pane
28547          */
28548         "activate" : true
28549          
28550     });
28551 };
28552
28553 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28554     
28555     active : false,
28556     title : '',
28557     
28558     // the tabBox that this is attached to.
28559     tab : false,
28560      
28561     getAutoCreate : function() 
28562     {
28563         var cfg = {
28564             tag: 'div',
28565             cls: 'tab-pane'
28566         };
28567         
28568         if(this.active){
28569             cfg.cls += ' active';
28570         }
28571         
28572         return cfg;
28573     },
28574     initEvents  : function()
28575     {
28576         //Roo.log('trigger add pane handler');
28577         this.parent().fireEvent('addpane', this)
28578     },
28579     
28580      /**
28581      * Updates the tab title 
28582      * @param {String} html to set the title to.
28583      */
28584     setTitle: function(str)
28585     {
28586         if (!this.tab) {
28587             return;
28588         }
28589         this.title = str;
28590         this.tab.select('a', true).first().dom.innerHTML = str;
28591         
28592     }
28593     
28594     
28595     
28596 });
28597
28598  
28599
28600
28601  /*
28602  * - LGPL
28603  *
28604  * menu
28605  * 
28606  */
28607 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28608
28609 /**
28610  * @class Roo.bootstrap.menu.Menu
28611  * @extends Roo.bootstrap.Component
28612  * Bootstrap Menu class - container for Menu
28613  * @cfg {String} html Text of the menu
28614  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28615  * @cfg {String} icon Font awesome icon
28616  * @cfg {String} pos Menu align to (top | bottom) default bottom
28617  * 
28618  * 
28619  * @constructor
28620  * Create a new Menu
28621  * @param {Object} config The config object
28622  */
28623
28624
28625 Roo.bootstrap.menu.Menu = function(config){
28626     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28627     
28628     this.addEvents({
28629         /**
28630          * @event beforeshow
28631          * Fires before this menu is displayed
28632          * @param {Roo.bootstrap.menu.Menu} this
28633          */
28634         beforeshow : true,
28635         /**
28636          * @event beforehide
28637          * Fires before this menu is hidden
28638          * @param {Roo.bootstrap.menu.Menu} this
28639          */
28640         beforehide : true,
28641         /**
28642          * @event show
28643          * Fires after this menu is displayed
28644          * @param {Roo.bootstrap.menu.Menu} this
28645          */
28646         show : true,
28647         /**
28648          * @event hide
28649          * Fires after this menu is hidden
28650          * @param {Roo.bootstrap.menu.Menu} this
28651          */
28652         hide : true,
28653         /**
28654          * @event click
28655          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28656          * @param {Roo.bootstrap.menu.Menu} this
28657          * @param {Roo.EventObject} e
28658          */
28659         click : true
28660     });
28661     
28662 };
28663
28664 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28665     
28666     submenu : false,
28667     html : '',
28668     weight : 'default',
28669     icon : false,
28670     pos : 'bottom',
28671     
28672     
28673     getChildContainer : function() {
28674         if(this.isSubMenu){
28675             return this.el;
28676         }
28677         
28678         return this.el.select('ul.dropdown-menu', true).first();  
28679     },
28680     
28681     getAutoCreate : function()
28682     {
28683         var text = [
28684             {
28685                 tag : 'span',
28686                 cls : 'roo-menu-text',
28687                 html : this.html
28688             }
28689         ];
28690         
28691         if(this.icon){
28692             text.unshift({
28693                 tag : 'i',
28694                 cls : 'fa ' + this.icon
28695             })
28696         }
28697         
28698         
28699         var cfg = {
28700             tag : 'div',
28701             cls : 'btn-group',
28702             cn : [
28703                 {
28704                     tag : 'button',
28705                     cls : 'dropdown-button btn btn-' + this.weight,
28706                     cn : text
28707                 },
28708                 {
28709                     tag : 'button',
28710                     cls : 'dropdown-toggle btn btn-' + this.weight,
28711                     cn : [
28712                         {
28713                             tag : 'span',
28714                             cls : 'caret'
28715                         }
28716                     ]
28717                 },
28718                 {
28719                     tag : 'ul',
28720                     cls : 'dropdown-menu'
28721                 }
28722             ]
28723             
28724         };
28725         
28726         if(this.pos == 'top'){
28727             cfg.cls += ' dropup';
28728         }
28729         
28730         if(this.isSubMenu){
28731             cfg = {
28732                 tag : 'ul',
28733                 cls : 'dropdown-menu'
28734             }
28735         }
28736         
28737         return cfg;
28738     },
28739     
28740     onRender : function(ct, position)
28741     {
28742         this.isSubMenu = ct.hasClass('dropdown-submenu');
28743         
28744         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28745     },
28746     
28747     initEvents : function() 
28748     {
28749         if(this.isSubMenu){
28750             return;
28751         }
28752         
28753         this.hidden = true;
28754         
28755         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28756         this.triggerEl.on('click', this.onTriggerPress, this);
28757         
28758         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28759         this.buttonEl.on('click', this.onClick, this);
28760         
28761     },
28762     
28763     list : function()
28764     {
28765         if(this.isSubMenu){
28766             return this.el;
28767         }
28768         
28769         return this.el.select('ul.dropdown-menu', true).first();
28770     },
28771     
28772     onClick : function(e)
28773     {
28774         this.fireEvent("click", this, e);
28775     },
28776     
28777     onTriggerPress  : function(e)
28778     {   
28779         if (this.isVisible()) {
28780             this.hide();
28781         } else {
28782             this.show();
28783         }
28784     },
28785     
28786     isVisible : function(){
28787         return !this.hidden;
28788     },
28789     
28790     show : function()
28791     {
28792         this.fireEvent("beforeshow", this);
28793         
28794         this.hidden = false;
28795         this.el.addClass('open');
28796         
28797         Roo.get(document).on("mouseup", this.onMouseUp, this);
28798         
28799         this.fireEvent("show", this);
28800         
28801         
28802     },
28803     
28804     hide : function()
28805     {
28806         this.fireEvent("beforehide", this);
28807         
28808         this.hidden = true;
28809         this.el.removeClass('open');
28810         
28811         Roo.get(document).un("mouseup", this.onMouseUp);
28812         
28813         this.fireEvent("hide", this);
28814     },
28815     
28816     onMouseUp : function()
28817     {
28818         this.hide();
28819     }
28820     
28821 });
28822
28823  
28824  /*
28825  * - LGPL
28826  *
28827  * menu item
28828  * 
28829  */
28830 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28831
28832 /**
28833  * @class Roo.bootstrap.menu.Item
28834  * @extends Roo.bootstrap.Component
28835  * Bootstrap MenuItem class
28836  * @cfg {Boolean} submenu (true | false) default false
28837  * @cfg {String} html text of the item
28838  * @cfg {String} href the link
28839  * @cfg {Boolean} disable (true | false) default false
28840  * @cfg {Boolean} preventDefault (true | false) default true
28841  * @cfg {String} icon Font awesome icon
28842  * @cfg {String} pos Submenu align to (left | right) default right 
28843  * 
28844  * 
28845  * @constructor
28846  * Create a new Item
28847  * @param {Object} config The config object
28848  */
28849
28850
28851 Roo.bootstrap.menu.Item = function(config){
28852     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28853     this.addEvents({
28854         /**
28855          * @event mouseover
28856          * Fires when the mouse is hovering over this menu
28857          * @param {Roo.bootstrap.menu.Item} this
28858          * @param {Roo.EventObject} e
28859          */
28860         mouseover : true,
28861         /**
28862          * @event mouseout
28863          * Fires when the mouse exits this menu
28864          * @param {Roo.bootstrap.menu.Item} this
28865          * @param {Roo.EventObject} e
28866          */
28867         mouseout : true,
28868         // raw events
28869         /**
28870          * @event click
28871          * The raw click event for the entire grid.
28872          * @param {Roo.EventObject} e
28873          */
28874         click : true
28875     });
28876 };
28877
28878 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28879     
28880     submenu : false,
28881     href : '',
28882     html : '',
28883     preventDefault: true,
28884     disable : false,
28885     icon : false,
28886     pos : 'right',
28887     
28888     getAutoCreate : function()
28889     {
28890         var text = [
28891             {
28892                 tag : 'span',
28893                 cls : 'roo-menu-item-text',
28894                 html : this.html
28895             }
28896         ];
28897         
28898         if(this.icon){
28899             text.unshift({
28900                 tag : 'i',
28901                 cls : 'fa ' + this.icon
28902             })
28903         }
28904         
28905         var cfg = {
28906             tag : 'li',
28907             cn : [
28908                 {
28909                     tag : 'a',
28910                     href : this.href || '#',
28911                     cn : text
28912                 }
28913             ]
28914         };
28915         
28916         if(this.disable){
28917             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28918         }
28919         
28920         if(this.submenu){
28921             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28922             
28923             if(this.pos == 'left'){
28924                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28925             }
28926         }
28927         
28928         return cfg;
28929     },
28930     
28931     initEvents : function() 
28932     {
28933         this.el.on('mouseover', this.onMouseOver, this);
28934         this.el.on('mouseout', this.onMouseOut, this);
28935         
28936         this.el.select('a', true).first().on('click', this.onClick, this);
28937         
28938     },
28939     
28940     onClick : function(e)
28941     {
28942         if(this.preventDefault){
28943             e.preventDefault();
28944         }
28945         
28946         this.fireEvent("click", this, e);
28947     },
28948     
28949     onMouseOver : function(e)
28950     {
28951         if(this.submenu && this.pos == 'left'){
28952             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28953         }
28954         
28955         this.fireEvent("mouseover", this, e);
28956     },
28957     
28958     onMouseOut : function(e)
28959     {
28960         this.fireEvent("mouseout", this, e);
28961     }
28962 });
28963
28964  
28965
28966  /*
28967  * - LGPL
28968  *
28969  * menu separator
28970  * 
28971  */
28972 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28973
28974 /**
28975  * @class Roo.bootstrap.menu.Separator
28976  * @extends Roo.bootstrap.Component
28977  * Bootstrap Separator class
28978  * 
28979  * @constructor
28980  * Create a new Separator
28981  * @param {Object} config The config object
28982  */
28983
28984
28985 Roo.bootstrap.menu.Separator = function(config){
28986     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28987 };
28988
28989 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28990     
28991     getAutoCreate : function(){
28992         var cfg = {
28993             tag : 'li',
28994             cls: 'dropdown-divider divider'
28995         };
28996         
28997         return cfg;
28998     }
28999    
29000 });
29001
29002  
29003
29004  /*
29005  * - LGPL
29006  *
29007  * Tooltip
29008  * 
29009  */
29010
29011 /**
29012  * @class Roo.bootstrap.Tooltip
29013  * Bootstrap Tooltip class
29014  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29015  * to determine which dom element triggers the tooltip.
29016  * 
29017  * It needs to add support for additional attributes like tooltip-position
29018  * 
29019  * @constructor
29020  * Create a new Toolti
29021  * @param {Object} config The config object
29022  */
29023
29024 Roo.bootstrap.Tooltip = function(config){
29025     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29026     
29027     this.alignment = Roo.bootstrap.Tooltip.alignment;
29028     
29029     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29030         this.alignment = config.alignment;
29031     }
29032     
29033 };
29034
29035 Roo.apply(Roo.bootstrap.Tooltip, {
29036     /**
29037      * @function init initialize tooltip monitoring.
29038      * @static
29039      */
29040     currentEl : false,
29041     currentTip : false,
29042     currentRegion : false,
29043     
29044     //  init : delay?
29045     
29046     init : function()
29047     {
29048         Roo.get(document).on('mouseover', this.enter ,this);
29049         Roo.get(document).on('mouseout', this.leave, this);
29050          
29051         
29052         this.currentTip = new Roo.bootstrap.Tooltip();
29053     },
29054     
29055     enter : function(ev)
29056     {
29057         var dom = ev.getTarget();
29058         
29059         //Roo.log(['enter',dom]);
29060         var el = Roo.fly(dom);
29061         if (this.currentEl) {
29062             //Roo.log(dom);
29063             //Roo.log(this.currentEl);
29064             //Roo.log(this.currentEl.contains(dom));
29065             if (this.currentEl == el) {
29066                 return;
29067             }
29068             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29069                 return;
29070             }
29071
29072         }
29073         
29074         if (this.currentTip.el) {
29075             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29076         }    
29077         //Roo.log(ev);
29078         
29079         if(!el || el.dom == document){
29080             return;
29081         }
29082         
29083         var bindEl = el;
29084         
29085         // you can not look for children, as if el is the body.. then everythign is the child..
29086         if (!el.attr('tooltip')) { //
29087             if (!el.select("[tooltip]").elements.length) {
29088                 return;
29089             }
29090             // is the mouse over this child...?
29091             bindEl = el.select("[tooltip]").first();
29092             var xy = ev.getXY();
29093             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29094                 //Roo.log("not in region.");
29095                 return;
29096             }
29097             //Roo.log("child element over..");
29098             
29099         }
29100         this.currentEl = bindEl;
29101         this.currentTip.bind(bindEl);
29102         this.currentRegion = Roo.lib.Region.getRegion(dom);
29103         this.currentTip.enter();
29104         
29105     },
29106     leave : function(ev)
29107     {
29108         var dom = ev.getTarget();
29109         //Roo.log(['leave',dom]);
29110         if (!this.currentEl) {
29111             return;
29112         }
29113         
29114         
29115         if (dom != this.currentEl.dom) {
29116             return;
29117         }
29118         var xy = ev.getXY();
29119         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29120             return;
29121         }
29122         // only activate leave if mouse cursor is outside... bounding box..
29123         
29124         
29125         
29126         
29127         if (this.currentTip) {
29128             this.currentTip.leave();
29129         }
29130         //Roo.log('clear currentEl');
29131         this.currentEl = false;
29132         
29133         
29134     },
29135     alignment : {
29136         'left' : ['r-l', [-2,0], 'right'],
29137         'right' : ['l-r', [2,0], 'left'],
29138         'bottom' : ['t-b', [0,2], 'top'],
29139         'top' : [ 'b-t', [0,-2], 'bottom']
29140     }
29141     
29142 });
29143
29144
29145 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29146     
29147     
29148     bindEl : false,
29149     
29150     delay : null, // can be { show : 300 , hide: 500}
29151     
29152     timeout : null,
29153     
29154     hoverState : null, //???
29155     
29156     placement : 'bottom', 
29157     
29158     alignment : false,
29159     
29160     getAutoCreate : function(){
29161     
29162         var cfg = {
29163            cls : 'tooltip',   
29164            role : 'tooltip',
29165            cn : [
29166                 {
29167                     cls : 'tooltip-arrow arrow'
29168                 },
29169                 {
29170                     cls : 'tooltip-inner'
29171                 }
29172            ]
29173         };
29174         
29175         return cfg;
29176     },
29177     bind : function(el)
29178     {
29179         this.bindEl = el;
29180     },
29181     
29182     initEvents : function()
29183     {
29184         this.arrowEl = this.el.select('.arrow', true).first();
29185         this.innerEl = this.el.select('.tooltip-inner', true).first();
29186     },
29187     
29188     enter : function () {
29189        
29190         if (this.timeout != null) {
29191             clearTimeout(this.timeout);
29192         }
29193         
29194         this.hoverState = 'in';
29195          //Roo.log("enter - show");
29196         if (!this.delay || !this.delay.show) {
29197             this.show();
29198             return;
29199         }
29200         var _t = this;
29201         this.timeout = setTimeout(function () {
29202             if (_t.hoverState == 'in') {
29203                 _t.show();
29204             }
29205         }, this.delay.show);
29206     },
29207     leave : function()
29208     {
29209         clearTimeout(this.timeout);
29210     
29211         this.hoverState = 'out';
29212          if (!this.delay || !this.delay.hide) {
29213             this.hide();
29214             return;
29215         }
29216        
29217         var _t = this;
29218         this.timeout = setTimeout(function () {
29219             //Roo.log("leave - timeout");
29220             
29221             if (_t.hoverState == 'out') {
29222                 _t.hide();
29223                 Roo.bootstrap.Tooltip.currentEl = false;
29224             }
29225         }, delay);
29226     },
29227     
29228     show : function (msg)
29229     {
29230         if (!this.el) {
29231             this.render(document.body);
29232         }
29233         // set content.
29234         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29235         
29236         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29237         
29238         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29239         
29240         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29241                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29242         
29243         var placement = typeof this.placement == 'function' ?
29244             this.placement.call(this, this.el, on_el) :
29245             this.placement;
29246             
29247         var autoToken = /\s?auto?\s?/i;
29248         var autoPlace = autoToken.test(placement);
29249         if (autoPlace) {
29250             placement = placement.replace(autoToken, '') || 'top';
29251         }
29252         
29253         //this.el.detach()
29254         //this.el.setXY([0,0]);
29255         this.el.show();
29256         //this.el.dom.style.display='block';
29257         
29258         //this.el.appendTo(on_el);
29259         
29260         var p = this.getPosition();
29261         var box = this.el.getBox();
29262         
29263         if (autoPlace) {
29264             // fixme..
29265         }
29266         
29267         var align = this.alignment[placement];
29268         
29269         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29270         
29271         if(placement == 'top' || placement == 'bottom'){
29272             if(xy[0] < 0){
29273                 placement = 'right';
29274             }
29275             
29276             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29277                 placement = 'left';
29278             }
29279             
29280             var scroll = Roo.select('body', true).first().getScroll();
29281             
29282             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29283                 placement = 'top';
29284             }
29285             
29286             align = this.alignment[placement];
29287             
29288             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29289             
29290         }
29291         
29292         this.el.alignTo(this.bindEl, align[0],align[1]);
29293         //var arrow = this.el.select('.arrow',true).first();
29294         //arrow.set(align[2], 
29295         
29296         this.el.addClass(placement);
29297         this.el.addClass("bs-tooltip-"+ placement);
29298         
29299         this.el.addClass('in fade show');
29300         
29301         this.hoverState = null;
29302         
29303         if (this.el.hasClass('fade')) {
29304             // fade it?
29305         }
29306         
29307         
29308         
29309         
29310         
29311     },
29312     hide : function()
29313     {
29314          
29315         if (!this.el) {
29316             return;
29317         }
29318         //this.el.setXY([0,0]);
29319         this.el.removeClass(['show', 'in']);
29320         //this.el.hide();
29321         
29322     }
29323     
29324 });
29325  
29326
29327  /*
29328  * - LGPL
29329  *
29330  * Location Picker
29331  * 
29332  */
29333
29334 /**
29335  * @class Roo.bootstrap.LocationPicker
29336  * @extends Roo.bootstrap.Component
29337  * Bootstrap LocationPicker class
29338  * @cfg {Number} latitude Position when init default 0
29339  * @cfg {Number} longitude Position when init default 0
29340  * @cfg {Number} zoom default 15
29341  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29342  * @cfg {Boolean} mapTypeControl default false
29343  * @cfg {Boolean} disableDoubleClickZoom default false
29344  * @cfg {Boolean} scrollwheel default true
29345  * @cfg {Boolean} streetViewControl default false
29346  * @cfg {Number} radius default 0
29347  * @cfg {String} locationName
29348  * @cfg {Boolean} draggable default true
29349  * @cfg {Boolean} enableAutocomplete default false
29350  * @cfg {Boolean} enableReverseGeocode default true
29351  * @cfg {String} markerTitle
29352  * 
29353  * @constructor
29354  * Create a new LocationPicker
29355  * @param {Object} config The config object
29356  */
29357
29358
29359 Roo.bootstrap.LocationPicker = function(config){
29360     
29361     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29362     
29363     this.addEvents({
29364         /**
29365          * @event initial
29366          * Fires when the picker initialized.
29367          * @param {Roo.bootstrap.LocationPicker} this
29368          * @param {Google Location} location
29369          */
29370         initial : true,
29371         /**
29372          * @event positionchanged
29373          * Fires when the picker position changed.
29374          * @param {Roo.bootstrap.LocationPicker} this
29375          * @param {Google Location} location
29376          */
29377         positionchanged : true,
29378         /**
29379          * @event resize
29380          * Fires when the map resize.
29381          * @param {Roo.bootstrap.LocationPicker} this
29382          */
29383         resize : true,
29384         /**
29385          * @event show
29386          * Fires when the map show.
29387          * @param {Roo.bootstrap.LocationPicker} this
29388          */
29389         show : true,
29390         /**
29391          * @event hide
29392          * Fires when the map hide.
29393          * @param {Roo.bootstrap.LocationPicker} this
29394          */
29395         hide : true,
29396         /**
29397          * @event mapClick
29398          * Fires when click the map.
29399          * @param {Roo.bootstrap.LocationPicker} this
29400          * @param {Map event} e
29401          */
29402         mapClick : true,
29403         /**
29404          * @event mapRightClick
29405          * Fires when right click the map.
29406          * @param {Roo.bootstrap.LocationPicker} this
29407          * @param {Map event} e
29408          */
29409         mapRightClick : true,
29410         /**
29411          * @event markerClick
29412          * Fires when click the marker.
29413          * @param {Roo.bootstrap.LocationPicker} this
29414          * @param {Map event} e
29415          */
29416         markerClick : true,
29417         /**
29418          * @event markerRightClick
29419          * Fires when right click the marker.
29420          * @param {Roo.bootstrap.LocationPicker} this
29421          * @param {Map event} e
29422          */
29423         markerRightClick : true,
29424         /**
29425          * @event OverlayViewDraw
29426          * Fires when OverlayView Draw
29427          * @param {Roo.bootstrap.LocationPicker} this
29428          */
29429         OverlayViewDraw : true,
29430         /**
29431          * @event OverlayViewOnAdd
29432          * Fires when OverlayView Draw
29433          * @param {Roo.bootstrap.LocationPicker} this
29434          */
29435         OverlayViewOnAdd : true,
29436         /**
29437          * @event OverlayViewOnRemove
29438          * Fires when OverlayView Draw
29439          * @param {Roo.bootstrap.LocationPicker} this
29440          */
29441         OverlayViewOnRemove : true,
29442         /**
29443          * @event OverlayViewShow
29444          * Fires when OverlayView Draw
29445          * @param {Roo.bootstrap.LocationPicker} this
29446          * @param {Pixel} cpx
29447          */
29448         OverlayViewShow : true,
29449         /**
29450          * @event OverlayViewHide
29451          * Fires when OverlayView Draw
29452          * @param {Roo.bootstrap.LocationPicker} this
29453          */
29454         OverlayViewHide : true,
29455         /**
29456          * @event loadexception
29457          * Fires when load google lib failed.
29458          * @param {Roo.bootstrap.LocationPicker} this
29459          */
29460         loadexception : true
29461     });
29462         
29463 };
29464
29465 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29466     
29467     gMapContext: false,
29468     
29469     latitude: 0,
29470     longitude: 0,
29471     zoom: 15,
29472     mapTypeId: false,
29473     mapTypeControl: false,
29474     disableDoubleClickZoom: false,
29475     scrollwheel: true,
29476     streetViewControl: false,
29477     radius: 0,
29478     locationName: '',
29479     draggable: true,
29480     enableAutocomplete: false,
29481     enableReverseGeocode: true,
29482     markerTitle: '',
29483     
29484     getAutoCreate: function()
29485     {
29486
29487         var cfg = {
29488             tag: 'div',
29489             cls: 'roo-location-picker'
29490         };
29491         
29492         return cfg
29493     },
29494     
29495     initEvents: function(ct, position)
29496     {       
29497         if(!this.el.getWidth() || this.isApplied()){
29498             return;
29499         }
29500         
29501         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29502         
29503         this.initial();
29504     },
29505     
29506     initial: function()
29507     {
29508         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29509             this.fireEvent('loadexception', this);
29510             return;
29511         }
29512         
29513         if(!this.mapTypeId){
29514             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29515         }
29516         
29517         this.gMapContext = this.GMapContext();
29518         
29519         this.initOverlayView();
29520         
29521         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29522         
29523         var _this = this;
29524                 
29525         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29526             _this.setPosition(_this.gMapContext.marker.position);
29527         });
29528         
29529         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29530             _this.fireEvent('mapClick', this, event);
29531             
29532         });
29533
29534         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29535             _this.fireEvent('mapRightClick', this, event);
29536             
29537         });
29538         
29539         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29540             _this.fireEvent('markerClick', this, event);
29541             
29542         });
29543
29544         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29545             _this.fireEvent('markerRightClick', this, event);
29546             
29547         });
29548         
29549         this.setPosition(this.gMapContext.location);
29550         
29551         this.fireEvent('initial', this, this.gMapContext.location);
29552     },
29553     
29554     initOverlayView: function()
29555     {
29556         var _this = this;
29557         
29558         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29559             
29560             draw: function()
29561             {
29562                 _this.fireEvent('OverlayViewDraw', _this);
29563             },
29564             
29565             onAdd: function()
29566             {
29567                 _this.fireEvent('OverlayViewOnAdd', _this);
29568             },
29569             
29570             onRemove: function()
29571             {
29572                 _this.fireEvent('OverlayViewOnRemove', _this);
29573             },
29574             
29575             show: function(cpx)
29576             {
29577                 _this.fireEvent('OverlayViewShow', _this, cpx);
29578             },
29579             
29580             hide: function()
29581             {
29582                 _this.fireEvent('OverlayViewHide', _this);
29583             }
29584             
29585         });
29586     },
29587     
29588     fromLatLngToContainerPixel: function(event)
29589     {
29590         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29591     },
29592     
29593     isApplied: function() 
29594     {
29595         return this.getGmapContext() == false ? false : true;
29596     },
29597     
29598     getGmapContext: function() 
29599     {
29600         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29601     },
29602     
29603     GMapContext: function() 
29604     {
29605         var position = new google.maps.LatLng(this.latitude, this.longitude);
29606         
29607         var _map = new google.maps.Map(this.el.dom, {
29608             center: position,
29609             zoom: this.zoom,
29610             mapTypeId: this.mapTypeId,
29611             mapTypeControl: this.mapTypeControl,
29612             disableDoubleClickZoom: this.disableDoubleClickZoom,
29613             scrollwheel: this.scrollwheel,
29614             streetViewControl: this.streetViewControl,
29615             locationName: this.locationName,
29616             draggable: this.draggable,
29617             enableAutocomplete: this.enableAutocomplete,
29618             enableReverseGeocode: this.enableReverseGeocode
29619         });
29620         
29621         var _marker = new google.maps.Marker({
29622             position: position,
29623             map: _map,
29624             title: this.markerTitle,
29625             draggable: this.draggable
29626         });
29627         
29628         return {
29629             map: _map,
29630             marker: _marker,
29631             circle: null,
29632             location: position,
29633             radius: this.radius,
29634             locationName: this.locationName,
29635             addressComponents: {
29636                 formatted_address: null,
29637                 addressLine1: null,
29638                 addressLine2: null,
29639                 streetName: null,
29640                 streetNumber: null,
29641                 city: null,
29642                 district: null,
29643                 state: null,
29644                 stateOrProvince: null
29645             },
29646             settings: this,
29647             domContainer: this.el.dom,
29648             geodecoder: new google.maps.Geocoder()
29649         };
29650     },
29651     
29652     drawCircle: function(center, radius, options) 
29653     {
29654         if (this.gMapContext.circle != null) {
29655             this.gMapContext.circle.setMap(null);
29656         }
29657         if (radius > 0) {
29658             radius *= 1;
29659             options = Roo.apply({}, options, {
29660                 strokeColor: "#0000FF",
29661                 strokeOpacity: .35,
29662                 strokeWeight: 2,
29663                 fillColor: "#0000FF",
29664                 fillOpacity: .2
29665             });
29666             
29667             options.map = this.gMapContext.map;
29668             options.radius = radius;
29669             options.center = center;
29670             this.gMapContext.circle = new google.maps.Circle(options);
29671             return this.gMapContext.circle;
29672         }
29673         
29674         return null;
29675     },
29676     
29677     setPosition: function(location) 
29678     {
29679         this.gMapContext.location = location;
29680         this.gMapContext.marker.setPosition(location);
29681         this.gMapContext.map.panTo(location);
29682         this.drawCircle(location, this.gMapContext.radius, {});
29683         
29684         var _this = this;
29685         
29686         if (this.gMapContext.settings.enableReverseGeocode) {
29687             this.gMapContext.geodecoder.geocode({
29688                 latLng: this.gMapContext.location
29689             }, function(results, status) {
29690                 
29691                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29692                     _this.gMapContext.locationName = results[0].formatted_address;
29693                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29694                     
29695                     _this.fireEvent('positionchanged', this, location);
29696                 }
29697             });
29698             
29699             return;
29700         }
29701         
29702         this.fireEvent('positionchanged', this, location);
29703     },
29704     
29705     resize: function()
29706     {
29707         google.maps.event.trigger(this.gMapContext.map, "resize");
29708         
29709         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29710         
29711         this.fireEvent('resize', this);
29712     },
29713     
29714     setPositionByLatLng: function(latitude, longitude)
29715     {
29716         this.setPosition(new google.maps.LatLng(latitude, longitude));
29717     },
29718     
29719     getCurrentPosition: function() 
29720     {
29721         return {
29722             latitude: this.gMapContext.location.lat(),
29723             longitude: this.gMapContext.location.lng()
29724         };
29725     },
29726     
29727     getAddressName: function() 
29728     {
29729         return this.gMapContext.locationName;
29730     },
29731     
29732     getAddressComponents: function() 
29733     {
29734         return this.gMapContext.addressComponents;
29735     },
29736     
29737     address_component_from_google_geocode: function(address_components) 
29738     {
29739         var result = {};
29740         
29741         for (var i = 0; i < address_components.length; i++) {
29742             var component = address_components[i];
29743             if (component.types.indexOf("postal_code") >= 0) {
29744                 result.postalCode = component.short_name;
29745             } else if (component.types.indexOf("street_number") >= 0) {
29746                 result.streetNumber = component.short_name;
29747             } else if (component.types.indexOf("route") >= 0) {
29748                 result.streetName = component.short_name;
29749             } else if (component.types.indexOf("neighborhood") >= 0) {
29750                 result.city = component.short_name;
29751             } else if (component.types.indexOf("locality") >= 0) {
29752                 result.city = component.short_name;
29753             } else if (component.types.indexOf("sublocality") >= 0) {
29754                 result.district = component.short_name;
29755             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29756                 result.stateOrProvince = component.short_name;
29757             } else if (component.types.indexOf("country") >= 0) {
29758                 result.country = component.short_name;
29759             }
29760         }
29761         
29762         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29763         result.addressLine2 = "";
29764         return result;
29765     },
29766     
29767     setZoomLevel: function(zoom)
29768     {
29769         this.gMapContext.map.setZoom(zoom);
29770     },
29771     
29772     show: function()
29773     {
29774         if(!this.el){
29775             return;
29776         }
29777         
29778         this.el.show();
29779         
29780         this.resize();
29781         
29782         this.fireEvent('show', this);
29783     },
29784     
29785     hide: function()
29786     {
29787         if(!this.el){
29788             return;
29789         }
29790         
29791         this.el.hide();
29792         
29793         this.fireEvent('hide', this);
29794     }
29795     
29796 });
29797
29798 Roo.apply(Roo.bootstrap.LocationPicker, {
29799     
29800     OverlayView : function(map, options)
29801     {
29802         options = options || {};
29803         
29804         this.setMap(map);
29805     }
29806     
29807     
29808 });/**
29809  * @class Roo.bootstrap.Alert
29810  * @extends Roo.bootstrap.Component
29811  * Bootstrap Alert class - shows an alert area box
29812  * eg
29813  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29814   Enter a valid email address
29815 </div>
29816  * @licence LGPL
29817  * @cfg {String} title The title of alert
29818  * @cfg {String} html The content of alert
29819  * @cfg {String} weight (  success | info | warning | danger )
29820  * @cfg {String} fa font-awesomeicon
29821  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29822  * @cfg {Boolean} close true to show a x closer
29823  * 
29824  * 
29825  * @constructor
29826  * Create a new alert
29827  * @param {Object} config The config object
29828  */
29829
29830
29831 Roo.bootstrap.Alert = function(config){
29832     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29833     
29834 };
29835
29836 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29837     
29838     title: '',
29839     html: '',
29840     weight: false,
29841     fa: false,
29842     faicon: false, // BC
29843     close : false,
29844     
29845     
29846     getAutoCreate : function()
29847     {
29848         
29849         var cfg = {
29850             tag : 'div',
29851             cls : 'alert',
29852             cn : [
29853                 {
29854                     tag: 'button',
29855                     type :  "button",
29856                     cls: "close",
29857                     html : '×',
29858                     style : this.close ? '' : 'display:none'
29859                 },
29860                 {
29861                     tag : 'i',
29862                     cls : 'roo-alert-icon'
29863                     
29864                 },
29865                 {
29866                     tag : 'b',
29867                     cls : 'roo-alert-title',
29868                     html : this.title
29869                 },
29870                 {
29871                     tag : 'span',
29872                     cls : 'roo-alert-text',
29873                     html : this.html
29874                 }
29875             ]
29876         };
29877         
29878         if(this.faicon){
29879             cfg.cn[0].cls += ' fa ' + this.faicon;
29880         }
29881         if(this.fa){
29882             cfg.cn[0].cls += ' fa ' + this.fa;
29883         }
29884         
29885         if(this.weight){
29886             cfg.cls += ' alert-' + this.weight;
29887         }
29888         
29889         return cfg;
29890     },
29891     
29892     initEvents: function() 
29893     {
29894         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29895         this.titleEl =  this.el.select('.roo-alert-title',true).first();
29896         this.iconEl = this.el.select('.roo-alert-icon',true).first();
29897         if (this.seconds > 0) {
29898             this.hide.defer(this.seconds, this);
29899         }
29900     },
29901     
29902     setTitle : function(str)
29903     {
29904         this.titleEl.dom.innerHTML = str;
29905     },
29906     
29907     setText : function(str)
29908     {
29909         this.titleEl.dom.innerHTML = str;
29910     },
29911     
29912     setWeight : function(weight)
29913     {
29914         if(this.weight){
29915             this.el.removeClass('alert-' + this.weight);
29916         }
29917         
29918         this.weight = weight;
29919         
29920         this.el.addClass('alert-' + this.weight);
29921     },
29922     
29923     setIcon : function(icon)
29924     {
29925         if(this.faicon){
29926             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29927         }
29928         
29929         this.faicon = icon;
29930         
29931         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
29932     },
29933     
29934     hide: function() 
29935     {
29936         this.el.hide();   
29937     },
29938     
29939     show: function() 
29940     {  
29941         this.el.show();   
29942     }
29943     
29944 });
29945
29946  
29947 /*
29948 * Licence: LGPL
29949 */
29950
29951 /**
29952  * @class Roo.bootstrap.UploadCropbox
29953  * @extends Roo.bootstrap.Component
29954  * Bootstrap UploadCropbox class
29955  * @cfg {String} emptyText show when image has been loaded
29956  * @cfg {String} rotateNotify show when image too small to rotate
29957  * @cfg {Number} errorTimeout default 3000
29958  * @cfg {Number} minWidth default 300
29959  * @cfg {Number} minHeight default 300
29960  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29961  * @cfg {Boolean} isDocument (true|false) default false
29962  * @cfg {String} url action url
29963  * @cfg {String} paramName default 'imageUpload'
29964  * @cfg {String} method default POST
29965  * @cfg {Boolean} loadMask (true|false) default true
29966  * @cfg {Boolean} loadingText default 'Loading...'
29967  * 
29968  * @constructor
29969  * Create a new UploadCropbox
29970  * @param {Object} config The config object
29971  */
29972
29973 Roo.bootstrap.UploadCropbox = function(config){
29974     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29975     
29976     this.addEvents({
29977         /**
29978          * @event beforeselectfile
29979          * Fire before select file
29980          * @param {Roo.bootstrap.UploadCropbox} this
29981          */
29982         "beforeselectfile" : true,
29983         /**
29984          * @event initial
29985          * Fire after initEvent
29986          * @param {Roo.bootstrap.UploadCropbox} this
29987          */
29988         "initial" : true,
29989         /**
29990          * @event crop
29991          * Fire after initEvent
29992          * @param {Roo.bootstrap.UploadCropbox} this
29993          * @param {String} data
29994          */
29995         "crop" : true,
29996         /**
29997          * @event prepare
29998          * Fire when preparing the file data
29999          * @param {Roo.bootstrap.UploadCropbox} this
30000          * @param {Object} file
30001          */
30002         "prepare" : true,
30003         /**
30004          * @event exception
30005          * Fire when get exception
30006          * @param {Roo.bootstrap.UploadCropbox} this
30007          * @param {XMLHttpRequest} xhr
30008          */
30009         "exception" : true,
30010         /**
30011          * @event beforeloadcanvas
30012          * Fire before load the canvas
30013          * @param {Roo.bootstrap.UploadCropbox} this
30014          * @param {String} src
30015          */
30016         "beforeloadcanvas" : true,
30017         /**
30018          * @event trash
30019          * Fire when trash image
30020          * @param {Roo.bootstrap.UploadCropbox} this
30021          */
30022         "trash" : true,
30023         /**
30024          * @event download
30025          * Fire when download the image
30026          * @param {Roo.bootstrap.UploadCropbox} this
30027          */
30028         "download" : true,
30029         /**
30030          * @event footerbuttonclick
30031          * Fire when footerbuttonclick
30032          * @param {Roo.bootstrap.UploadCropbox} this
30033          * @param {String} type
30034          */
30035         "footerbuttonclick" : true,
30036         /**
30037          * @event resize
30038          * Fire when resize
30039          * @param {Roo.bootstrap.UploadCropbox} this
30040          */
30041         "resize" : true,
30042         /**
30043          * @event rotate
30044          * Fire when rotate the image
30045          * @param {Roo.bootstrap.UploadCropbox} this
30046          * @param {String} pos
30047          */
30048         "rotate" : true,
30049         /**
30050          * @event inspect
30051          * Fire when inspect the file
30052          * @param {Roo.bootstrap.UploadCropbox} this
30053          * @param {Object} file
30054          */
30055         "inspect" : true,
30056         /**
30057          * @event upload
30058          * Fire when xhr upload the file
30059          * @param {Roo.bootstrap.UploadCropbox} this
30060          * @param {Object} data
30061          */
30062         "upload" : true,
30063         /**
30064          * @event arrange
30065          * Fire when arrange the file data
30066          * @param {Roo.bootstrap.UploadCropbox} this
30067          * @param {Object} formData
30068          */
30069         "arrange" : true
30070     });
30071     
30072     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30073 };
30074
30075 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30076     
30077     emptyText : 'Click to upload image',
30078     rotateNotify : 'Image is too small to rotate',
30079     errorTimeout : 3000,
30080     scale : 0,
30081     baseScale : 1,
30082     rotate : 0,
30083     dragable : false,
30084     pinching : false,
30085     mouseX : 0,
30086     mouseY : 0,
30087     cropData : false,
30088     minWidth : 300,
30089     minHeight : 300,
30090     file : false,
30091     exif : {},
30092     baseRotate : 1,
30093     cropType : 'image/jpeg',
30094     buttons : false,
30095     canvasLoaded : false,
30096     isDocument : false,
30097     method : 'POST',
30098     paramName : 'imageUpload',
30099     loadMask : true,
30100     loadingText : 'Loading...',
30101     maskEl : false,
30102     
30103     getAutoCreate : function()
30104     {
30105         var cfg = {
30106             tag : 'div',
30107             cls : 'roo-upload-cropbox',
30108             cn : [
30109                 {
30110                     tag : 'input',
30111                     cls : 'roo-upload-cropbox-selector',
30112                     type : 'file'
30113                 },
30114                 {
30115                     tag : 'div',
30116                     cls : 'roo-upload-cropbox-body',
30117                     style : 'cursor:pointer',
30118                     cn : [
30119                         {
30120                             tag : 'div',
30121                             cls : 'roo-upload-cropbox-preview'
30122                         },
30123                         {
30124                             tag : 'div',
30125                             cls : 'roo-upload-cropbox-thumb'
30126                         },
30127                         {
30128                             tag : 'div',
30129                             cls : 'roo-upload-cropbox-empty-notify',
30130                             html : this.emptyText
30131                         },
30132                         {
30133                             tag : 'div',
30134                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30135                             html : this.rotateNotify
30136                         }
30137                     ]
30138                 },
30139                 {
30140                     tag : 'div',
30141                     cls : 'roo-upload-cropbox-footer',
30142                     cn : {
30143                         tag : 'div',
30144                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30145                         cn : []
30146                     }
30147                 }
30148             ]
30149         };
30150         
30151         return cfg;
30152     },
30153     
30154     onRender : function(ct, position)
30155     {
30156         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30157         
30158         if (this.buttons.length) {
30159             
30160             Roo.each(this.buttons, function(bb) {
30161                 
30162                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30163                 
30164                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30165                 
30166             }, this);
30167         }
30168         
30169         if(this.loadMask){
30170             this.maskEl = this.el;
30171         }
30172     },
30173     
30174     initEvents : function()
30175     {
30176         this.urlAPI = (window.createObjectURL && window) || 
30177                                 (window.URL && URL.revokeObjectURL && URL) || 
30178                                 (window.webkitURL && webkitURL);
30179                         
30180         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30181         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30182         
30183         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30184         this.selectorEl.hide();
30185         
30186         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30187         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30188         
30189         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30190         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30191         this.thumbEl.hide();
30192         
30193         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30194         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30195         
30196         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30197         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30198         this.errorEl.hide();
30199         
30200         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30201         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30202         this.footerEl.hide();
30203         
30204         this.setThumbBoxSize();
30205         
30206         this.bind();
30207         
30208         this.resize();
30209         
30210         this.fireEvent('initial', this);
30211     },
30212
30213     bind : function()
30214     {
30215         var _this = this;
30216         
30217         window.addEventListener("resize", function() { _this.resize(); } );
30218         
30219         this.bodyEl.on('click', this.beforeSelectFile, this);
30220         
30221         if(Roo.isTouch){
30222             this.bodyEl.on('touchstart', this.onTouchStart, this);
30223             this.bodyEl.on('touchmove', this.onTouchMove, this);
30224             this.bodyEl.on('touchend', this.onTouchEnd, this);
30225         }
30226         
30227         if(!Roo.isTouch){
30228             this.bodyEl.on('mousedown', this.onMouseDown, this);
30229             this.bodyEl.on('mousemove', this.onMouseMove, this);
30230             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30231             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30232             Roo.get(document).on('mouseup', this.onMouseUp, this);
30233         }
30234         
30235         this.selectorEl.on('change', this.onFileSelected, this);
30236     },
30237     
30238     reset : function()
30239     {    
30240         this.scale = 0;
30241         this.baseScale = 1;
30242         this.rotate = 0;
30243         this.baseRotate = 1;
30244         this.dragable = false;
30245         this.pinching = false;
30246         this.mouseX = 0;
30247         this.mouseY = 0;
30248         this.cropData = false;
30249         this.notifyEl.dom.innerHTML = this.emptyText;
30250         
30251         this.selectorEl.dom.value = '';
30252         
30253     },
30254     
30255     resize : function()
30256     {
30257         if(this.fireEvent('resize', this) != false){
30258             this.setThumbBoxPosition();
30259             this.setCanvasPosition();
30260         }
30261     },
30262     
30263     onFooterButtonClick : function(e, el, o, type)
30264     {
30265         switch (type) {
30266             case 'rotate-left' :
30267                 this.onRotateLeft(e);
30268                 break;
30269             case 'rotate-right' :
30270                 this.onRotateRight(e);
30271                 break;
30272             case 'picture' :
30273                 this.beforeSelectFile(e);
30274                 break;
30275             case 'trash' :
30276                 this.trash(e);
30277                 break;
30278             case 'crop' :
30279                 this.crop(e);
30280                 break;
30281             case 'download' :
30282                 this.download(e);
30283                 break;
30284             default :
30285                 break;
30286         }
30287         
30288         this.fireEvent('footerbuttonclick', this, type);
30289     },
30290     
30291     beforeSelectFile : function(e)
30292     {
30293         e.preventDefault();
30294         
30295         if(this.fireEvent('beforeselectfile', this) != false){
30296             this.selectorEl.dom.click();
30297         }
30298     },
30299     
30300     onFileSelected : function(e)
30301     {
30302         e.preventDefault();
30303         
30304         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30305             return;
30306         }
30307         
30308         var file = this.selectorEl.dom.files[0];
30309         
30310         if(this.fireEvent('inspect', this, file) != false){
30311             this.prepare(file);
30312         }
30313         
30314     },
30315     
30316     trash : function(e)
30317     {
30318         this.fireEvent('trash', this);
30319     },
30320     
30321     download : function(e)
30322     {
30323         this.fireEvent('download', this);
30324     },
30325     
30326     loadCanvas : function(src)
30327     {   
30328         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30329             
30330             this.reset();
30331             
30332             this.imageEl = document.createElement('img');
30333             
30334             var _this = this;
30335             
30336             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30337             
30338             this.imageEl.src = src;
30339         }
30340     },
30341     
30342     onLoadCanvas : function()
30343     {   
30344         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30345         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30346         
30347         this.bodyEl.un('click', this.beforeSelectFile, this);
30348         
30349         this.notifyEl.hide();
30350         this.thumbEl.show();
30351         this.footerEl.show();
30352         
30353         this.baseRotateLevel();
30354         
30355         if(this.isDocument){
30356             this.setThumbBoxSize();
30357         }
30358         
30359         this.setThumbBoxPosition();
30360         
30361         this.baseScaleLevel();
30362         
30363         this.draw();
30364         
30365         this.resize();
30366         
30367         this.canvasLoaded = true;
30368         
30369         if(this.loadMask){
30370             this.maskEl.unmask();
30371         }
30372         
30373     },
30374     
30375     setCanvasPosition : function()
30376     {   
30377         if(!this.canvasEl){
30378             return;
30379         }
30380         
30381         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30382         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30383         
30384         this.previewEl.setLeft(pw);
30385         this.previewEl.setTop(ph);
30386         
30387     },
30388     
30389     onMouseDown : function(e)
30390     {   
30391         e.stopEvent();
30392         
30393         this.dragable = true;
30394         this.pinching = false;
30395         
30396         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30397             this.dragable = false;
30398             return;
30399         }
30400         
30401         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30402         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30403         
30404     },
30405     
30406     onMouseMove : function(e)
30407     {   
30408         e.stopEvent();
30409         
30410         if(!this.canvasLoaded){
30411             return;
30412         }
30413         
30414         if (!this.dragable){
30415             return;
30416         }
30417         
30418         var minX = Math.ceil(this.thumbEl.getLeft(true));
30419         var minY = Math.ceil(this.thumbEl.getTop(true));
30420         
30421         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30422         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30423         
30424         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30425         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30426         
30427         x = x - this.mouseX;
30428         y = y - this.mouseY;
30429         
30430         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30431         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30432         
30433         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30434         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30435         
30436         this.previewEl.setLeft(bgX);
30437         this.previewEl.setTop(bgY);
30438         
30439         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30440         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30441     },
30442     
30443     onMouseUp : function(e)
30444     {   
30445         e.stopEvent();
30446         
30447         this.dragable = false;
30448     },
30449     
30450     onMouseWheel : function(e)
30451     {   
30452         e.stopEvent();
30453         
30454         this.startScale = this.scale;
30455         
30456         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30457         
30458         if(!this.zoomable()){
30459             this.scale = this.startScale;
30460             return;
30461         }
30462         
30463         this.draw();
30464         
30465         return;
30466     },
30467     
30468     zoomable : function()
30469     {
30470         var minScale = this.thumbEl.getWidth() / this.minWidth;
30471         
30472         if(this.minWidth < this.minHeight){
30473             minScale = this.thumbEl.getHeight() / this.minHeight;
30474         }
30475         
30476         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30477         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30478         
30479         if(
30480                 this.isDocument &&
30481                 (this.rotate == 0 || this.rotate == 180) && 
30482                 (
30483                     width > this.imageEl.OriginWidth || 
30484                     height > this.imageEl.OriginHeight ||
30485                     (width < this.minWidth && height < this.minHeight)
30486                 )
30487         ){
30488             return false;
30489         }
30490         
30491         if(
30492                 this.isDocument &&
30493                 (this.rotate == 90 || this.rotate == 270) && 
30494                 (
30495                     width > this.imageEl.OriginWidth || 
30496                     height > this.imageEl.OriginHeight ||
30497                     (width < this.minHeight && height < this.minWidth)
30498                 )
30499         ){
30500             return false;
30501         }
30502         
30503         if(
30504                 !this.isDocument &&
30505                 (this.rotate == 0 || this.rotate == 180) && 
30506                 (
30507                     width < this.minWidth || 
30508                     width > this.imageEl.OriginWidth || 
30509                     height < this.minHeight || 
30510                     height > this.imageEl.OriginHeight
30511                 )
30512         ){
30513             return false;
30514         }
30515         
30516         if(
30517                 !this.isDocument &&
30518                 (this.rotate == 90 || this.rotate == 270) && 
30519                 (
30520                     width < this.minHeight || 
30521                     width > this.imageEl.OriginWidth || 
30522                     height < this.minWidth || 
30523                     height > this.imageEl.OriginHeight
30524                 )
30525         ){
30526             return false;
30527         }
30528         
30529         return true;
30530         
30531     },
30532     
30533     onRotateLeft : function(e)
30534     {   
30535         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30536             
30537             var minScale = this.thumbEl.getWidth() / this.minWidth;
30538             
30539             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30540             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30541             
30542             this.startScale = this.scale;
30543             
30544             while (this.getScaleLevel() < minScale){
30545             
30546                 this.scale = this.scale + 1;
30547                 
30548                 if(!this.zoomable()){
30549                     break;
30550                 }
30551                 
30552                 if(
30553                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30554                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30555                 ){
30556                     continue;
30557                 }
30558                 
30559                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30560
30561                 this.draw();
30562                 
30563                 return;
30564             }
30565             
30566             this.scale = this.startScale;
30567             
30568             this.onRotateFail();
30569             
30570             return false;
30571         }
30572         
30573         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30574
30575         if(this.isDocument){
30576             this.setThumbBoxSize();
30577             this.setThumbBoxPosition();
30578             this.setCanvasPosition();
30579         }
30580         
30581         this.draw();
30582         
30583         this.fireEvent('rotate', this, 'left');
30584         
30585     },
30586     
30587     onRotateRight : function(e)
30588     {
30589         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30590             
30591             var minScale = this.thumbEl.getWidth() / this.minWidth;
30592         
30593             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30594             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30595             
30596             this.startScale = this.scale;
30597             
30598             while (this.getScaleLevel() < minScale){
30599             
30600                 this.scale = this.scale + 1;
30601                 
30602                 if(!this.zoomable()){
30603                     break;
30604                 }
30605                 
30606                 if(
30607                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30608                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30609                 ){
30610                     continue;
30611                 }
30612                 
30613                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30614
30615                 this.draw();
30616                 
30617                 return;
30618             }
30619             
30620             this.scale = this.startScale;
30621             
30622             this.onRotateFail();
30623             
30624             return false;
30625         }
30626         
30627         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30628
30629         if(this.isDocument){
30630             this.setThumbBoxSize();
30631             this.setThumbBoxPosition();
30632             this.setCanvasPosition();
30633         }
30634         
30635         this.draw();
30636         
30637         this.fireEvent('rotate', this, 'right');
30638     },
30639     
30640     onRotateFail : function()
30641     {
30642         this.errorEl.show(true);
30643         
30644         var _this = this;
30645         
30646         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30647     },
30648     
30649     draw : function()
30650     {
30651         this.previewEl.dom.innerHTML = '';
30652         
30653         var canvasEl = document.createElement("canvas");
30654         
30655         var contextEl = canvasEl.getContext("2d");
30656         
30657         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30658         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30659         var center = this.imageEl.OriginWidth / 2;
30660         
30661         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30662             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30663             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30664             center = this.imageEl.OriginHeight / 2;
30665         }
30666         
30667         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30668         
30669         contextEl.translate(center, center);
30670         contextEl.rotate(this.rotate * Math.PI / 180);
30671
30672         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30673         
30674         this.canvasEl = document.createElement("canvas");
30675         
30676         this.contextEl = this.canvasEl.getContext("2d");
30677         
30678         switch (this.rotate) {
30679             case 0 :
30680                 
30681                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30682                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30683                 
30684                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30685                 
30686                 break;
30687             case 90 : 
30688                 
30689                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30690                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30691                 
30692                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30693                     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);
30694                     break;
30695                 }
30696                 
30697                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30698                 
30699                 break;
30700             case 180 :
30701                 
30702                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30703                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30704                 
30705                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30706                     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);
30707                     break;
30708                 }
30709                 
30710                 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);
30711                 
30712                 break;
30713             case 270 :
30714                 
30715                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30716                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30717         
30718                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30719                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30720                     break;
30721                 }
30722                 
30723                 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);
30724                 
30725                 break;
30726             default : 
30727                 break;
30728         }
30729         
30730         this.previewEl.appendChild(this.canvasEl);
30731         
30732         this.setCanvasPosition();
30733     },
30734     
30735     crop : function()
30736     {
30737         if(!this.canvasLoaded){
30738             return;
30739         }
30740         
30741         var imageCanvas = document.createElement("canvas");
30742         
30743         var imageContext = imageCanvas.getContext("2d");
30744         
30745         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30746         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30747         
30748         var center = imageCanvas.width / 2;
30749         
30750         imageContext.translate(center, center);
30751         
30752         imageContext.rotate(this.rotate * Math.PI / 180);
30753         
30754         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30755         
30756         var canvas = document.createElement("canvas");
30757         
30758         var context = canvas.getContext("2d");
30759                 
30760         canvas.width = this.minWidth;
30761         canvas.height = this.minHeight;
30762
30763         switch (this.rotate) {
30764             case 0 :
30765                 
30766                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30767                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30768                 
30769                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30770                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30771                 
30772                 var targetWidth = this.minWidth - 2 * x;
30773                 var targetHeight = this.minHeight - 2 * y;
30774                 
30775                 var scale = 1;
30776                 
30777                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30778                     scale = targetWidth / width;
30779                 }
30780                 
30781                 if(x > 0 && y == 0){
30782                     scale = targetHeight / height;
30783                 }
30784                 
30785                 if(x > 0 && y > 0){
30786                     scale = targetWidth / width;
30787                     
30788                     if(width < height){
30789                         scale = targetHeight / height;
30790                     }
30791                 }
30792                 
30793                 context.scale(scale, scale);
30794                 
30795                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30796                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30797
30798                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30799                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30800
30801                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30802                 
30803                 break;
30804             case 90 : 
30805                 
30806                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30807                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30808                 
30809                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30810                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30811                 
30812                 var targetWidth = this.minWidth - 2 * x;
30813                 var targetHeight = this.minHeight - 2 * y;
30814                 
30815                 var scale = 1;
30816                 
30817                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30818                     scale = targetWidth / width;
30819                 }
30820                 
30821                 if(x > 0 && y == 0){
30822                     scale = targetHeight / height;
30823                 }
30824                 
30825                 if(x > 0 && y > 0){
30826                     scale = targetWidth / width;
30827                     
30828                     if(width < height){
30829                         scale = targetHeight / height;
30830                     }
30831                 }
30832                 
30833                 context.scale(scale, scale);
30834                 
30835                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30836                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30837
30838                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30839                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30840                 
30841                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30842                 
30843                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30844                 
30845                 break;
30846             case 180 :
30847                 
30848                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30849                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30850                 
30851                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30852                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30853                 
30854                 var targetWidth = this.minWidth - 2 * x;
30855                 var targetHeight = this.minHeight - 2 * y;
30856                 
30857                 var scale = 1;
30858                 
30859                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30860                     scale = targetWidth / width;
30861                 }
30862                 
30863                 if(x > 0 && y == 0){
30864                     scale = targetHeight / height;
30865                 }
30866                 
30867                 if(x > 0 && y > 0){
30868                     scale = targetWidth / width;
30869                     
30870                     if(width < height){
30871                         scale = targetHeight / height;
30872                     }
30873                 }
30874                 
30875                 context.scale(scale, scale);
30876                 
30877                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30878                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30879
30880                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30881                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30882
30883                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30884                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30885                 
30886                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30887                 
30888                 break;
30889             case 270 :
30890                 
30891                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30892                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30893                 
30894                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30895                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30896                 
30897                 var targetWidth = this.minWidth - 2 * x;
30898                 var targetHeight = this.minHeight - 2 * y;
30899                 
30900                 var scale = 1;
30901                 
30902                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30903                     scale = targetWidth / width;
30904                 }
30905                 
30906                 if(x > 0 && y == 0){
30907                     scale = targetHeight / height;
30908                 }
30909                 
30910                 if(x > 0 && y > 0){
30911                     scale = targetWidth / width;
30912                     
30913                     if(width < height){
30914                         scale = targetHeight / height;
30915                     }
30916                 }
30917                 
30918                 context.scale(scale, scale);
30919                 
30920                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30921                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30922
30923                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30924                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30925                 
30926                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30927                 
30928                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30929                 
30930                 break;
30931             default : 
30932                 break;
30933         }
30934         
30935         this.cropData = canvas.toDataURL(this.cropType);
30936         
30937         if(this.fireEvent('crop', this, this.cropData) !== false){
30938             this.process(this.file, this.cropData);
30939         }
30940         
30941         return;
30942         
30943     },
30944     
30945     setThumbBoxSize : function()
30946     {
30947         var width, height;
30948         
30949         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30950             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30951             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30952             
30953             this.minWidth = width;
30954             this.minHeight = height;
30955             
30956             if(this.rotate == 90 || this.rotate == 270){
30957                 this.minWidth = height;
30958                 this.minHeight = width;
30959             }
30960         }
30961         
30962         height = 300;
30963         width = Math.ceil(this.minWidth * height / this.minHeight);
30964         
30965         if(this.minWidth > this.minHeight){
30966             width = 300;
30967             height = Math.ceil(this.minHeight * width / this.minWidth);
30968         }
30969         
30970         this.thumbEl.setStyle({
30971             width : width + 'px',
30972             height : height + 'px'
30973         });
30974
30975         return;
30976             
30977     },
30978     
30979     setThumbBoxPosition : function()
30980     {
30981         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30982         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30983         
30984         this.thumbEl.setLeft(x);
30985         this.thumbEl.setTop(y);
30986         
30987     },
30988     
30989     baseRotateLevel : function()
30990     {
30991         this.baseRotate = 1;
30992         
30993         if(
30994                 typeof(this.exif) != 'undefined' &&
30995                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30996                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30997         ){
30998             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30999         }
31000         
31001         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31002         
31003     },
31004     
31005     baseScaleLevel : function()
31006     {
31007         var width, height;
31008         
31009         if(this.isDocument){
31010             
31011             if(this.baseRotate == 6 || this.baseRotate == 8){
31012             
31013                 height = this.thumbEl.getHeight();
31014                 this.baseScale = height / this.imageEl.OriginWidth;
31015
31016                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31017                     width = this.thumbEl.getWidth();
31018                     this.baseScale = width / this.imageEl.OriginHeight;
31019                 }
31020
31021                 return;
31022             }
31023
31024             height = this.thumbEl.getHeight();
31025             this.baseScale = height / this.imageEl.OriginHeight;
31026
31027             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31028                 width = this.thumbEl.getWidth();
31029                 this.baseScale = width / this.imageEl.OriginWidth;
31030             }
31031
31032             return;
31033         }
31034         
31035         if(this.baseRotate == 6 || this.baseRotate == 8){
31036             
31037             width = this.thumbEl.getHeight();
31038             this.baseScale = width / this.imageEl.OriginHeight;
31039             
31040             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31041                 height = this.thumbEl.getWidth();
31042                 this.baseScale = height / this.imageEl.OriginHeight;
31043             }
31044             
31045             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31046                 height = this.thumbEl.getWidth();
31047                 this.baseScale = height / this.imageEl.OriginHeight;
31048                 
31049                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31050                     width = this.thumbEl.getHeight();
31051                     this.baseScale = width / this.imageEl.OriginWidth;
31052                 }
31053             }
31054             
31055             return;
31056         }
31057         
31058         width = this.thumbEl.getWidth();
31059         this.baseScale = width / this.imageEl.OriginWidth;
31060         
31061         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31062             height = this.thumbEl.getHeight();
31063             this.baseScale = height / this.imageEl.OriginHeight;
31064         }
31065         
31066         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31067             
31068             height = this.thumbEl.getHeight();
31069             this.baseScale = height / this.imageEl.OriginHeight;
31070             
31071             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31072                 width = this.thumbEl.getWidth();
31073                 this.baseScale = width / this.imageEl.OriginWidth;
31074             }
31075             
31076         }
31077         
31078         return;
31079     },
31080     
31081     getScaleLevel : function()
31082     {
31083         return this.baseScale * Math.pow(1.1, this.scale);
31084     },
31085     
31086     onTouchStart : function(e)
31087     {
31088         if(!this.canvasLoaded){
31089             this.beforeSelectFile(e);
31090             return;
31091         }
31092         
31093         var touches = e.browserEvent.touches;
31094         
31095         if(!touches){
31096             return;
31097         }
31098         
31099         if(touches.length == 1){
31100             this.onMouseDown(e);
31101             return;
31102         }
31103         
31104         if(touches.length != 2){
31105             return;
31106         }
31107         
31108         var coords = [];
31109         
31110         for(var i = 0, finger; finger = touches[i]; i++){
31111             coords.push(finger.pageX, finger.pageY);
31112         }
31113         
31114         var x = Math.pow(coords[0] - coords[2], 2);
31115         var y = Math.pow(coords[1] - coords[3], 2);
31116         
31117         this.startDistance = Math.sqrt(x + y);
31118         
31119         this.startScale = this.scale;
31120         
31121         this.pinching = true;
31122         this.dragable = false;
31123         
31124     },
31125     
31126     onTouchMove : function(e)
31127     {
31128         if(!this.pinching && !this.dragable){
31129             return;
31130         }
31131         
31132         var touches = e.browserEvent.touches;
31133         
31134         if(!touches){
31135             return;
31136         }
31137         
31138         if(this.dragable){
31139             this.onMouseMove(e);
31140             return;
31141         }
31142         
31143         var coords = [];
31144         
31145         for(var i = 0, finger; finger = touches[i]; i++){
31146             coords.push(finger.pageX, finger.pageY);
31147         }
31148         
31149         var x = Math.pow(coords[0] - coords[2], 2);
31150         var y = Math.pow(coords[1] - coords[3], 2);
31151         
31152         this.endDistance = Math.sqrt(x + y);
31153         
31154         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31155         
31156         if(!this.zoomable()){
31157             this.scale = this.startScale;
31158             return;
31159         }
31160         
31161         this.draw();
31162         
31163     },
31164     
31165     onTouchEnd : function(e)
31166     {
31167         this.pinching = false;
31168         this.dragable = false;
31169         
31170     },
31171     
31172     process : function(file, crop)
31173     {
31174         if(this.loadMask){
31175             this.maskEl.mask(this.loadingText);
31176         }
31177         
31178         this.xhr = new XMLHttpRequest();
31179         
31180         file.xhr = this.xhr;
31181
31182         this.xhr.open(this.method, this.url, true);
31183         
31184         var headers = {
31185             "Accept": "application/json",
31186             "Cache-Control": "no-cache",
31187             "X-Requested-With": "XMLHttpRequest"
31188         };
31189         
31190         for (var headerName in headers) {
31191             var headerValue = headers[headerName];
31192             if (headerValue) {
31193                 this.xhr.setRequestHeader(headerName, headerValue);
31194             }
31195         }
31196         
31197         var _this = this;
31198         
31199         this.xhr.onload = function()
31200         {
31201             _this.xhrOnLoad(_this.xhr);
31202         }
31203         
31204         this.xhr.onerror = function()
31205         {
31206             _this.xhrOnError(_this.xhr);
31207         }
31208         
31209         var formData = new FormData();
31210
31211         formData.append('returnHTML', 'NO');
31212         
31213         if(crop){
31214             formData.append('crop', crop);
31215         }
31216         
31217         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31218             formData.append(this.paramName, file, file.name);
31219         }
31220         
31221         if(typeof(file.filename) != 'undefined'){
31222             formData.append('filename', file.filename);
31223         }
31224         
31225         if(typeof(file.mimetype) != 'undefined'){
31226             formData.append('mimetype', file.mimetype);
31227         }
31228         
31229         if(this.fireEvent('arrange', this, formData) != false){
31230             this.xhr.send(formData);
31231         };
31232     },
31233     
31234     xhrOnLoad : function(xhr)
31235     {
31236         if(this.loadMask){
31237             this.maskEl.unmask();
31238         }
31239         
31240         if (xhr.readyState !== 4) {
31241             this.fireEvent('exception', this, xhr);
31242             return;
31243         }
31244
31245         var response = Roo.decode(xhr.responseText);
31246         
31247         if(!response.success){
31248             this.fireEvent('exception', this, xhr);
31249             return;
31250         }
31251         
31252         var response = Roo.decode(xhr.responseText);
31253         
31254         this.fireEvent('upload', this, response);
31255         
31256     },
31257     
31258     xhrOnError : function()
31259     {
31260         if(this.loadMask){
31261             this.maskEl.unmask();
31262         }
31263         
31264         Roo.log('xhr on error');
31265         
31266         var response = Roo.decode(xhr.responseText);
31267           
31268         Roo.log(response);
31269         
31270     },
31271     
31272     prepare : function(file)
31273     {   
31274         if(this.loadMask){
31275             this.maskEl.mask(this.loadingText);
31276         }
31277         
31278         this.file = false;
31279         this.exif = {};
31280         
31281         if(typeof(file) === 'string'){
31282             this.loadCanvas(file);
31283             return;
31284         }
31285         
31286         if(!file || !this.urlAPI){
31287             return;
31288         }
31289         
31290         this.file = file;
31291         this.cropType = file.type;
31292         
31293         var _this = this;
31294         
31295         if(this.fireEvent('prepare', this, this.file) != false){
31296             
31297             var reader = new FileReader();
31298             
31299             reader.onload = function (e) {
31300                 if (e.target.error) {
31301                     Roo.log(e.target.error);
31302                     return;
31303                 }
31304                 
31305                 var buffer = e.target.result,
31306                     dataView = new DataView(buffer),
31307                     offset = 2,
31308                     maxOffset = dataView.byteLength - 4,
31309                     markerBytes,
31310                     markerLength;
31311                 
31312                 if (dataView.getUint16(0) === 0xffd8) {
31313                     while (offset < maxOffset) {
31314                         markerBytes = dataView.getUint16(offset);
31315                         
31316                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31317                             markerLength = dataView.getUint16(offset + 2) + 2;
31318                             if (offset + markerLength > dataView.byteLength) {
31319                                 Roo.log('Invalid meta data: Invalid segment size.');
31320                                 break;
31321                             }
31322                             
31323                             if(markerBytes == 0xffe1){
31324                                 _this.parseExifData(
31325                                     dataView,
31326                                     offset,
31327                                     markerLength
31328                                 );
31329                             }
31330                             
31331                             offset += markerLength;
31332                             
31333                             continue;
31334                         }
31335                         
31336                         break;
31337                     }
31338                     
31339                 }
31340                 
31341                 var url = _this.urlAPI.createObjectURL(_this.file);
31342                 
31343                 _this.loadCanvas(url);
31344                 
31345                 return;
31346             }
31347             
31348             reader.readAsArrayBuffer(this.file);
31349             
31350         }
31351         
31352     },
31353     
31354     parseExifData : function(dataView, offset, length)
31355     {
31356         var tiffOffset = offset + 10,
31357             littleEndian,
31358             dirOffset;
31359     
31360         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31361             // No Exif data, might be XMP data instead
31362             return;
31363         }
31364         
31365         // Check for the ASCII code for "Exif" (0x45786966):
31366         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31367             // No Exif data, might be XMP data instead
31368             return;
31369         }
31370         if (tiffOffset + 8 > dataView.byteLength) {
31371             Roo.log('Invalid Exif data: Invalid segment size.');
31372             return;
31373         }
31374         // Check for the two null bytes:
31375         if (dataView.getUint16(offset + 8) !== 0x0000) {
31376             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31377             return;
31378         }
31379         // Check the byte alignment:
31380         switch (dataView.getUint16(tiffOffset)) {
31381         case 0x4949:
31382             littleEndian = true;
31383             break;
31384         case 0x4D4D:
31385             littleEndian = false;
31386             break;
31387         default:
31388             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31389             return;
31390         }
31391         // Check for the TIFF tag marker (0x002A):
31392         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31393             Roo.log('Invalid Exif data: Missing TIFF marker.');
31394             return;
31395         }
31396         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31397         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31398         
31399         this.parseExifTags(
31400             dataView,
31401             tiffOffset,
31402             tiffOffset + dirOffset,
31403             littleEndian
31404         );
31405     },
31406     
31407     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31408     {
31409         var tagsNumber,
31410             dirEndOffset,
31411             i;
31412         if (dirOffset + 6 > dataView.byteLength) {
31413             Roo.log('Invalid Exif data: Invalid directory offset.');
31414             return;
31415         }
31416         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31417         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31418         if (dirEndOffset + 4 > dataView.byteLength) {
31419             Roo.log('Invalid Exif data: Invalid directory size.');
31420             return;
31421         }
31422         for (i = 0; i < tagsNumber; i += 1) {
31423             this.parseExifTag(
31424                 dataView,
31425                 tiffOffset,
31426                 dirOffset + 2 + 12 * i, // tag offset
31427                 littleEndian
31428             );
31429         }
31430         // Return the offset to the next directory:
31431         return dataView.getUint32(dirEndOffset, littleEndian);
31432     },
31433     
31434     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31435     {
31436         var tag = dataView.getUint16(offset, littleEndian);
31437         
31438         this.exif[tag] = this.getExifValue(
31439             dataView,
31440             tiffOffset,
31441             offset,
31442             dataView.getUint16(offset + 2, littleEndian), // tag type
31443             dataView.getUint32(offset + 4, littleEndian), // tag length
31444             littleEndian
31445         );
31446     },
31447     
31448     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31449     {
31450         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31451             tagSize,
31452             dataOffset,
31453             values,
31454             i,
31455             str,
31456             c;
31457     
31458         if (!tagType) {
31459             Roo.log('Invalid Exif data: Invalid tag type.');
31460             return;
31461         }
31462         
31463         tagSize = tagType.size * length;
31464         // Determine if the value is contained in the dataOffset bytes,
31465         // or if the value at the dataOffset is a pointer to the actual data:
31466         dataOffset = tagSize > 4 ?
31467                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31468         if (dataOffset + tagSize > dataView.byteLength) {
31469             Roo.log('Invalid Exif data: Invalid data offset.');
31470             return;
31471         }
31472         if (length === 1) {
31473             return tagType.getValue(dataView, dataOffset, littleEndian);
31474         }
31475         values = [];
31476         for (i = 0; i < length; i += 1) {
31477             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31478         }
31479         
31480         if (tagType.ascii) {
31481             str = '';
31482             // Concatenate the chars:
31483             for (i = 0; i < values.length; i += 1) {
31484                 c = values[i];
31485                 // Ignore the terminating NULL byte(s):
31486                 if (c === '\u0000') {
31487                     break;
31488                 }
31489                 str += c;
31490             }
31491             return str;
31492         }
31493         return values;
31494     }
31495     
31496 });
31497
31498 Roo.apply(Roo.bootstrap.UploadCropbox, {
31499     tags : {
31500         'Orientation': 0x0112
31501     },
31502     
31503     Orientation: {
31504             1: 0, //'top-left',
31505 //            2: 'top-right',
31506             3: 180, //'bottom-right',
31507 //            4: 'bottom-left',
31508 //            5: 'left-top',
31509             6: 90, //'right-top',
31510 //            7: 'right-bottom',
31511             8: 270 //'left-bottom'
31512     },
31513     
31514     exifTagTypes : {
31515         // byte, 8-bit unsigned int:
31516         1: {
31517             getValue: function (dataView, dataOffset) {
31518                 return dataView.getUint8(dataOffset);
31519             },
31520             size: 1
31521         },
31522         // ascii, 8-bit byte:
31523         2: {
31524             getValue: function (dataView, dataOffset) {
31525                 return String.fromCharCode(dataView.getUint8(dataOffset));
31526             },
31527             size: 1,
31528             ascii: true
31529         },
31530         // short, 16 bit int:
31531         3: {
31532             getValue: function (dataView, dataOffset, littleEndian) {
31533                 return dataView.getUint16(dataOffset, littleEndian);
31534             },
31535             size: 2
31536         },
31537         // long, 32 bit int:
31538         4: {
31539             getValue: function (dataView, dataOffset, littleEndian) {
31540                 return dataView.getUint32(dataOffset, littleEndian);
31541             },
31542             size: 4
31543         },
31544         // rational = two long values, first is numerator, second is denominator:
31545         5: {
31546             getValue: function (dataView, dataOffset, littleEndian) {
31547                 return dataView.getUint32(dataOffset, littleEndian) /
31548                     dataView.getUint32(dataOffset + 4, littleEndian);
31549             },
31550             size: 8
31551         },
31552         // slong, 32 bit signed int:
31553         9: {
31554             getValue: function (dataView, dataOffset, littleEndian) {
31555                 return dataView.getInt32(dataOffset, littleEndian);
31556             },
31557             size: 4
31558         },
31559         // srational, two slongs, first is numerator, second is denominator:
31560         10: {
31561             getValue: function (dataView, dataOffset, littleEndian) {
31562                 return dataView.getInt32(dataOffset, littleEndian) /
31563                     dataView.getInt32(dataOffset + 4, littleEndian);
31564             },
31565             size: 8
31566         }
31567     },
31568     
31569     footer : {
31570         STANDARD : [
31571             {
31572                 tag : 'div',
31573                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31574                 action : 'rotate-left',
31575                 cn : [
31576                     {
31577                         tag : 'button',
31578                         cls : 'btn btn-default',
31579                         html : '<i class="fa fa-undo"></i>'
31580                     }
31581                 ]
31582             },
31583             {
31584                 tag : 'div',
31585                 cls : 'btn-group roo-upload-cropbox-picture',
31586                 action : 'picture',
31587                 cn : [
31588                     {
31589                         tag : 'button',
31590                         cls : 'btn btn-default',
31591                         html : '<i class="fa fa-picture-o"></i>'
31592                     }
31593                 ]
31594             },
31595             {
31596                 tag : 'div',
31597                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31598                 action : 'rotate-right',
31599                 cn : [
31600                     {
31601                         tag : 'button',
31602                         cls : 'btn btn-default',
31603                         html : '<i class="fa fa-repeat"></i>'
31604                     }
31605                 ]
31606             }
31607         ],
31608         DOCUMENT : [
31609             {
31610                 tag : 'div',
31611                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31612                 action : 'rotate-left',
31613                 cn : [
31614                     {
31615                         tag : 'button',
31616                         cls : 'btn btn-default',
31617                         html : '<i class="fa fa-undo"></i>'
31618                     }
31619                 ]
31620             },
31621             {
31622                 tag : 'div',
31623                 cls : 'btn-group roo-upload-cropbox-download',
31624                 action : 'download',
31625                 cn : [
31626                     {
31627                         tag : 'button',
31628                         cls : 'btn btn-default',
31629                         html : '<i class="fa fa-download"></i>'
31630                     }
31631                 ]
31632             },
31633             {
31634                 tag : 'div',
31635                 cls : 'btn-group roo-upload-cropbox-crop',
31636                 action : 'crop',
31637                 cn : [
31638                     {
31639                         tag : 'button',
31640                         cls : 'btn btn-default',
31641                         html : '<i class="fa fa-crop"></i>'
31642                     }
31643                 ]
31644             },
31645             {
31646                 tag : 'div',
31647                 cls : 'btn-group roo-upload-cropbox-trash',
31648                 action : 'trash',
31649                 cn : [
31650                     {
31651                         tag : 'button',
31652                         cls : 'btn btn-default',
31653                         html : '<i class="fa fa-trash"></i>'
31654                     }
31655                 ]
31656             },
31657             {
31658                 tag : 'div',
31659                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31660                 action : 'rotate-right',
31661                 cn : [
31662                     {
31663                         tag : 'button',
31664                         cls : 'btn btn-default',
31665                         html : '<i class="fa fa-repeat"></i>'
31666                     }
31667                 ]
31668             }
31669         ],
31670         ROTATOR : [
31671             {
31672                 tag : 'div',
31673                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31674                 action : 'rotate-left',
31675                 cn : [
31676                     {
31677                         tag : 'button',
31678                         cls : 'btn btn-default',
31679                         html : '<i class="fa fa-undo"></i>'
31680                     }
31681                 ]
31682             },
31683             {
31684                 tag : 'div',
31685                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31686                 action : 'rotate-right',
31687                 cn : [
31688                     {
31689                         tag : 'button',
31690                         cls : 'btn btn-default',
31691                         html : '<i class="fa fa-repeat"></i>'
31692                     }
31693                 ]
31694             }
31695         ]
31696     }
31697 });
31698
31699 /*
31700 * Licence: LGPL
31701 */
31702
31703 /**
31704  * @class Roo.bootstrap.DocumentManager
31705  * @extends Roo.bootstrap.Component
31706  * Bootstrap DocumentManager class
31707  * @cfg {String} paramName default 'imageUpload'
31708  * @cfg {String} toolTipName default 'filename'
31709  * @cfg {String} method default POST
31710  * @cfg {String} url action url
31711  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31712  * @cfg {Boolean} multiple multiple upload default true
31713  * @cfg {Number} thumbSize default 300
31714  * @cfg {String} fieldLabel
31715  * @cfg {Number} labelWidth default 4
31716  * @cfg {String} labelAlign (left|top) default left
31717  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31718 * @cfg {Number} labellg set the width of label (1-12)
31719  * @cfg {Number} labelmd set the width of label (1-12)
31720  * @cfg {Number} labelsm set the width of label (1-12)
31721  * @cfg {Number} labelxs set the width of label (1-12)
31722  * 
31723  * @constructor
31724  * Create a new DocumentManager
31725  * @param {Object} config The config object
31726  */
31727
31728 Roo.bootstrap.DocumentManager = function(config){
31729     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31730     
31731     this.files = [];
31732     this.delegates = [];
31733     
31734     this.addEvents({
31735         /**
31736          * @event initial
31737          * Fire when initial the DocumentManager
31738          * @param {Roo.bootstrap.DocumentManager} this
31739          */
31740         "initial" : true,
31741         /**
31742          * @event inspect
31743          * inspect selected file
31744          * @param {Roo.bootstrap.DocumentManager} this
31745          * @param {File} file
31746          */
31747         "inspect" : true,
31748         /**
31749          * @event exception
31750          * Fire when xhr load exception
31751          * @param {Roo.bootstrap.DocumentManager} this
31752          * @param {XMLHttpRequest} xhr
31753          */
31754         "exception" : true,
31755         /**
31756          * @event afterupload
31757          * Fire when xhr load exception
31758          * @param {Roo.bootstrap.DocumentManager} this
31759          * @param {XMLHttpRequest} xhr
31760          */
31761         "afterupload" : true,
31762         /**
31763          * @event prepare
31764          * prepare the form data
31765          * @param {Roo.bootstrap.DocumentManager} this
31766          * @param {Object} formData
31767          */
31768         "prepare" : true,
31769         /**
31770          * @event remove
31771          * Fire when remove the file
31772          * @param {Roo.bootstrap.DocumentManager} this
31773          * @param {Object} file
31774          */
31775         "remove" : true,
31776         /**
31777          * @event refresh
31778          * Fire after refresh the file
31779          * @param {Roo.bootstrap.DocumentManager} this
31780          */
31781         "refresh" : true,
31782         /**
31783          * @event click
31784          * Fire after click the image
31785          * @param {Roo.bootstrap.DocumentManager} this
31786          * @param {Object} file
31787          */
31788         "click" : true,
31789         /**
31790          * @event edit
31791          * Fire when upload a image and editable set to true
31792          * @param {Roo.bootstrap.DocumentManager} this
31793          * @param {Object} file
31794          */
31795         "edit" : true,
31796         /**
31797          * @event beforeselectfile
31798          * Fire before select file
31799          * @param {Roo.bootstrap.DocumentManager} this
31800          */
31801         "beforeselectfile" : true,
31802         /**
31803          * @event process
31804          * Fire before process file
31805          * @param {Roo.bootstrap.DocumentManager} this
31806          * @param {Object} file
31807          */
31808         "process" : true,
31809         /**
31810          * @event previewrendered
31811          * Fire when preview rendered
31812          * @param {Roo.bootstrap.DocumentManager} this
31813          * @param {Object} file
31814          */
31815         "previewrendered" : true,
31816         /**
31817          */
31818         "previewResize" : true
31819         
31820     });
31821 };
31822
31823 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31824     
31825     boxes : 0,
31826     inputName : '',
31827     thumbSize : 300,
31828     multiple : true,
31829     files : false,
31830     method : 'POST',
31831     url : '',
31832     paramName : 'imageUpload',
31833     toolTipName : 'filename',
31834     fieldLabel : '',
31835     labelWidth : 4,
31836     labelAlign : 'left',
31837     editable : true,
31838     delegates : false,
31839     xhr : false, 
31840     
31841     labellg : 0,
31842     labelmd : 0,
31843     labelsm : 0,
31844     labelxs : 0,
31845     
31846     getAutoCreate : function()
31847     {   
31848         var managerWidget = {
31849             tag : 'div',
31850             cls : 'roo-document-manager',
31851             cn : [
31852                 {
31853                     tag : 'input',
31854                     cls : 'roo-document-manager-selector',
31855                     type : 'file'
31856                 },
31857                 {
31858                     tag : 'div',
31859                     cls : 'roo-document-manager-uploader',
31860                     cn : [
31861                         {
31862                             tag : 'div',
31863                             cls : 'roo-document-manager-upload-btn',
31864                             html : '<i class="fa fa-plus"></i>'
31865                         }
31866                     ]
31867                     
31868                 }
31869             ]
31870         };
31871         
31872         var content = [
31873             {
31874                 tag : 'div',
31875                 cls : 'column col-md-12',
31876                 cn : managerWidget
31877             }
31878         ];
31879         
31880         if(this.fieldLabel.length){
31881             
31882             content = [
31883                 {
31884                     tag : 'div',
31885                     cls : 'column col-md-12',
31886                     html : this.fieldLabel
31887                 },
31888                 {
31889                     tag : 'div',
31890                     cls : 'column col-md-12',
31891                     cn : managerWidget
31892                 }
31893             ];
31894
31895             if(this.labelAlign == 'left'){
31896                 content = [
31897                     {
31898                         tag : 'div',
31899                         cls : 'column',
31900                         html : this.fieldLabel
31901                     },
31902                     {
31903                         tag : 'div',
31904                         cls : 'column',
31905                         cn : managerWidget
31906                     }
31907                 ];
31908                 
31909                 if(this.labelWidth > 12){
31910                     content[0].style = "width: " + this.labelWidth + 'px';
31911                 }
31912
31913                 if(this.labelWidth < 13 && this.labelmd == 0){
31914                     this.labelmd = this.labelWidth;
31915                 }
31916
31917                 if(this.labellg > 0){
31918                     content[0].cls += ' col-lg-' + this.labellg;
31919                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31920                 }
31921
31922                 if(this.labelmd > 0){
31923                     content[0].cls += ' col-md-' + this.labelmd;
31924                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31925                 }
31926
31927                 if(this.labelsm > 0){
31928                     content[0].cls += ' col-sm-' + this.labelsm;
31929                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31930                 }
31931
31932                 if(this.labelxs > 0){
31933                     content[0].cls += ' col-xs-' + this.labelxs;
31934                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31935                 }
31936                 
31937             }
31938         }
31939         
31940         var cfg = {
31941             tag : 'div',
31942             cls : 'row clearfix',
31943             cn : content
31944         };
31945         
31946         return cfg;
31947         
31948     },
31949     
31950     initEvents : function()
31951     {
31952         this.managerEl = this.el.select('.roo-document-manager', true).first();
31953         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31954         
31955         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31956         this.selectorEl.hide();
31957         
31958         if(this.multiple){
31959             this.selectorEl.attr('multiple', 'multiple');
31960         }
31961         
31962         this.selectorEl.on('change', this.onFileSelected, this);
31963         
31964         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31965         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31966         
31967         this.uploader.on('click', this.onUploaderClick, this);
31968         
31969         this.renderProgressDialog();
31970         
31971         var _this = this;
31972         
31973         window.addEventListener("resize", function() { _this.refresh(); } );
31974         
31975         this.fireEvent('initial', this);
31976     },
31977     
31978     renderProgressDialog : function()
31979     {
31980         var _this = this;
31981         
31982         this.progressDialog = new Roo.bootstrap.Modal({
31983             cls : 'roo-document-manager-progress-dialog',
31984             allow_close : false,
31985             animate : false,
31986             title : '',
31987             buttons : [
31988                 {
31989                     name  :'cancel',
31990                     weight : 'danger',
31991                     html : 'Cancel'
31992                 }
31993             ], 
31994             listeners : { 
31995                 btnclick : function() {
31996                     _this.uploadCancel();
31997                     this.hide();
31998                 }
31999             }
32000         });
32001          
32002         this.progressDialog.render(Roo.get(document.body));
32003          
32004         this.progress = new Roo.bootstrap.Progress({
32005             cls : 'roo-document-manager-progress',
32006             active : true,
32007             striped : true
32008         });
32009         
32010         this.progress.render(this.progressDialog.getChildContainer());
32011         
32012         this.progressBar = new Roo.bootstrap.ProgressBar({
32013             cls : 'roo-document-manager-progress-bar',
32014             aria_valuenow : 0,
32015             aria_valuemin : 0,
32016             aria_valuemax : 12,
32017             panel : 'success'
32018         });
32019         
32020         this.progressBar.render(this.progress.getChildContainer());
32021     },
32022     
32023     onUploaderClick : function(e)
32024     {
32025         e.preventDefault();
32026      
32027         if(this.fireEvent('beforeselectfile', this) != false){
32028             this.selectorEl.dom.click();
32029         }
32030         
32031     },
32032     
32033     onFileSelected : function(e)
32034     {
32035         e.preventDefault();
32036         
32037         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32038             return;
32039         }
32040         
32041         Roo.each(this.selectorEl.dom.files, function(file){
32042             if(this.fireEvent('inspect', this, file) != false){
32043                 this.files.push(file);
32044             }
32045         }, this);
32046         
32047         this.queue();
32048         
32049     },
32050     
32051     queue : function()
32052     {
32053         this.selectorEl.dom.value = '';
32054         
32055         if(!this.files || !this.files.length){
32056             return;
32057         }
32058         
32059         if(this.boxes > 0 && this.files.length > this.boxes){
32060             this.files = this.files.slice(0, this.boxes);
32061         }
32062         
32063         this.uploader.show();
32064         
32065         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32066             this.uploader.hide();
32067         }
32068         
32069         var _this = this;
32070         
32071         var files = [];
32072         
32073         var docs = [];
32074         
32075         Roo.each(this.files, function(file){
32076             
32077             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32078                 var f = this.renderPreview(file);
32079                 files.push(f);
32080                 return;
32081             }
32082             
32083             if(file.type.indexOf('image') != -1){
32084                 this.delegates.push(
32085                     (function(){
32086                         _this.process(file);
32087                     }).createDelegate(this)
32088                 );
32089         
32090                 return;
32091             }
32092             
32093             docs.push(
32094                 (function(){
32095                     _this.process(file);
32096                 }).createDelegate(this)
32097             );
32098             
32099         }, this);
32100         
32101         this.files = files;
32102         
32103         this.delegates = this.delegates.concat(docs);
32104         
32105         if(!this.delegates.length){
32106             this.refresh();
32107             return;
32108         }
32109         
32110         this.progressBar.aria_valuemax = this.delegates.length;
32111         
32112         this.arrange();
32113         
32114         return;
32115     },
32116     
32117     arrange : function()
32118     {
32119         if(!this.delegates.length){
32120             this.progressDialog.hide();
32121             this.refresh();
32122             return;
32123         }
32124         
32125         var delegate = this.delegates.shift();
32126         
32127         this.progressDialog.show();
32128         
32129         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32130         
32131         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32132         
32133         delegate();
32134     },
32135     
32136     refresh : function()
32137     {
32138         this.uploader.show();
32139         
32140         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32141             this.uploader.hide();
32142         }
32143         
32144         Roo.isTouch ? this.closable(false) : this.closable(true);
32145         
32146         this.fireEvent('refresh', this);
32147     },
32148     
32149     onRemove : function(e, el, o)
32150     {
32151         e.preventDefault();
32152         
32153         this.fireEvent('remove', this, o);
32154         
32155     },
32156     
32157     remove : function(o)
32158     {
32159         var files = [];
32160         
32161         Roo.each(this.files, function(file){
32162             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32163                 files.push(file);
32164                 return;
32165             }
32166
32167             o.target.remove();
32168
32169         }, this);
32170         
32171         this.files = files;
32172         
32173         this.refresh();
32174     },
32175     
32176     clear : function()
32177     {
32178         Roo.each(this.files, function(file){
32179             if(!file.target){
32180                 return;
32181             }
32182             
32183             file.target.remove();
32184
32185         }, this);
32186         
32187         this.files = [];
32188         
32189         this.refresh();
32190     },
32191     
32192     onClick : function(e, el, o)
32193     {
32194         e.preventDefault();
32195         
32196         this.fireEvent('click', this, o);
32197         
32198     },
32199     
32200     closable : function(closable)
32201     {
32202         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32203             
32204             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32205             
32206             if(closable){
32207                 el.show();
32208                 return;
32209             }
32210             
32211             el.hide();
32212             
32213         }, this);
32214     },
32215     
32216     xhrOnLoad : function(xhr)
32217     {
32218         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32219             el.remove();
32220         }, this);
32221         
32222         if (xhr.readyState !== 4) {
32223             this.arrange();
32224             this.fireEvent('exception', this, xhr);
32225             return;
32226         }
32227
32228         var response = Roo.decode(xhr.responseText);
32229         
32230         if(!response.success){
32231             this.arrange();
32232             this.fireEvent('exception', this, xhr);
32233             return;
32234         }
32235         
32236         var file = this.renderPreview(response.data);
32237         
32238         this.files.push(file);
32239         
32240         this.arrange();
32241         
32242         this.fireEvent('afterupload', this, xhr);
32243         
32244     },
32245     
32246     xhrOnError : function(xhr)
32247     {
32248         Roo.log('xhr on error');
32249         
32250         var response = Roo.decode(xhr.responseText);
32251           
32252         Roo.log(response);
32253         
32254         this.arrange();
32255     },
32256     
32257     process : function(file)
32258     {
32259         if(this.fireEvent('process', this, file) !== false){
32260             if(this.editable && file.type.indexOf('image') != -1){
32261                 this.fireEvent('edit', this, file);
32262                 return;
32263             }
32264
32265             this.uploadStart(file, false);
32266
32267             return;
32268         }
32269         
32270     },
32271     
32272     uploadStart : function(file, crop)
32273     {
32274         this.xhr = new XMLHttpRequest();
32275         
32276         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32277             this.arrange();
32278             return;
32279         }
32280         
32281         file.xhr = this.xhr;
32282             
32283         this.managerEl.createChild({
32284             tag : 'div',
32285             cls : 'roo-document-manager-loading',
32286             cn : [
32287                 {
32288                     tag : 'div',
32289                     tooltip : file.name,
32290                     cls : 'roo-document-manager-thumb',
32291                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32292                 }
32293             ]
32294
32295         });
32296
32297         this.xhr.open(this.method, this.url, true);
32298         
32299         var headers = {
32300             "Accept": "application/json",
32301             "Cache-Control": "no-cache",
32302             "X-Requested-With": "XMLHttpRequest"
32303         };
32304         
32305         for (var headerName in headers) {
32306             var headerValue = headers[headerName];
32307             if (headerValue) {
32308                 this.xhr.setRequestHeader(headerName, headerValue);
32309             }
32310         }
32311         
32312         var _this = this;
32313         
32314         this.xhr.onload = function()
32315         {
32316             _this.xhrOnLoad(_this.xhr);
32317         }
32318         
32319         this.xhr.onerror = function()
32320         {
32321             _this.xhrOnError(_this.xhr);
32322         }
32323         
32324         var formData = new FormData();
32325
32326         formData.append('returnHTML', 'NO');
32327         
32328         if(crop){
32329             formData.append('crop', crop);
32330         }
32331         
32332         formData.append(this.paramName, file, file.name);
32333         
32334         var options = {
32335             file : file, 
32336             manually : false
32337         };
32338         
32339         if(this.fireEvent('prepare', this, formData, options) != false){
32340             
32341             if(options.manually){
32342                 return;
32343             }
32344             
32345             this.xhr.send(formData);
32346             return;
32347         };
32348         
32349         this.uploadCancel();
32350     },
32351     
32352     uploadCancel : function()
32353     {
32354         if (this.xhr) {
32355             this.xhr.abort();
32356         }
32357         
32358         this.delegates = [];
32359         
32360         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32361             el.remove();
32362         }, this);
32363         
32364         this.arrange();
32365     },
32366     
32367     renderPreview : function(file)
32368     {
32369         if(typeof(file.target) != 'undefined' && file.target){
32370             return file;
32371         }
32372         
32373         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32374         
32375         var previewEl = this.managerEl.createChild({
32376             tag : 'div',
32377             cls : 'roo-document-manager-preview',
32378             cn : [
32379                 {
32380                     tag : 'div',
32381                     tooltip : file[this.toolTipName],
32382                     cls : 'roo-document-manager-thumb',
32383                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32384                 },
32385                 {
32386                     tag : 'button',
32387                     cls : 'close',
32388                     html : '<i class="fa fa-times-circle"></i>'
32389                 }
32390             ]
32391         });
32392
32393         var close = previewEl.select('button.close', true).first();
32394
32395         close.on('click', this.onRemove, this, file);
32396
32397         file.target = previewEl;
32398
32399         var image = previewEl.select('img', true).first();
32400         
32401         var _this = this;
32402         
32403         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32404         
32405         image.on('click', this.onClick, this, file);
32406         
32407         this.fireEvent('previewrendered', this, file);
32408         
32409         return file;
32410         
32411     },
32412     
32413     onPreviewLoad : function(file, image)
32414     {
32415         if(typeof(file.target) == 'undefined' || !file.target){
32416             return;
32417         }
32418         
32419         var width = image.dom.naturalWidth || image.dom.width;
32420         var height = image.dom.naturalHeight || image.dom.height;
32421         
32422         if(!this.previewResize) {
32423             return;
32424         }
32425         
32426         if(width > height){
32427             file.target.addClass('wide');
32428             return;
32429         }
32430         
32431         file.target.addClass('tall');
32432         return;
32433         
32434     },
32435     
32436     uploadFromSource : function(file, crop)
32437     {
32438         this.xhr = new XMLHttpRequest();
32439         
32440         this.managerEl.createChild({
32441             tag : 'div',
32442             cls : 'roo-document-manager-loading',
32443             cn : [
32444                 {
32445                     tag : 'div',
32446                     tooltip : file.name,
32447                     cls : 'roo-document-manager-thumb',
32448                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32449                 }
32450             ]
32451
32452         });
32453
32454         this.xhr.open(this.method, this.url, true);
32455         
32456         var headers = {
32457             "Accept": "application/json",
32458             "Cache-Control": "no-cache",
32459             "X-Requested-With": "XMLHttpRequest"
32460         };
32461         
32462         for (var headerName in headers) {
32463             var headerValue = headers[headerName];
32464             if (headerValue) {
32465                 this.xhr.setRequestHeader(headerName, headerValue);
32466             }
32467         }
32468         
32469         var _this = this;
32470         
32471         this.xhr.onload = function()
32472         {
32473             _this.xhrOnLoad(_this.xhr);
32474         }
32475         
32476         this.xhr.onerror = function()
32477         {
32478             _this.xhrOnError(_this.xhr);
32479         }
32480         
32481         var formData = new FormData();
32482
32483         formData.append('returnHTML', 'NO');
32484         
32485         formData.append('crop', crop);
32486         
32487         if(typeof(file.filename) != 'undefined'){
32488             formData.append('filename', file.filename);
32489         }
32490         
32491         if(typeof(file.mimetype) != 'undefined'){
32492             formData.append('mimetype', file.mimetype);
32493         }
32494         
32495         Roo.log(formData);
32496         
32497         if(this.fireEvent('prepare', this, formData) != false){
32498             this.xhr.send(formData);
32499         };
32500     }
32501 });
32502
32503 /*
32504 * Licence: LGPL
32505 */
32506
32507 /**
32508  * @class Roo.bootstrap.DocumentViewer
32509  * @extends Roo.bootstrap.Component
32510  * Bootstrap DocumentViewer class
32511  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32512  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32513  * 
32514  * @constructor
32515  * Create a new DocumentViewer
32516  * @param {Object} config The config object
32517  */
32518
32519 Roo.bootstrap.DocumentViewer = function(config){
32520     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32521     
32522     this.addEvents({
32523         /**
32524          * @event initial
32525          * Fire after initEvent
32526          * @param {Roo.bootstrap.DocumentViewer} this
32527          */
32528         "initial" : true,
32529         /**
32530          * @event click
32531          * Fire after click
32532          * @param {Roo.bootstrap.DocumentViewer} this
32533          */
32534         "click" : true,
32535         /**
32536          * @event download
32537          * Fire after download button
32538          * @param {Roo.bootstrap.DocumentViewer} this
32539          */
32540         "download" : true,
32541         /**
32542          * @event trash
32543          * Fire after trash button
32544          * @param {Roo.bootstrap.DocumentViewer} this
32545          */
32546         "trash" : true
32547         
32548     });
32549 };
32550
32551 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32552     
32553     showDownload : true,
32554     
32555     showTrash : true,
32556     
32557     getAutoCreate : function()
32558     {
32559         var cfg = {
32560             tag : 'div',
32561             cls : 'roo-document-viewer',
32562             cn : [
32563                 {
32564                     tag : 'div',
32565                     cls : 'roo-document-viewer-body',
32566                     cn : [
32567                         {
32568                             tag : 'div',
32569                             cls : 'roo-document-viewer-thumb',
32570                             cn : [
32571                                 {
32572                                     tag : 'img',
32573                                     cls : 'roo-document-viewer-image'
32574                                 }
32575                             ]
32576                         }
32577                     ]
32578                 },
32579                 {
32580                     tag : 'div',
32581                     cls : 'roo-document-viewer-footer',
32582                     cn : {
32583                         tag : 'div',
32584                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32585                         cn : [
32586                             {
32587                                 tag : 'div',
32588                                 cls : 'btn-group roo-document-viewer-download',
32589                                 cn : [
32590                                     {
32591                                         tag : 'button',
32592                                         cls : 'btn btn-default',
32593                                         html : '<i class="fa fa-download"></i>'
32594                                     }
32595                                 ]
32596                             },
32597                             {
32598                                 tag : 'div',
32599                                 cls : 'btn-group roo-document-viewer-trash',
32600                                 cn : [
32601                                     {
32602                                         tag : 'button',
32603                                         cls : 'btn btn-default',
32604                                         html : '<i class="fa fa-trash"></i>'
32605                                     }
32606                                 ]
32607                             }
32608                         ]
32609                     }
32610                 }
32611             ]
32612         };
32613         
32614         return cfg;
32615     },
32616     
32617     initEvents : function()
32618     {
32619         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32620         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32621         
32622         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32623         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32624         
32625         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32626         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32627         
32628         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32629         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32630         
32631         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32632         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32633         
32634         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32635         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32636         
32637         this.bodyEl.on('click', this.onClick, this);
32638         this.downloadBtn.on('click', this.onDownload, this);
32639         this.trashBtn.on('click', this.onTrash, this);
32640         
32641         this.downloadBtn.hide();
32642         this.trashBtn.hide();
32643         
32644         if(this.showDownload){
32645             this.downloadBtn.show();
32646         }
32647         
32648         if(this.showTrash){
32649             this.trashBtn.show();
32650         }
32651         
32652         if(!this.showDownload && !this.showTrash) {
32653             this.footerEl.hide();
32654         }
32655         
32656     },
32657     
32658     initial : function()
32659     {
32660         this.fireEvent('initial', this);
32661         
32662     },
32663     
32664     onClick : function(e)
32665     {
32666         e.preventDefault();
32667         
32668         this.fireEvent('click', this);
32669     },
32670     
32671     onDownload : function(e)
32672     {
32673         e.preventDefault();
32674         
32675         this.fireEvent('download', this);
32676     },
32677     
32678     onTrash : function(e)
32679     {
32680         e.preventDefault();
32681         
32682         this.fireEvent('trash', this);
32683     }
32684     
32685 });
32686 /*
32687  * - LGPL
32688  *
32689  * nav progress bar
32690  * 
32691  */
32692
32693 /**
32694  * @class Roo.bootstrap.NavProgressBar
32695  * @extends Roo.bootstrap.Component
32696  * Bootstrap NavProgressBar class
32697  * 
32698  * @constructor
32699  * Create a new nav progress bar
32700  * @param {Object} config The config object
32701  */
32702
32703 Roo.bootstrap.NavProgressBar = function(config){
32704     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32705
32706     this.bullets = this.bullets || [];
32707    
32708 //    Roo.bootstrap.NavProgressBar.register(this);
32709      this.addEvents({
32710         /**
32711              * @event changed
32712              * Fires when the active item changes
32713              * @param {Roo.bootstrap.NavProgressBar} this
32714              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32715              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32716          */
32717         'changed': true
32718      });
32719     
32720 };
32721
32722 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32723     
32724     bullets : [],
32725     barItems : [],
32726     
32727     getAutoCreate : function()
32728     {
32729         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32730         
32731         cfg = {
32732             tag : 'div',
32733             cls : 'roo-navigation-bar-group',
32734             cn : [
32735                 {
32736                     tag : 'div',
32737                     cls : 'roo-navigation-top-bar'
32738                 },
32739                 {
32740                     tag : 'div',
32741                     cls : 'roo-navigation-bullets-bar',
32742                     cn : [
32743                         {
32744                             tag : 'ul',
32745                             cls : 'roo-navigation-bar'
32746                         }
32747                     ]
32748                 },
32749                 
32750                 {
32751                     tag : 'div',
32752                     cls : 'roo-navigation-bottom-bar'
32753                 }
32754             ]
32755             
32756         };
32757         
32758         return cfg;
32759         
32760     },
32761     
32762     initEvents: function() 
32763     {
32764         
32765     },
32766     
32767     onRender : function(ct, position) 
32768     {
32769         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32770         
32771         if(this.bullets.length){
32772             Roo.each(this.bullets, function(b){
32773                this.addItem(b);
32774             }, this);
32775         }
32776         
32777         this.format();
32778         
32779     },
32780     
32781     addItem : function(cfg)
32782     {
32783         var item = new Roo.bootstrap.NavProgressItem(cfg);
32784         
32785         item.parentId = this.id;
32786         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32787         
32788         if(cfg.html){
32789             var top = new Roo.bootstrap.Element({
32790                 tag : 'div',
32791                 cls : 'roo-navigation-bar-text'
32792             });
32793             
32794             var bottom = new Roo.bootstrap.Element({
32795                 tag : 'div',
32796                 cls : 'roo-navigation-bar-text'
32797             });
32798             
32799             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32800             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32801             
32802             var topText = new Roo.bootstrap.Element({
32803                 tag : 'span',
32804                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32805             });
32806             
32807             var bottomText = new Roo.bootstrap.Element({
32808                 tag : 'span',
32809                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32810             });
32811             
32812             topText.onRender(top.el, null);
32813             bottomText.onRender(bottom.el, null);
32814             
32815             item.topEl = top;
32816             item.bottomEl = bottom;
32817         }
32818         
32819         this.barItems.push(item);
32820         
32821         return item;
32822     },
32823     
32824     getActive : function()
32825     {
32826         var active = false;
32827         
32828         Roo.each(this.barItems, function(v){
32829             
32830             if (!v.isActive()) {
32831                 return;
32832             }
32833             
32834             active = v;
32835             return false;
32836             
32837         });
32838         
32839         return active;
32840     },
32841     
32842     setActiveItem : function(item)
32843     {
32844         var prev = false;
32845         
32846         Roo.each(this.barItems, function(v){
32847             if (v.rid == item.rid) {
32848                 return ;
32849             }
32850             
32851             if (v.isActive()) {
32852                 v.setActive(false);
32853                 prev = v;
32854             }
32855         });
32856
32857         item.setActive(true);
32858         
32859         this.fireEvent('changed', this, item, prev);
32860     },
32861     
32862     getBarItem: function(rid)
32863     {
32864         var ret = false;
32865         
32866         Roo.each(this.barItems, function(e) {
32867             if (e.rid != rid) {
32868                 return;
32869             }
32870             
32871             ret =  e;
32872             return false;
32873         });
32874         
32875         return ret;
32876     },
32877     
32878     indexOfItem : function(item)
32879     {
32880         var index = false;
32881         
32882         Roo.each(this.barItems, function(v, i){
32883             
32884             if (v.rid != item.rid) {
32885                 return;
32886             }
32887             
32888             index = i;
32889             return false
32890         });
32891         
32892         return index;
32893     },
32894     
32895     setActiveNext : function()
32896     {
32897         var i = this.indexOfItem(this.getActive());
32898         
32899         if (i > this.barItems.length) {
32900             return;
32901         }
32902         
32903         this.setActiveItem(this.barItems[i+1]);
32904     },
32905     
32906     setActivePrev : function()
32907     {
32908         var i = this.indexOfItem(this.getActive());
32909         
32910         if (i  < 1) {
32911             return;
32912         }
32913         
32914         this.setActiveItem(this.barItems[i-1]);
32915     },
32916     
32917     format : function()
32918     {
32919         if(!this.barItems.length){
32920             return;
32921         }
32922      
32923         var width = 100 / this.barItems.length;
32924         
32925         Roo.each(this.barItems, function(i){
32926             i.el.setStyle('width', width + '%');
32927             i.topEl.el.setStyle('width', width + '%');
32928             i.bottomEl.el.setStyle('width', width + '%');
32929         }, this);
32930         
32931     }
32932     
32933 });
32934 /*
32935  * - LGPL
32936  *
32937  * Nav Progress Item
32938  * 
32939  */
32940
32941 /**
32942  * @class Roo.bootstrap.NavProgressItem
32943  * @extends Roo.bootstrap.Component
32944  * Bootstrap NavProgressItem class
32945  * @cfg {String} rid the reference id
32946  * @cfg {Boolean} active (true|false) Is item active default false
32947  * @cfg {Boolean} disabled (true|false) Is item active default false
32948  * @cfg {String} html
32949  * @cfg {String} position (top|bottom) text position default bottom
32950  * @cfg {String} icon show icon instead of number
32951  * 
32952  * @constructor
32953  * Create a new NavProgressItem
32954  * @param {Object} config The config object
32955  */
32956 Roo.bootstrap.NavProgressItem = function(config){
32957     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32958     this.addEvents({
32959         // raw events
32960         /**
32961          * @event click
32962          * The raw click event for the entire grid.
32963          * @param {Roo.bootstrap.NavProgressItem} this
32964          * @param {Roo.EventObject} e
32965          */
32966         "click" : true
32967     });
32968    
32969 };
32970
32971 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32972     
32973     rid : '',
32974     active : false,
32975     disabled : false,
32976     html : '',
32977     position : 'bottom',
32978     icon : false,
32979     
32980     getAutoCreate : function()
32981     {
32982         var iconCls = 'roo-navigation-bar-item-icon';
32983         
32984         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32985         
32986         var cfg = {
32987             tag: 'li',
32988             cls: 'roo-navigation-bar-item',
32989             cn : [
32990                 {
32991                     tag : 'i',
32992                     cls : iconCls
32993                 }
32994             ]
32995         };
32996         
32997         if(this.active){
32998             cfg.cls += ' active';
32999         }
33000         if(this.disabled){
33001             cfg.cls += ' disabled';
33002         }
33003         
33004         return cfg;
33005     },
33006     
33007     disable : function()
33008     {
33009         this.setDisabled(true);
33010     },
33011     
33012     enable : function()
33013     {
33014         this.setDisabled(false);
33015     },
33016     
33017     initEvents: function() 
33018     {
33019         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33020         
33021         this.iconEl.on('click', this.onClick, this);
33022     },
33023     
33024     onClick : function(e)
33025     {
33026         e.preventDefault();
33027         
33028         if(this.disabled){
33029             return;
33030         }
33031         
33032         if(this.fireEvent('click', this, e) === false){
33033             return;
33034         };
33035         
33036         this.parent().setActiveItem(this);
33037     },
33038     
33039     isActive: function () 
33040     {
33041         return this.active;
33042     },
33043     
33044     setActive : function(state)
33045     {
33046         if(this.active == state){
33047             return;
33048         }
33049         
33050         this.active = state;
33051         
33052         if (state) {
33053             this.el.addClass('active');
33054             return;
33055         }
33056         
33057         this.el.removeClass('active');
33058         
33059         return;
33060     },
33061     
33062     setDisabled : function(state)
33063     {
33064         if(this.disabled == state){
33065             return;
33066         }
33067         
33068         this.disabled = state;
33069         
33070         if (state) {
33071             this.el.addClass('disabled');
33072             return;
33073         }
33074         
33075         this.el.removeClass('disabled');
33076     },
33077     
33078     tooltipEl : function()
33079     {
33080         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33081     }
33082 });
33083  
33084
33085  /*
33086  * - LGPL
33087  *
33088  * FieldLabel
33089  * 
33090  */
33091
33092 /**
33093  * @class Roo.bootstrap.FieldLabel
33094  * @extends Roo.bootstrap.Component
33095  * Bootstrap FieldLabel class
33096  * @cfg {String} html contents of the element
33097  * @cfg {String} tag tag of the element default label
33098  * @cfg {String} cls class of the element
33099  * @cfg {String} target label target 
33100  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33101  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33102  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33103  * @cfg {String} iconTooltip default "This field is required"
33104  * @cfg {String} indicatorpos (left|right) default left
33105  * 
33106  * @constructor
33107  * Create a new FieldLabel
33108  * @param {Object} config The config object
33109  */
33110
33111 Roo.bootstrap.FieldLabel = function(config){
33112     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33113     
33114     this.addEvents({
33115             /**
33116              * @event invalid
33117              * Fires after the field has been marked as invalid.
33118              * @param {Roo.form.FieldLabel} this
33119              * @param {String} msg The validation message
33120              */
33121             invalid : true,
33122             /**
33123              * @event valid
33124              * Fires after the field has been validated with no errors.
33125              * @param {Roo.form.FieldLabel} this
33126              */
33127             valid : true
33128         });
33129 };
33130
33131 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33132     
33133     tag: 'label',
33134     cls: '',
33135     html: '',
33136     target: '',
33137     allowBlank : true,
33138     invalidClass : 'has-warning',
33139     validClass : 'has-success',
33140     iconTooltip : 'This field is required',
33141     indicatorpos : 'left',
33142     
33143     getAutoCreate : function(){
33144         
33145         var cls = "";
33146         if (!this.allowBlank) {
33147             cls  = "visible";
33148         }
33149         
33150         var cfg = {
33151             tag : this.tag,
33152             cls : 'roo-bootstrap-field-label ' + this.cls,
33153             for : this.target,
33154             cn : [
33155                 {
33156                     tag : 'i',
33157                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33158                     tooltip : this.iconTooltip
33159                 },
33160                 {
33161                     tag : 'span',
33162                     html : this.html
33163                 }
33164             ] 
33165         };
33166         
33167         if(this.indicatorpos == 'right'){
33168             var cfg = {
33169                 tag : this.tag,
33170                 cls : 'roo-bootstrap-field-label ' + this.cls,
33171                 for : this.target,
33172                 cn : [
33173                     {
33174                         tag : 'span',
33175                         html : this.html
33176                     },
33177                     {
33178                         tag : 'i',
33179                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33180                         tooltip : this.iconTooltip
33181                     }
33182                 ] 
33183             };
33184         }
33185         
33186         return cfg;
33187     },
33188     
33189     initEvents: function() 
33190     {
33191         Roo.bootstrap.Element.superclass.initEvents.call(this);
33192         
33193         this.indicator = this.indicatorEl();
33194         
33195         if(this.indicator){
33196             this.indicator.removeClass('visible');
33197             this.indicator.addClass('invisible');
33198         }
33199         
33200         Roo.bootstrap.FieldLabel.register(this);
33201     },
33202     
33203     indicatorEl : function()
33204     {
33205         var indicator = this.el.select('i.roo-required-indicator',true).first();
33206         
33207         if(!indicator){
33208             return false;
33209         }
33210         
33211         return indicator;
33212         
33213     },
33214     
33215     /**
33216      * Mark this field as valid
33217      */
33218     markValid : function()
33219     {
33220         if(this.indicator){
33221             this.indicator.removeClass('visible');
33222             this.indicator.addClass('invisible');
33223         }
33224         if (Roo.bootstrap.version == 3) {
33225             this.el.removeClass(this.invalidClass);
33226             this.el.addClass(this.validClass);
33227         } else {
33228             this.el.removeClass('is-invalid');
33229             this.el.addClass('is-valid');
33230         }
33231         
33232         
33233         this.fireEvent('valid', this);
33234     },
33235     
33236     /**
33237      * Mark this field as invalid
33238      * @param {String} msg The validation message
33239      */
33240     markInvalid : function(msg)
33241     {
33242         if(this.indicator){
33243             this.indicator.removeClass('invisible');
33244             this.indicator.addClass('visible');
33245         }
33246           if (Roo.bootstrap.version == 3) {
33247             this.el.removeClass(this.validClass);
33248             this.el.addClass(this.invalidClass);
33249         } else {
33250             this.el.removeClass('is-valid');
33251             this.el.addClass('is-invalid');
33252         }
33253         
33254         
33255         this.fireEvent('invalid', this, msg);
33256     }
33257     
33258    
33259 });
33260
33261 Roo.apply(Roo.bootstrap.FieldLabel, {
33262     
33263     groups: {},
33264     
33265      /**
33266     * register a FieldLabel Group
33267     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33268     */
33269     register : function(label)
33270     {
33271         if(this.groups.hasOwnProperty(label.target)){
33272             return;
33273         }
33274      
33275         this.groups[label.target] = label;
33276         
33277     },
33278     /**
33279     * fetch a FieldLabel Group based on the target
33280     * @param {string} target
33281     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33282     */
33283     get: function(target) {
33284         if (typeof(this.groups[target]) == 'undefined') {
33285             return false;
33286         }
33287         
33288         return this.groups[target] ;
33289     }
33290 });
33291
33292  
33293
33294  /*
33295  * - LGPL
33296  *
33297  * page DateSplitField.
33298  * 
33299  */
33300
33301
33302 /**
33303  * @class Roo.bootstrap.DateSplitField
33304  * @extends Roo.bootstrap.Component
33305  * Bootstrap DateSplitField class
33306  * @cfg {string} fieldLabel - the label associated
33307  * @cfg {Number} labelWidth set the width of label (0-12)
33308  * @cfg {String} labelAlign (top|left)
33309  * @cfg {Boolean} dayAllowBlank (true|false) default false
33310  * @cfg {Boolean} monthAllowBlank (true|false) default false
33311  * @cfg {Boolean} yearAllowBlank (true|false) default false
33312  * @cfg {string} dayPlaceholder 
33313  * @cfg {string} monthPlaceholder
33314  * @cfg {string} yearPlaceholder
33315  * @cfg {string} dayFormat default 'd'
33316  * @cfg {string} monthFormat default 'm'
33317  * @cfg {string} yearFormat default 'Y'
33318  * @cfg {Number} labellg set the width of label (1-12)
33319  * @cfg {Number} labelmd set the width of label (1-12)
33320  * @cfg {Number} labelsm set the width of label (1-12)
33321  * @cfg {Number} labelxs set the width of label (1-12)
33322
33323  *     
33324  * @constructor
33325  * Create a new DateSplitField
33326  * @param {Object} config The config object
33327  */
33328
33329 Roo.bootstrap.DateSplitField = function(config){
33330     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33331     
33332     this.addEvents({
33333         // raw events
33334          /**
33335          * @event years
33336          * getting the data of years
33337          * @param {Roo.bootstrap.DateSplitField} this
33338          * @param {Object} years
33339          */
33340         "years" : true,
33341         /**
33342          * @event days
33343          * getting the data of days
33344          * @param {Roo.bootstrap.DateSplitField} this
33345          * @param {Object} days
33346          */
33347         "days" : true,
33348         /**
33349          * @event invalid
33350          * Fires after the field has been marked as invalid.
33351          * @param {Roo.form.Field} this
33352          * @param {String} msg The validation message
33353          */
33354         invalid : true,
33355        /**
33356          * @event valid
33357          * Fires after the field has been validated with no errors.
33358          * @param {Roo.form.Field} this
33359          */
33360         valid : true
33361     });
33362 };
33363
33364 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33365     
33366     fieldLabel : '',
33367     labelAlign : 'top',
33368     labelWidth : 3,
33369     dayAllowBlank : false,
33370     monthAllowBlank : false,
33371     yearAllowBlank : false,
33372     dayPlaceholder : '',
33373     monthPlaceholder : '',
33374     yearPlaceholder : '',
33375     dayFormat : 'd',
33376     monthFormat : 'm',
33377     yearFormat : 'Y',
33378     isFormField : true,
33379     labellg : 0,
33380     labelmd : 0,
33381     labelsm : 0,
33382     labelxs : 0,
33383     
33384     getAutoCreate : function()
33385     {
33386         var cfg = {
33387             tag : 'div',
33388             cls : 'row roo-date-split-field-group',
33389             cn : [
33390                 {
33391                     tag : 'input',
33392                     type : 'hidden',
33393                     cls : 'form-hidden-field roo-date-split-field-group-value',
33394                     name : this.name
33395                 }
33396             ]
33397         };
33398         
33399         var labelCls = 'col-md-12';
33400         var contentCls = 'col-md-4';
33401         
33402         if(this.fieldLabel){
33403             
33404             var label = {
33405                 tag : 'div',
33406                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33407                 cn : [
33408                     {
33409                         tag : 'label',
33410                         html : this.fieldLabel
33411                     }
33412                 ]
33413             };
33414             
33415             if(this.labelAlign == 'left'){
33416             
33417                 if(this.labelWidth > 12){
33418                     label.style = "width: " + this.labelWidth + 'px';
33419                 }
33420
33421                 if(this.labelWidth < 13 && this.labelmd == 0){
33422                     this.labelmd = this.labelWidth;
33423                 }
33424
33425                 if(this.labellg > 0){
33426                     labelCls = ' col-lg-' + this.labellg;
33427                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33428                 }
33429
33430                 if(this.labelmd > 0){
33431                     labelCls = ' col-md-' + this.labelmd;
33432                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33433                 }
33434
33435                 if(this.labelsm > 0){
33436                     labelCls = ' col-sm-' + this.labelsm;
33437                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33438                 }
33439
33440                 if(this.labelxs > 0){
33441                     labelCls = ' col-xs-' + this.labelxs;
33442                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33443                 }
33444             }
33445             
33446             label.cls += ' ' + labelCls;
33447             
33448             cfg.cn.push(label);
33449         }
33450         
33451         Roo.each(['day', 'month', 'year'], function(t){
33452             cfg.cn.push({
33453                 tag : 'div',
33454                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33455             });
33456         }, this);
33457         
33458         return cfg;
33459     },
33460     
33461     inputEl: function ()
33462     {
33463         return this.el.select('.roo-date-split-field-group-value', true).first();
33464     },
33465     
33466     onRender : function(ct, position) 
33467     {
33468         var _this = this;
33469         
33470         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33471         
33472         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33473         
33474         this.dayField = new Roo.bootstrap.ComboBox({
33475             allowBlank : this.dayAllowBlank,
33476             alwaysQuery : true,
33477             displayField : 'value',
33478             editable : false,
33479             fieldLabel : '',
33480             forceSelection : true,
33481             mode : 'local',
33482             placeholder : this.dayPlaceholder,
33483             selectOnFocus : true,
33484             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33485             triggerAction : 'all',
33486             typeAhead : true,
33487             valueField : 'value',
33488             store : new Roo.data.SimpleStore({
33489                 data : (function() {    
33490                     var days = [];
33491                     _this.fireEvent('days', _this, days);
33492                     return days;
33493                 })(),
33494                 fields : [ 'value' ]
33495             }),
33496             listeners : {
33497                 select : function (_self, record, index)
33498                 {
33499                     _this.setValue(_this.getValue());
33500                 }
33501             }
33502         });
33503
33504         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33505         
33506         this.monthField = new Roo.bootstrap.MonthField({
33507             after : '<i class=\"fa fa-calendar\"></i>',
33508             allowBlank : this.monthAllowBlank,
33509             placeholder : this.monthPlaceholder,
33510             readOnly : true,
33511             listeners : {
33512                 render : function (_self)
33513                 {
33514                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33515                         e.preventDefault();
33516                         _self.focus();
33517                     });
33518                 },
33519                 select : function (_self, oldvalue, newvalue)
33520                 {
33521                     _this.setValue(_this.getValue());
33522                 }
33523             }
33524         });
33525         
33526         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33527         
33528         this.yearField = new Roo.bootstrap.ComboBox({
33529             allowBlank : this.yearAllowBlank,
33530             alwaysQuery : true,
33531             displayField : 'value',
33532             editable : false,
33533             fieldLabel : '',
33534             forceSelection : true,
33535             mode : 'local',
33536             placeholder : this.yearPlaceholder,
33537             selectOnFocus : true,
33538             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33539             triggerAction : 'all',
33540             typeAhead : true,
33541             valueField : 'value',
33542             store : new Roo.data.SimpleStore({
33543                 data : (function() {
33544                     var years = [];
33545                     _this.fireEvent('years', _this, years);
33546                     return years;
33547                 })(),
33548                 fields : [ 'value' ]
33549             }),
33550             listeners : {
33551                 select : function (_self, record, index)
33552                 {
33553                     _this.setValue(_this.getValue());
33554                 }
33555             }
33556         });
33557
33558         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33559     },
33560     
33561     setValue : function(v, format)
33562     {
33563         this.inputEl.dom.value = v;
33564         
33565         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33566         
33567         var d = Date.parseDate(v, f);
33568         
33569         if(!d){
33570             this.validate();
33571             return;
33572         }
33573         
33574         this.setDay(d.format(this.dayFormat));
33575         this.setMonth(d.format(this.monthFormat));
33576         this.setYear(d.format(this.yearFormat));
33577         
33578         this.validate();
33579         
33580         return;
33581     },
33582     
33583     setDay : function(v)
33584     {
33585         this.dayField.setValue(v);
33586         this.inputEl.dom.value = this.getValue();
33587         this.validate();
33588         return;
33589     },
33590     
33591     setMonth : function(v)
33592     {
33593         this.monthField.setValue(v, true);
33594         this.inputEl.dom.value = this.getValue();
33595         this.validate();
33596         return;
33597     },
33598     
33599     setYear : function(v)
33600     {
33601         this.yearField.setValue(v);
33602         this.inputEl.dom.value = this.getValue();
33603         this.validate();
33604         return;
33605     },
33606     
33607     getDay : function()
33608     {
33609         return this.dayField.getValue();
33610     },
33611     
33612     getMonth : function()
33613     {
33614         return this.monthField.getValue();
33615     },
33616     
33617     getYear : function()
33618     {
33619         return this.yearField.getValue();
33620     },
33621     
33622     getValue : function()
33623     {
33624         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33625         
33626         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33627         
33628         return date;
33629     },
33630     
33631     reset : function()
33632     {
33633         this.setDay('');
33634         this.setMonth('');
33635         this.setYear('');
33636         this.inputEl.dom.value = '';
33637         this.validate();
33638         return;
33639     },
33640     
33641     validate : function()
33642     {
33643         var d = this.dayField.validate();
33644         var m = this.monthField.validate();
33645         var y = this.yearField.validate();
33646         
33647         var valid = true;
33648         
33649         if(
33650                 (!this.dayAllowBlank && !d) ||
33651                 (!this.monthAllowBlank && !m) ||
33652                 (!this.yearAllowBlank && !y)
33653         ){
33654             valid = false;
33655         }
33656         
33657         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33658             return valid;
33659         }
33660         
33661         if(valid){
33662             this.markValid();
33663             return valid;
33664         }
33665         
33666         this.markInvalid();
33667         
33668         return valid;
33669     },
33670     
33671     markValid : function()
33672     {
33673         
33674         var label = this.el.select('label', true).first();
33675         var icon = this.el.select('i.fa-star', true).first();
33676
33677         if(label && icon){
33678             icon.remove();
33679         }
33680         
33681         this.fireEvent('valid', this);
33682     },
33683     
33684      /**
33685      * Mark this field as invalid
33686      * @param {String} msg The validation message
33687      */
33688     markInvalid : function(msg)
33689     {
33690         
33691         var label = this.el.select('label', true).first();
33692         var icon = this.el.select('i.fa-star', true).first();
33693
33694         if(label && !icon){
33695             this.el.select('.roo-date-split-field-label', true).createChild({
33696                 tag : 'i',
33697                 cls : 'text-danger fa fa-lg fa-star',
33698                 tooltip : 'This field is required',
33699                 style : 'margin-right:5px;'
33700             }, label, true);
33701         }
33702         
33703         this.fireEvent('invalid', this, msg);
33704     },
33705     
33706     clearInvalid : function()
33707     {
33708         var label = this.el.select('label', true).first();
33709         var icon = this.el.select('i.fa-star', true).first();
33710
33711         if(label && icon){
33712             icon.remove();
33713         }
33714         
33715         this.fireEvent('valid', this);
33716     },
33717     
33718     getName: function()
33719     {
33720         return this.name;
33721     }
33722     
33723 });
33724
33725  /**
33726  *
33727  * This is based on 
33728  * http://masonry.desandro.com
33729  *
33730  * The idea is to render all the bricks based on vertical width...
33731  *
33732  * The original code extends 'outlayer' - we might need to use that....
33733  * 
33734  */
33735
33736
33737 /**
33738  * @class Roo.bootstrap.LayoutMasonry
33739  * @extends Roo.bootstrap.Component
33740  * Bootstrap Layout Masonry class
33741  * 
33742  * @constructor
33743  * Create a new Element
33744  * @param {Object} config The config object
33745  */
33746
33747 Roo.bootstrap.LayoutMasonry = function(config){
33748     
33749     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33750     
33751     this.bricks = [];
33752     
33753     Roo.bootstrap.LayoutMasonry.register(this);
33754     
33755     this.addEvents({
33756         // raw events
33757         /**
33758          * @event layout
33759          * Fire after layout the items
33760          * @param {Roo.bootstrap.LayoutMasonry} this
33761          * @param {Roo.EventObject} e
33762          */
33763         "layout" : true
33764     });
33765     
33766 };
33767
33768 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33769     
33770     /**
33771      * @cfg {Boolean} isLayoutInstant = no animation?
33772      */   
33773     isLayoutInstant : false, // needed?
33774    
33775     /**
33776      * @cfg {Number} boxWidth  width of the columns
33777      */   
33778     boxWidth : 450,
33779     
33780       /**
33781      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33782      */   
33783     boxHeight : 0,
33784     
33785     /**
33786      * @cfg {Number} padWidth padding below box..
33787      */   
33788     padWidth : 10, 
33789     
33790     /**
33791      * @cfg {Number} gutter gutter width..
33792      */   
33793     gutter : 10,
33794     
33795      /**
33796      * @cfg {Number} maxCols maximum number of columns
33797      */   
33798     
33799     maxCols: 0,
33800     
33801     /**
33802      * @cfg {Boolean} isAutoInitial defalut true
33803      */   
33804     isAutoInitial : true, 
33805     
33806     containerWidth: 0,
33807     
33808     /**
33809      * @cfg {Boolean} isHorizontal defalut false
33810      */   
33811     isHorizontal : false, 
33812
33813     currentSize : null,
33814     
33815     tag: 'div',
33816     
33817     cls: '',
33818     
33819     bricks: null, //CompositeElement
33820     
33821     cols : 1,
33822     
33823     _isLayoutInited : false,
33824     
33825 //    isAlternative : false, // only use for vertical layout...
33826     
33827     /**
33828      * @cfg {Number} alternativePadWidth padding below box..
33829      */   
33830     alternativePadWidth : 50,
33831     
33832     selectedBrick : [],
33833     
33834     getAutoCreate : function(){
33835         
33836         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33837         
33838         var cfg = {
33839             tag: this.tag,
33840             cls: 'blog-masonary-wrapper ' + this.cls,
33841             cn : {
33842                 cls : 'mas-boxes masonary'
33843             }
33844         };
33845         
33846         return cfg;
33847     },
33848     
33849     getChildContainer: function( )
33850     {
33851         if (this.boxesEl) {
33852             return this.boxesEl;
33853         }
33854         
33855         this.boxesEl = this.el.select('.mas-boxes').first();
33856         
33857         return this.boxesEl;
33858     },
33859     
33860     
33861     initEvents : function()
33862     {
33863         var _this = this;
33864         
33865         if(this.isAutoInitial){
33866             Roo.log('hook children rendered');
33867             this.on('childrenrendered', function() {
33868                 Roo.log('children rendered');
33869                 _this.initial();
33870             } ,this);
33871         }
33872     },
33873     
33874     initial : function()
33875     {
33876         this.selectedBrick = [];
33877         
33878         this.currentSize = this.el.getBox(true);
33879         
33880         Roo.EventManager.onWindowResize(this.resize, this); 
33881
33882         if(!this.isAutoInitial){
33883             this.layout();
33884             return;
33885         }
33886         
33887         this.layout();
33888         
33889         return;
33890         //this.layout.defer(500,this);
33891         
33892     },
33893     
33894     resize : function()
33895     {
33896         var cs = this.el.getBox(true);
33897         
33898         if (
33899                 this.currentSize.width == cs.width && 
33900                 this.currentSize.x == cs.x && 
33901                 this.currentSize.height == cs.height && 
33902                 this.currentSize.y == cs.y 
33903         ) {
33904             Roo.log("no change in with or X or Y");
33905             return;
33906         }
33907         
33908         this.currentSize = cs;
33909         
33910         this.layout();
33911         
33912     },
33913     
33914     layout : function()
33915     {   
33916         this._resetLayout();
33917         
33918         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33919         
33920         this.layoutItems( isInstant );
33921       
33922         this._isLayoutInited = true;
33923         
33924         this.fireEvent('layout', this);
33925         
33926     },
33927     
33928     _resetLayout : function()
33929     {
33930         if(this.isHorizontal){
33931             this.horizontalMeasureColumns();
33932             return;
33933         }
33934         
33935         this.verticalMeasureColumns();
33936         
33937     },
33938     
33939     verticalMeasureColumns : function()
33940     {
33941         this.getContainerWidth();
33942         
33943 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33944 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33945 //            return;
33946 //        }
33947         
33948         var boxWidth = this.boxWidth + this.padWidth;
33949         
33950         if(this.containerWidth < this.boxWidth){
33951             boxWidth = this.containerWidth
33952         }
33953         
33954         var containerWidth = this.containerWidth;
33955         
33956         var cols = Math.floor(containerWidth / boxWidth);
33957         
33958         this.cols = Math.max( cols, 1 );
33959         
33960         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33961         
33962         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33963         
33964         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33965         
33966         this.colWidth = boxWidth + avail - this.padWidth;
33967         
33968         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33969         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33970     },
33971     
33972     horizontalMeasureColumns : function()
33973     {
33974         this.getContainerWidth();
33975         
33976         var boxWidth = this.boxWidth;
33977         
33978         if(this.containerWidth < boxWidth){
33979             boxWidth = this.containerWidth;
33980         }
33981         
33982         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33983         
33984         this.el.setHeight(boxWidth);
33985         
33986     },
33987     
33988     getContainerWidth : function()
33989     {
33990         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33991     },
33992     
33993     layoutItems : function( isInstant )
33994     {
33995         Roo.log(this.bricks);
33996         
33997         var items = Roo.apply([], this.bricks);
33998         
33999         if(this.isHorizontal){
34000             this._horizontalLayoutItems( items , isInstant );
34001             return;
34002         }
34003         
34004 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34005 //            this._verticalAlternativeLayoutItems( items , isInstant );
34006 //            return;
34007 //        }
34008         
34009         this._verticalLayoutItems( items , isInstant );
34010         
34011     },
34012     
34013     _verticalLayoutItems : function ( items , isInstant)
34014     {
34015         if ( !items || !items.length ) {
34016             return;
34017         }
34018         
34019         var standard = [
34020             ['xs', 'xs', 'xs', 'tall'],
34021             ['xs', 'xs', 'tall'],
34022             ['xs', 'xs', 'sm'],
34023             ['xs', 'xs', 'xs'],
34024             ['xs', 'tall'],
34025             ['xs', 'sm'],
34026             ['xs', 'xs'],
34027             ['xs'],
34028             
34029             ['sm', 'xs', 'xs'],
34030             ['sm', 'xs'],
34031             ['sm'],
34032             
34033             ['tall', 'xs', 'xs', 'xs'],
34034             ['tall', 'xs', 'xs'],
34035             ['tall', 'xs'],
34036             ['tall']
34037             
34038         ];
34039         
34040         var queue = [];
34041         
34042         var boxes = [];
34043         
34044         var box = [];
34045         
34046         Roo.each(items, function(item, k){
34047             
34048             switch (item.size) {
34049                 // these layouts take up a full box,
34050                 case 'md' :
34051                 case 'md-left' :
34052                 case 'md-right' :
34053                 case 'wide' :
34054                     
34055                     if(box.length){
34056                         boxes.push(box);
34057                         box = [];
34058                     }
34059                     
34060                     boxes.push([item]);
34061                     
34062                     break;
34063                     
34064                 case 'xs' :
34065                 case 'sm' :
34066                 case 'tall' :
34067                     
34068                     box.push(item);
34069                     
34070                     break;
34071                 default :
34072                     break;
34073                     
34074             }
34075             
34076         }, this);
34077         
34078         if(box.length){
34079             boxes.push(box);
34080             box = [];
34081         }
34082         
34083         var filterPattern = function(box, length)
34084         {
34085             if(!box.length){
34086                 return;
34087             }
34088             
34089             var match = false;
34090             
34091             var pattern = box.slice(0, length);
34092             
34093             var format = [];
34094             
34095             Roo.each(pattern, function(i){
34096                 format.push(i.size);
34097             }, this);
34098             
34099             Roo.each(standard, function(s){
34100                 
34101                 if(String(s) != String(format)){
34102                     return;
34103                 }
34104                 
34105                 match = true;
34106                 return false;
34107                 
34108             }, this);
34109             
34110             if(!match && length == 1){
34111                 return;
34112             }
34113             
34114             if(!match){
34115                 filterPattern(box, length - 1);
34116                 return;
34117             }
34118                 
34119             queue.push(pattern);
34120
34121             box = box.slice(length, box.length);
34122
34123             filterPattern(box, 4);
34124
34125             return;
34126             
34127         }
34128         
34129         Roo.each(boxes, function(box, k){
34130             
34131             if(!box.length){
34132                 return;
34133             }
34134             
34135             if(box.length == 1){
34136                 queue.push(box);
34137                 return;
34138             }
34139             
34140             filterPattern(box, 4);
34141             
34142         }, this);
34143         
34144         this._processVerticalLayoutQueue( queue, isInstant );
34145         
34146     },
34147     
34148 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34149 //    {
34150 //        if ( !items || !items.length ) {
34151 //            return;
34152 //        }
34153 //
34154 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34155 //        
34156 //    },
34157     
34158     _horizontalLayoutItems : function ( items , isInstant)
34159     {
34160         if ( !items || !items.length || items.length < 3) {
34161             return;
34162         }
34163         
34164         items.reverse();
34165         
34166         var eItems = items.slice(0, 3);
34167         
34168         items = items.slice(3, items.length);
34169         
34170         var standard = [
34171             ['xs', 'xs', 'xs', 'wide'],
34172             ['xs', 'xs', 'wide'],
34173             ['xs', 'xs', 'sm'],
34174             ['xs', 'xs', 'xs'],
34175             ['xs', 'wide'],
34176             ['xs', 'sm'],
34177             ['xs', 'xs'],
34178             ['xs'],
34179             
34180             ['sm', 'xs', 'xs'],
34181             ['sm', 'xs'],
34182             ['sm'],
34183             
34184             ['wide', 'xs', 'xs', 'xs'],
34185             ['wide', 'xs', 'xs'],
34186             ['wide', 'xs'],
34187             ['wide'],
34188             
34189             ['wide-thin']
34190         ];
34191         
34192         var queue = [];
34193         
34194         var boxes = [];
34195         
34196         var box = [];
34197         
34198         Roo.each(items, function(item, k){
34199             
34200             switch (item.size) {
34201                 case 'md' :
34202                 case 'md-left' :
34203                 case 'md-right' :
34204                 case 'tall' :
34205                     
34206                     if(box.length){
34207                         boxes.push(box);
34208                         box = [];
34209                     }
34210                     
34211                     boxes.push([item]);
34212                     
34213                     break;
34214                     
34215                 case 'xs' :
34216                 case 'sm' :
34217                 case 'wide' :
34218                 case 'wide-thin' :
34219                     
34220                     box.push(item);
34221                     
34222                     break;
34223                 default :
34224                     break;
34225                     
34226             }
34227             
34228         }, this);
34229         
34230         if(box.length){
34231             boxes.push(box);
34232             box = [];
34233         }
34234         
34235         var filterPattern = function(box, length)
34236         {
34237             if(!box.length){
34238                 return;
34239             }
34240             
34241             var match = false;
34242             
34243             var pattern = box.slice(0, length);
34244             
34245             var format = [];
34246             
34247             Roo.each(pattern, function(i){
34248                 format.push(i.size);
34249             }, this);
34250             
34251             Roo.each(standard, function(s){
34252                 
34253                 if(String(s) != String(format)){
34254                     return;
34255                 }
34256                 
34257                 match = true;
34258                 return false;
34259                 
34260             }, this);
34261             
34262             if(!match && length == 1){
34263                 return;
34264             }
34265             
34266             if(!match){
34267                 filterPattern(box, length - 1);
34268                 return;
34269             }
34270                 
34271             queue.push(pattern);
34272
34273             box = box.slice(length, box.length);
34274
34275             filterPattern(box, 4);
34276
34277             return;
34278             
34279         }
34280         
34281         Roo.each(boxes, function(box, k){
34282             
34283             if(!box.length){
34284                 return;
34285             }
34286             
34287             if(box.length == 1){
34288                 queue.push(box);
34289                 return;
34290             }
34291             
34292             filterPattern(box, 4);
34293             
34294         }, this);
34295         
34296         
34297         var prune = [];
34298         
34299         var pos = this.el.getBox(true);
34300         
34301         var minX = pos.x;
34302         
34303         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34304         
34305         var hit_end = false;
34306         
34307         Roo.each(queue, function(box){
34308             
34309             if(hit_end){
34310                 
34311                 Roo.each(box, function(b){
34312                 
34313                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34314                     b.el.hide();
34315
34316                 }, this);
34317
34318                 return;
34319             }
34320             
34321             var mx = 0;
34322             
34323             Roo.each(box, function(b){
34324                 
34325                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34326                 b.el.show();
34327
34328                 mx = Math.max(mx, b.x);
34329                 
34330             }, this);
34331             
34332             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34333             
34334             if(maxX < minX){
34335                 
34336                 Roo.each(box, function(b){
34337                 
34338                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34339                     b.el.hide();
34340                     
34341                 }, this);
34342                 
34343                 hit_end = true;
34344                 
34345                 return;
34346             }
34347             
34348             prune.push(box);
34349             
34350         }, this);
34351         
34352         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34353     },
34354     
34355     /** Sets position of item in DOM
34356     * @param {Element} item
34357     * @param {Number} x - horizontal position
34358     * @param {Number} y - vertical position
34359     * @param {Boolean} isInstant - disables transitions
34360     */
34361     _processVerticalLayoutQueue : function( queue, isInstant )
34362     {
34363         var pos = this.el.getBox(true);
34364         var x = pos.x;
34365         var y = pos.y;
34366         var maxY = [];
34367         
34368         for (var i = 0; i < this.cols; i++){
34369             maxY[i] = pos.y;
34370         }
34371         
34372         Roo.each(queue, function(box, k){
34373             
34374             var col = k % this.cols;
34375             
34376             Roo.each(box, function(b,kk){
34377                 
34378                 b.el.position('absolute');
34379                 
34380                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34381                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34382                 
34383                 if(b.size == 'md-left' || b.size == 'md-right'){
34384                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34385                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34386                 }
34387                 
34388                 b.el.setWidth(width);
34389                 b.el.setHeight(height);
34390                 // iframe?
34391                 b.el.select('iframe',true).setSize(width,height);
34392                 
34393             }, this);
34394             
34395             for (var i = 0; i < this.cols; i++){
34396                 
34397                 if(maxY[i] < maxY[col]){
34398                     col = i;
34399                     continue;
34400                 }
34401                 
34402                 col = Math.min(col, i);
34403                 
34404             }
34405             
34406             x = pos.x + col * (this.colWidth + this.padWidth);
34407             
34408             y = maxY[col];
34409             
34410             var positions = [];
34411             
34412             switch (box.length){
34413                 case 1 :
34414                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34415                     break;
34416                 case 2 :
34417                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34418                     break;
34419                 case 3 :
34420                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34421                     break;
34422                 case 4 :
34423                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34424                     break;
34425                 default :
34426                     break;
34427             }
34428             
34429             Roo.each(box, function(b,kk){
34430                 
34431                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34432                 
34433                 var sz = b.el.getSize();
34434                 
34435                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34436                 
34437             }, this);
34438             
34439         }, this);
34440         
34441         var mY = 0;
34442         
34443         for (var i = 0; i < this.cols; i++){
34444             mY = Math.max(mY, maxY[i]);
34445         }
34446         
34447         this.el.setHeight(mY - pos.y);
34448         
34449     },
34450     
34451 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34452 //    {
34453 //        var pos = this.el.getBox(true);
34454 //        var x = pos.x;
34455 //        var y = pos.y;
34456 //        var maxX = pos.right;
34457 //        
34458 //        var maxHeight = 0;
34459 //        
34460 //        Roo.each(items, function(item, k){
34461 //            
34462 //            var c = k % 2;
34463 //            
34464 //            item.el.position('absolute');
34465 //                
34466 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34467 //
34468 //            item.el.setWidth(width);
34469 //
34470 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34471 //
34472 //            item.el.setHeight(height);
34473 //            
34474 //            if(c == 0){
34475 //                item.el.setXY([x, y], isInstant ? false : true);
34476 //            } else {
34477 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34478 //            }
34479 //            
34480 //            y = y + height + this.alternativePadWidth;
34481 //            
34482 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34483 //            
34484 //        }, this);
34485 //        
34486 //        this.el.setHeight(maxHeight);
34487 //        
34488 //    },
34489     
34490     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34491     {
34492         var pos = this.el.getBox(true);
34493         
34494         var minX = pos.x;
34495         var minY = pos.y;
34496         
34497         var maxX = pos.right;
34498         
34499         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34500         
34501         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34502         
34503         Roo.each(queue, function(box, k){
34504             
34505             Roo.each(box, function(b, kk){
34506                 
34507                 b.el.position('absolute');
34508                 
34509                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34510                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34511                 
34512                 if(b.size == 'md-left' || b.size == 'md-right'){
34513                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34514                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34515                 }
34516                 
34517                 b.el.setWidth(width);
34518                 b.el.setHeight(height);
34519                 
34520             }, this);
34521             
34522             if(!box.length){
34523                 return;
34524             }
34525             
34526             var positions = [];
34527             
34528             switch (box.length){
34529                 case 1 :
34530                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34531                     break;
34532                 case 2 :
34533                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34534                     break;
34535                 case 3 :
34536                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34537                     break;
34538                 case 4 :
34539                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34540                     break;
34541                 default :
34542                     break;
34543             }
34544             
34545             Roo.each(box, function(b,kk){
34546                 
34547                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34548                 
34549                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34550                 
34551             }, this);
34552             
34553         }, this);
34554         
34555     },
34556     
34557     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34558     {
34559         Roo.each(eItems, function(b,k){
34560             
34561             b.size = (k == 0) ? 'sm' : 'xs';
34562             b.x = (k == 0) ? 2 : 1;
34563             b.y = (k == 0) ? 2 : 1;
34564             
34565             b.el.position('absolute');
34566             
34567             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34568                 
34569             b.el.setWidth(width);
34570             
34571             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34572             
34573             b.el.setHeight(height);
34574             
34575         }, this);
34576
34577         var positions = [];
34578         
34579         positions.push({
34580             x : maxX - this.unitWidth * 2 - this.gutter,
34581             y : minY
34582         });
34583         
34584         positions.push({
34585             x : maxX - this.unitWidth,
34586             y : minY + (this.unitWidth + this.gutter) * 2
34587         });
34588         
34589         positions.push({
34590             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34591             y : minY
34592         });
34593         
34594         Roo.each(eItems, function(b,k){
34595             
34596             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34597
34598         }, this);
34599         
34600     },
34601     
34602     getVerticalOneBoxColPositions : function(x, y, box)
34603     {
34604         var pos = [];
34605         
34606         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34607         
34608         if(box[0].size == 'md-left'){
34609             rand = 0;
34610         }
34611         
34612         if(box[0].size == 'md-right'){
34613             rand = 1;
34614         }
34615         
34616         pos.push({
34617             x : x + (this.unitWidth + this.gutter) * rand,
34618             y : y
34619         });
34620         
34621         return pos;
34622     },
34623     
34624     getVerticalTwoBoxColPositions : function(x, y, box)
34625     {
34626         var pos = [];
34627         
34628         if(box[0].size == 'xs'){
34629             
34630             pos.push({
34631                 x : x,
34632                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34633             });
34634
34635             pos.push({
34636                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34637                 y : y
34638             });
34639             
34640             return pos;
34641             
34642         }
34643         
34644         pos.push({
34645             x : x,
34646             y : y
34647         });
34648
34649         pos.push({
34650             x : x + (this.unitWidth + this.gutter) * 2,
34651             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34652         });
34653         
34654         return pos;
34655         
34656     },
34657     
34658     getVerticalThreeBoxColPositions : function(x, y, box)
34659     {
34660         var pos = [];
34661         
34662         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34663             
34664             pos.push({
34665                 x : x,
34666                 y : y
34667             });
34668
34669             pos.push({
34670                 x : x + (this.unitWidth + this.gutter) * 1,
34671                 y : y
34672             });
34673             
34674             pos.push({
34675                 x : x + (this.unitWidth + this.gutter) * 2,
34676                 y : y
34677             });
34678             
34679             return pos;
34680             
34681         }
34682         
34683         if(box[0].size == 'xs' && box[1].size == 'xs'){
34684             
34685             pos.push({
34686                 x : x,
34687                 y : y
34688             });
34689
34690             pos.push({
34691                 x : x,
34692                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34693             });
34694             
34695             pos.push({
34696                 x : x + (this.unitWidth + this.gutter) * 1,
34697                 y : y
34698             });
34699             
34700             return pos;
34701             
34702         }
34703         
34704         pos.push({
34705             x : x,
34706             y : y
34707         });
34708
34709         pos.push({
34710             x : x + (this.unitWidth + this.gutter) * 2,
34711             y : y
34712         });
34713
34714         pos.push({
34715             x : x + (this.unitWidth + this.gutter) * 2,
34716             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34717         });
34718             
34719         return pos;
34720         
34721     },
34722     
34723     getVerticalFourBoxColPositions : function(x, y, box)
34724     {
34725         var pos = [];
34726         
34727         if(box[0].size == 'xs'){
34728             
34729             pos.push({
34730                 x : x,
34731                 y : y
34732             });
34733
34734             pos.push({
34735                 x : x,
34736                 y : y + (this.unitHeight + this.gutter) * 1
34737             });
34738             
34739             pos.push({
34740                 x : x,
34741                 y : y + (this.unitHeight + this.gutter) * 2
34742             });
34743             
34744             pos.push({
34745                 x : x + (this.unitWidth + this.gutter) * 1,
34746                 y : y
34747             });
34748             
34749             return pos;
34750             
34751         }
34752         
34753         pos.push({
34754             x : x,
34755             y : y
34756         });
34757
34758         pos.push({
34759             x : x + (this.unitWidth + this.gutter) * 2,
34760             y : y
34761         });
34762
34763         pos.push({
34764             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34765             y : y + (this.unitHeight + this.gutter) * 1
34766         });
34767
34768         pos.push({
34769             x : x + (this.unitWidth + this.gutter) * 2,
34770             y : y + (this.unitWidth + this.gutter) * 2
34771         });
34772
34773         return pos;
34774         
34775     },
34776     
34777     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34778     {
34779         var pos = [];
34780         
34781         if(box[0].size == 'md-left'){
34782             pos.push({
34783                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34784                 y : minY
34785             });
34786             
34787             return pos;
34788         }
34789         
34790         if(box[0].size == 'md-right'){
34791             pos.push({
34792                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34793                 y : minY + (this.unitWidth + this.gutter) * 1
34794             });
34795             
34796             return pos;
34797         }
34798         
34799         var rand = Math.floor(Math.random() * (4 - box[0].y));
34800         
34801         pos.push({
34802             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34803             y : minY + (this.unitWidth + this.gutter) * rand
34804         });
34805         
34806         return pos;
34807         
34808     },
34809     
34810     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34811     {
34812         var pos = [];
34813         
34814         if(box[0].size == 'xs'){
34815             
34816             pos.push({
34817                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34818                 y : minY
34819             });
34820
34821             pos.push({
34822                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34823                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34824             });
34825             
34826             return pos;
34827             
34828         }
34829         
34830         pos.push({
34831             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34832             y : minY
34833         });
34834
34835         pos.push({
34836             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34837             y : minY + (this.unitWidth + this.gutter) * 2
34838         });
34839         
34840         return pos;
34841         
34842     },
34843     
34844     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34845     {
34846         var pos = [];
34847         
34848         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34849             
34850             pos.push({
34851                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34852                 y : minY
34853             });
34854
34855             pos.push({
34856                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34857                 y : minY + (this.unitWidth + this.gutter) * 1
34858             });
34859             
34860             pos.push({
34861                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34862                 y : minY + (this.unitWidth + this.gutter) * 2
34863             });
34864             
34865             return pos;
34866             
34867         }
34868         
34869         if(box[0].size == 'xs' && box[1].size == 'xs'){
34870             
34871             pos.push({
34872                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34873                 y : minY
34874             });
34875
34876             pos.push({
34877                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34878                 y : minY
34879             });
34880             
34881             pos.push({
34882                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34883                 y : minY + (this.unitWidth + this.gutter) * 1
34884             });
34885             
34886             return pos;
34887             
34888         }
34889         
34890         pos.push({
34891             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34892             y : minY
34893         });
34894
34895         pos.push({
34896             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34897             y : minY + (this.unitWidth + this.gutter) * 2
34898         });
34899
34900         pos.push({
34901             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34902             y : minY + (this.unitWidth + this.gutter) * 2
34903         });
34904             
34905         return pos;
34906         
34907     },
34908     
34909     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34910     {
34911         var pos = [];
34912         
34913         if(box[0].size == 'xs'){
34914             
34915             pos.push({
34916                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34917                 y : minY
34918             });
34919
34920             pos.push({
34921                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34922                 y : minY
34923             });
34924             
34925             pos.push({
34926                 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),
34927                 y : minY
34928             });
34929             
34930             pos.push({
34931                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34932                 y : minY + (this.unitWidth + this.gutter) * 1
34933             });
34934             
34935             return pos;
34936             
34937         }
34938         
34939         pos.push({
34940             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34941             y : minY
34942         });
34943         
34944         pos.push({
34945             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34946             y : minY + (this.unitWidth + this.gutter) * 2
34947         });
34948         
34949         pos.push({
34950             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34951             y : minY + (this.unitWidth + this.gutter) * 2
34952         });
34953         
34954         pos.push({
34955             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),
34956             y : minY + (this.unitWidth + this.gutter) * 2
34957         });
34958
34959         return pos;
34960         
34961     },
34962     
34963     /**
34964     * remove a Masonry Brick
34965     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34966     */
34967     removeBrick : function(brick_id)
34968     {
34969         if (!brick_id) {
34970             return;
34971         }
34972         
34973         for (var i = 0; i<this.bricks.length; i++) {
34974             if (this.bricks[i].id == brick_id) {
34975                 this.bricks.splice(i,1);
34976                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34977                 this.initial();
34978             }
34979         }
34980     },
34981     
34982     /**
34983     * adds a Masonry Brick
34984     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34985     */
34986     addBrick : function(cfg)
34987     {
34988         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34989         //this.register(cn);
34990         cn.parentId = this.id;
34991         cn.render(this.el);
34992         return cn;
34993     },
34994     
34995     /**
34996     * register a Masonry Brick
34997     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34998     */
34999     
35000     register : function(brick)
35001     {
35002         this.bricks.push(brick);
35003         brick.masonryId = this.id;
35004     },
35005     
35006     /**
35007     * clear all the Masonry Brick
35008     */
35009     clearAll : function()
35010     {
35011         this.bricks = [];
35012         //this.getChildContainer().dom.innerHTML = "";
35013         this.el.dom.innerHTML = '';
35014     },
35015     
35016     getSelected : function()
35017     {
35018         if (!this.selectedBrick) {
35019             return false;
35020         }
35021         
35022         return this.selectedBrick;
35023     }
35024 });
35025
35026 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35027     
35028     groups: {},
35029      /**
35030     * register a Masonry Layout
35031     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35032     */
35033     
35034     register : function(layout)
35035     {
35036         this.groups[layout.id] = layout;
35037     },
35038     /**
35039     * fetch a  Masonry Layout based on the masonry layout ID
35040     * @param {string} the masonry layout to add
35041     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35042     */
35043     
35044     get: function(layout_id) {
35045         if (typeof(this.groups[layout_id]) == 'undefined') {
35046             return false;
35047         }
35048         return this.groups[layout_id] ;
35049     }
35050     
35051     
35052     
35053 });
35054
35055  
35056
35057  /**
35058  *
35059  * This is based on 
35060  * http://masonry.desandro.com
35061  *
35062  * The idea is to render all the bricks based on vertical width...
35063  *
35064  * The original code extends 'outlayer' - we might need to use that....
35065  * 
35066  */
35067
35068
35069 /**
35070  * @class Roo.bootstrap.LayoutMasonryAuto
35071  * @extends Roo.bootstrap.Component
35072  * Bootstrap Layout Masonry class
35073  * 
35074  * @constructor
35075  * Create a new Element
35076  * @param {Object} config The config object
35077  */
35078
35079 Roo.bootstrap.LayoutMasonryAuto = function(config){
35080     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35081 };
35082
35083 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35084     
35085       /**
35086      * @cfg {Boolean} isFitWidth  - resize the width..
35087      */   
35088     isFitWidth : false,  // options..
35089     /**
35090      * @cfg {Boolean} isOriginLeft = left align?
35091      */   
35092     isOriginLeft : true,
35093     /**
35094      * @cfg {Boolean} isOriginTop = top align?
35095      */   
35096     isOriginTop : false,
35097     /**
35098      * @cfg {Boolean} isLayoutInstant = no animation?
35099      */   
35100     isLayoutInstant : false, // needed?
35101     /**
35102      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35103      */   
35104     isResizingContainer : true,
35105     /**
35106      * @cfg {Number} columnWidth  width of the columns 
35107      */   
35108     
35109     columnWidth : 0,
35110     
35111     /**
35112      * @cfg {Number} maxCols maximum number of columns
35113      */   
35114     
35115     maxCols: 0,
35116     /**
35117      * @cfg {Number} padHeight padding below box..
35118      */   
35119     
35120     padHeight : 10, 
35121     
35122     /**
35123      * @cfg {Boolean} isAutoInitial defalut true
35124      */   
35125     
35126     isAutoInitial : true, 
35127     
35128     // private?
35129     gutter : 0,
35130     
35131     containerWidth: 0,
35132     initialColumnWidth : 0,
35133     currentSize : null,
35134     
35135     colYs : null, // array.
35136     maxY : 0,
35137     padWidth: 10,
35138     
35139     
35140     tag: 'div',
35141     cls: '',
35142     bricks: null, //CompositeElement
35143     cols : 0, // array?
35144     // element : null, // wrapped now this.el
35145     _isLayoutInited : null, 
35146     
35147     
35148     getAutoCreate : function(){
35149         
35150         var cfg = {
35151             tag: this.tag,
35152             cls: 'blog-masonary-wrapper ' + this.cls,
35153             cn : {
35154                 cls : 'mas-boxes masonary'
35155             }
35156         };
35157         
35158         return cfg;
35159     },
35160     
35161     getChildContainer: function( )
35162     {
35163         if (this.boxesEl) {
35164             return this.boxesEl;
35165         }
35166         
35167         this.boxesEl = this.el.select('.mas-boxes').first();
35168         
35169         return this.boxesEl;
35170     },
35171     
35172     
35173     initEvents : function()
35174     {
35175         var _this = this;
35176         
35177         if(this.isAutoInitial){
35178             Roo.log('hook children rendered');
35179             this.on('childrenrendered', function() {
35180                 Roo.log('children rendered');
35181                 _this.initial();
35182             } ,this);
35183         }
35184         
35185     },
35186     
35187     initial : function()
35188     {
35189         this.reloadItems();
35190
35191         this.currentSize = this.el.getBox(true);
35192
35193         /// was window resize... - let's see if this works..
35194         Roo.EventManager.onWindowResize(this.resize, this); 
35195
35196         if(!this.isAutoInitial){
35197             this.layout();
35198             return;
35199         }
35200         
35201         this.layout.defer(500,this);
35202     },
35203     
35204     reloadItems: function()
35205     {
35206         this.bricks = this.el.select('.masonry-brick', true);
35207         
35208         this.bricks.each(function(b) {
35209             //Roo.log(b.getSize());
35210             if (!b.attr('originalwidth')) {
35211                 b.attr('originalwidth',  b.getSize().width);
35212             }
35213             
35214         });
35215         
35216         Roo.log(this.bricks.elements.length);
35217     },
35218     
35219     resize : function()
35220     {
35221         Roo.log('resize');
35222         var cs = this.el.getBox(true);
35223         
35224         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35225             Roo.log("no change in with or X");
35226             return;
35227         }
35228         this.currentSize = cs;
35229         this.layout();
35230     },
35231     
35232     layout : function()
35233     {
35234          Roo.log('layout');
35235         this._resetLayout();
35236         //this._manageStamps();
35237       
35238         // don't animate first layout
35239         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35240         this.layoutItems( isInstant );
35241       
35242         // flag for initalized
35243         this._isLayoutInited = true;
35244     },
35245     
35246     layoutItems : function( isInstant )
35247     {
35248         //var items = this._getItemsForLayout( this.items );
35249         // original code supports filtering layout items.. we just ignore it..
35250         
35251         this._layoutItems( this.bricks , isInstant );
35252       
35253         this._postLayout();
35254     },
35255     _layoutItems : function ( items , isInstant)
35256     {
35257        //this.fireEvent( 'layout', this, items );
35258     
35259
35260         if ( !items || !items.elements.length ) {
35261           // no items, emit event with empty array
35262             return;
35263         }
35264
35265         var queue = [];
35266         items.each(function(item) {
35267             Roo.log("layout item");
35268             Roo.log(item);
35269             // get x/y object from method
35270             var position = this._getItemLayoutPosition( item );
35271             // enqueue
35272             position.item = item;
35273             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35274             queue.push( position );
35275         }, this);
35276       
35277         this._processLayoutQueue( queue );
35278     },
35279     /** Sets position of item in DOM
35280     * @param {Element} item
35281     * @param {Number} x - horizontal position
35282     * @param {Number} y - vertical position
35283     * @param {Boolean} isInstant - disables transitions
35284     */
35285     _processLayoutQueue : function( queue )
35286     {
35287         for ( var i=0, len = queue.length; i < len; i++ ) {
35288             var obj = queue[i];
35289             obj.item.position('absolute');
35290             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35291         }
35292     },
35293       
35294     
35295     /**
35296     * Any logic you want to do after each layout,
35297     * i.e. size the container
35298     */
35299     _postLayout : function()
35300     {
35301         this.resizeContainer();
35302     },
35303     
35304     resizeContainer : function()
35305     {
35306         if ( !this.isResizingContainer ) {
35307             return;
35308         }
35309         var size = this._getContainerSize();
35310         if ( size ) {
35311             this.el.setSize(size.width,size.height);
35312             this.boxesEl.setSize(size.width,size.height);
35313         }
35314     },
35315     
35316     
35317     
35318     _resetLayout : function()
35319     {
35320         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35321         this.colWidth = this.el.getWidth();
35322         //this.gutter = this.el.getWidth(); 
35323         
35324         this.measureColumns();
35325
35326         // reset column Y
35327         var i = this.cols;
35328         this.colYs = [];
35329         while (i--) {
35330             this.colYs.push( 0 );
35331         }
35332     
35333         this.maxY = 0;
35334     },
35335
35336     measureColumns : function()
35337     {
35338         this.getContainerWidth();
35339       // if columnWidth is 0, default to outerWidth of first item
35340         if ( !this.columnWidth ) {
35341             var firstItem = this.bricks.first();
35342             Roo.log(firstItem);
35343             this.columnWidth  = this.containerWidth;
35344             if (firstItem && firstItem.attr('originalwidth') ) {
35345                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35346             }
35347             // columnWidth fall back to item of first element
35348             Roo.log("set column width?");
35349                         this.initialColumnWidth = this.columnWidth  ;
35350
35351             // if first elem has no width, default to size of container
35352             
35353         }
35354         
35355         
35356         if (this.initialColumnWidth) {
35357             this.columnWidth = this.initialColumnWidth;
35358         }
35359         
35360         
35361             
35362         // column width is fixed at the top - however if container width get's smaller we should
35363         // reduce it...
35364         
35365         // this bit calcs how man columns..
35366             
35367         var columnWidth = this.columnWidth += this.gutter;
35368       
35369         // calculate columns
35370         var containerWidth = this.containerWidth + this.gutter;
35371         
35372         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35373         // fix rounding errors, typically with gutters
35374         var excess = columnWidth - containerWidth % columnWidth;
35375         
35376         
35377         // if overshoot is less than a pixel, round up, otherwise floor it
35378         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35379         cols = Math[ mathMethod ]( cols );
35380         this.cols = Math.max( cols, 1 );
35381         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35382         
35383          // padding positioning..
35384         var totalColWidth = this.cols * this.columnWidth;
35385         var padavail = this.containerWidth - totalColWidth;
35386         // so for 2 columns - we need 3 'pads'
35387         
35388         var padNeeded = (1+this.cols) * this.padWidth;
35389         
35390         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35391         
35392         this.columnWidth += padExtra
35393         //this.padWidth = Math.floor(padavail /  ( this.cols));
35394         
35395         // adjust colum width so that padding is fixed??
35396         
35397         // we have 3 columns ... total = width * 3
35398         // we have X left over... that should be used by 
35399         
35400         //if (this.expandC) {
35401             
35402         //}
35403         
35404         
35405         
35406     },
35407     
35408     getContainerWidth : function()
35409     {
35410        /* // container is parent if fit width
35411         var container = this.isFitWidth ? this.element.parentNode : this.element;
35412         // check that this.size and size are there
35413         // IE8 triggers resize on body size change, so they might not be
35414         
35415         var size = getSize( container );  //FIXME
35416         this.containerWidth = size && size.innerWidth; //FIXME
35417         */
35418          
35419         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35420         
35421     },
35422     
35423     _getItemLayoutPosition : function( item )  // what is item?
35424     {
35425         // we resize the item to our columnWidth..
35426       
35427         item.setWidth(this.columnWidth);
35428         item.autoBoxAdjust  = false;
35429         
35430         var sz = item.getSize();
35431  
35432         // how many columns does this brick span
35433         var remainder = this.containerWidth % this.columnWidth;
35434         
35435         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35436         // round if off by 1 pixel, otherwise use ceil
35437         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35438         colSpan = Math.min( colSpan, this.cols );
35439         
35440         // normally this should be '1' as we dont' currently allow multi width columns..
35441         
35442         var colGroup = this._getColGroup( colSpan );
35443         // get the minimum Y value from the columns
35444         var minimumY = Math.min.apply( Math, colGroup );
35445         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35446         
35447         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35448          
35449         // position the brick
35450         var position = {
35451             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35452             y: this.currentSize.y + minimumY + this.padHeight
35453         };
35454         
35455         Roo.log(position);
35456         // apply setHeight to necessary columns
35457         var setHeight = minimumY + sz.height + this.padHeight;
35458         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35459         
35460         var setSpan = this.cols + 1 - colGroup.length;
35461         for ( var i = 0; i < setSpan; i++ ) {
35462           this.colYs[ shortColIndex + i ] = setHeight ;
35463         }
35464       
35465         return position;
35466     },
35467     
35468     /**
35469      * @param {Number} colSpan - number of columns the element spans
35470      * @returns {Array} colGroup
35471      */
35472     _getColGroup : function( colSpan )
35473     {
35474         if ( colSpan < 2 ) {
35475           // if brick spans only one column, use all the column Ys
35476           return this.colYs;
35477         }
35478       
35479         var colGroup = [];
35480         // how many different places could this brick fit horizontally
35481         var groupCount = this.cols + 1 - colSpan;
35482         // for each group potential horizontal position
35483         for ( var i = 0; i < groupCount; i++ ) {
35484           // make an array of colY values for that one group
35485           var groupColYs = this.colYs.slice( i, i + colSpan );
35486           // and get the max value of the array
35487           colGroup[i] = Math.max.apply( Math, groupColYs );
35488         }
35489         return colGroup;
35490     },
35491     /*
35492     _manageStamp : function( stamp )
35493     {
35494         var stampSize =  stamp.getSize();
35495         var offset = stamp.getBox();
35496         // get the columns that this stamp affects
35497         var firstX = this.isOriginLeft ? offset.x : offset.right;
35498         var lastX = firstX + stampSize.width;
35499         var firstCol = Math.floor( firstX / this.columnWidth );
35500         firstCol = Math.max( 0, firstCol );
35501         
35502         var lastCol = Math.floor( lastX / this.columnWidth );
35503         // lastCol should not go over if multiple of columnWidth #425
35504         lastCol -= lastX % this.columnWidth ? 0 : 1;
35505         lastCol = Math.min( this.cols - 1, lastCol );
35506         
35507         // set colYs to bottom of the stamp
35508         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35509             stampSize.height;
35510             
35511         for ( var i = firstCol; i <= lastCol; i++ ) {
35512           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35513         }
35514     },
35515     */
35516     
35517     _getContainerSize : function()
35518     {
35519         this.maxY = Math.max.apply( Math, this.colYs );
35520         var size = {
35521             height: this.maxY
35522         };
35523       
35524         if ( this.isFitWidth ) {
35525             size.width = this._getContainerFitWidth();
35526         }
35527       
35528         return size;
35529     },
35530     
35531     _getContainerFitWidth : function()
35532     {
35533         var unusedCols = 0;
35534         // count unused columns
35535         var i = this.cols;
35536         while ( --i ) {
35537           if ( this.colYs[i] !== 0 ) {
35538             break;
35539           }
35540           unusedCols++;
35541         }
35542         // fit container to columns that have been used
35543         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35544     },
35545     
35546     needsResizeLayout : function()
35547     {
35548         var previousWidth = this.containerWidth;
35549         this.getContainerWidth();
35550         return previousWidth !== this.containerWidth;
35551     }
35552  
35553 });
35554
35555  
35556
35557  /*
35558  * - LGPL
35559  *
35560  * element
35561  * 
35562  */
35563
35564 /**
35565  * @class Roo.bootstrap.MasonryBrick
35566  * @extends Roo.bootstrap.Component
35567  * Bootstrap MasonryBrick class
35568  * 
35569  * @constructor
35570  * Create a new MasonryBrick
35571  * @param {Object} config The config object
35572  */
35573
35574 Roo.bootstrap.MasonryBrick = function(config){
35575     
35576     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35577     
35578     Roo.bootstrap.MasonryBrick.register(this);
35579     
35580     this.addEvents({
35581         // raw events
35582         /**
35583          * @event click
35584          * When a MasonryBrick is clcik
35585          * @param {Roo.bootstrap.MasonryBrick} this
35586          * @param {Roo.EventObject} e
35587          */
35588         "click" : true
35589     });
35590 };
35591
35592 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35593     
35594     /**
35595      * @cfg {String} title
35596      */   
35597     title : '',
35598     /**
35599      * @cfg {String} html
35600      */   
35601     html : '',
35602     /**
35603      * @cfg {String} bgimage
35604      */   
35605     bgimage : '',
35606     /**
35607      * @cfg {String} videourl
35608      */   
35609     videourl : '',
35610     /**
35611      * @cfg {String} cls
35612      */   
35613     cls : '',
35614     /**
35615      * @cfg {String} href
35616      */   
35617     href : '',
35618     /**
35619      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35620      */   
35621     size : 'xs',
35622     
35623     /**
35624      * @cfg {String} placetitle (center|bottom)
35625      */   
35626     placetitle : '',
35627     
35628     /**
35629      * @cfg {Boolean} isFitContainer defalut true
35630      */   
35631     isFitContainer : true, 
35632     
35633     /**
35634      * @cfg {Boolean} preventDefault defalut false
35635      */   
35636     preventDefault : false, 
35637     
35638     /**
35639      * @cfg {Boolean} inverse defalut false
35640      */   
35641     maskInverse : false, 
35642     
35643     getAutoCreate : function()
35644     {
35645         if(!this.isFitContainer){
35646             return this.getSplitAutoCreate();
35647         }
35648         
35649         var cls = 'masonry-brick masonry-brick-full';
35650         
35651         if(this.href.length){
35652             cls += ' masonry-brick-link';
35653         }
35654         
35655         if(this.bgimage.length){
35656             cls += ' masonry-brick-image';
35657         }
35658         
35659         if(this.maskInverse){
35660             cls += ' mask-inverse';
35661         }
35662         
35663         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35664             cls += ' enable-mask';
35665         }
35666         
35667         if(this.size){
35668             cls += ' masonry-' + this.size + '-brick';
35669         }
35670         
35671         if(this.placetitle.length){
35672             
35673             switch (this.placetitle) {
35674                 case 'center' :
35675                     cls += ' masonry-center-title';
35676                     break;
35677                 case 'bottom' :
35678                     cls += ' masonry-bottom-title';
35679                     break;
35680                 default:
35681                     break;
35682             }
35683             
35684         } else {
35685             if(!this.html.length && !this.bgimage.length){
35686                 cls += ' masonry-center-title';
35687             }
35688
35689             if(!this.html.length && this.bgimage.length){
35690                 cls += ' masonry-bottom-title';
35691             }
35692         }
35693         
35694         if(this.cls){
35695             cls += ' ' + this.cls;
35696         }
35697         
35698         var cfg = {
35699             tag: (this.href.length) ? 'a' : 'div',
35700             cls: cls,
35701             cn: [
35702                 {
35703                     tag: 'div',
35704                     cls: 'masonry-brick-mask'
35705                 },
35706                 {
35707                     tag: 'div',
35708                     cls: 'masonry-brick-paragraph',
35709                     cn: []
35710                 }
35711             ]
35712         };
35713         
35714         if(this.href.length){
35715             cfg.href = this.href;
35716         }
35717         
35718         var cn = cfg.cn[1].cn;
35719         
35720         if(this.title.length){
35721             cn.push({
35722                 tag: 'h4',
35723                 cls: 'masonry-brick-title',
35724                 html: this.title
35725             });
35726         }
35727         
35728         if(this.html.length){
35729             cn.push({
35730                 tag: 'p',
35731                 cls: 'masonry-brick-text',
35732                 html: this.html
35733             });
35734         }
35735         
35736         if (!this.title.length && !this.html.length) {
35737             cfg.cn[1].cls += ' hide';
35738         }
35739         
35740         if(this.bgimage.length){
35741             cfg.cn.push({
35742                 tag: 'img',
35743                 cls: 'masonry-brick-image-view',
35744                 src: this.bgimage
35745             });
35746         }
35747         
35748         if(this.videourl.length){
35749             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35750             // youtube support only?
35751             cfg.cn.push({
35752                 tag: 'iframe',
35753                 cls: 'masonry-brick-image-view',
35754                 src: vurl,
35755                 frameborder : 0,
35756                 allowfullscreen : true
35757             });
35758         }
35759         
35760         return cfg;
35761         
35762     },
35763     
35764     getSplitAutoCreate : function()
35765     {
35766         var cls = 'masonry-brick masonry-brick-split';
35767         
35768         if(this.href.length){
35769             cls += ' masonry-brick-link';
35770         }
35771         
35772         if(this.bgimage.length){
35773             cls += ' masonry-brick-image';
35774         }
35775         
35776         if(this.size){
35777             cls += ' masonry-' + this.size + '-brick';
35778         }
35779         
35780         switch (this.placetitle) {
35781             case 'center' :
35782                 cls += ' masonry-center-title';
35783                 break;
35784             case 'bottom' :
35785                 cls += ' masonry-bottom-title';
35786                 break;
35787             default:
35788                 if(!this.bgimage.length){
35789                     cls += ' masonry-center-title';
35790                 }
35791
35792                 if(this.bgimage.length){
35793                     cls += ' masonry-bottom-title';
35794                 }
35795                 break;
35796         }
35797         
35798         if(this.cls){
35799             cls += ' ' + this.cls;
35800         }
35801         
35802         var cfg = {
35803             tag: (this.href.length) ? 'a' : 'div',
35804             cls: cls,
35805             cn: [
35806                 {
35807                     tag: 'div',
35808                     cls: 'masonry-brick-split-head',
35809                     cn: [
35810                         {
35811                             tag: 'div',
35812                             cls: 'masonry-brick-paragraph',
35813                             cn: []
35814                         }
35815                     ]
35816                 },
35817                 {
35818                     tag: 'div',
35819                     cls: 'masonry-brick-split-body',
35820                     cn: []
35821                 }
35822             ]
35823         };
35824         
35825         if(this.href.length){
35826             cfg.href = this.href;
35827         }
35828         
35829         if(this.title.length){
35830             cfg.cn[0].cn[0].cn.push({
35831                 tag: 'h4',
35832                 cls: 'masonry-brick-title',
35833                 html: this.title
35834             });
35835         }
35836         
35837         if(this.html.length){
35838             cfg.cn[1].cn.push({
35839                 tag: 'p',
35840                 cls: 'masonry-brick-text',
35841                 html: this.html
35842             });
35843         }
35844
35845         if(this.bgimage.length){
35846             cfg.cn[0].cn.push({
35847                 tag: 'img',
35848                 cls: 'masonry-brick-image-view',
35849                 src: this.bgimage
35850             });
35851         }
35852         
35853         if(this.videourl.length){
35854             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35855             // youtube support only?
35856             cfg.cn[0].cn.cn.push({
35857                 tag: 'iframe',
35858                 cls: 'masonry-brick-image-view',
35859                 src: vurl,
35860                 frameborder : 0,
35861                 allowfullscreen : true
35862             });
35863         }
35864         
35865         return cfg;
35866     },
35867     
35868     initEvents: function() 
35869     {
35870         switch (this.size) {
35871             case 'xs' :
35872                 this.x = 1;
35873                 this.y = 1;
35874                 break;
35875             case 'sm' :
35876                 this.x = 2;
35877                 this.y = 2;
35878                 break;
35879             case 'md' :
35880             case 'md-left' :
35881             case 'md-right' :
35882                 this.x = 3;
35883                 this.y = 3;
35884                 break;
35885             case 'tall' :
35886                 this.x = 2;
35887                 this.y = 3;
35888                 break;
35889             case 'wide' :
35890                 this.x = 3;
35891                 this.y = 2;
35892                 break;
35893             case 'wide-thin' :
35894                 this.x = 3;
35895                 this.y = 1;
35896                 break;
35897                         
35898             default :
35899                 break;
35900         }
35901         
35902         if(Roo.isTouch){
35903             this.el.on('touchstart', this.onTouchStart, this);
35904             this.el.on('touchmove', this.onTouchMove, this);
35905             this.el.on('touchend', this.onTouchEnd, this);
35906             this.el.on('contextmenu', this.onContextMenu, this);
35907         } else {
35908             this.el.on('mouseenter'  ,this.enter, this);
35909             this.el.on('mouseleave', this.leave, this);
35910             this.el.on('click', this.onClick, this);
35911         }
35912         
35913         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35914             this.parent().bricks.push(this);   
35915         }
35916         
35917     },
35918     
35919     onClick: function(e, el)
35920     {
35921         var time = this.endTimer - this.startTimer;
35922         // Roo.log(e.preventDefault());
35923         if(Roo.isTouch){
35924             if(time > 1000){
35925                 e.preventDefault();
35926                 return;
35927             }
35928         }
35929         
35930         if(!this.preventDefault){
35931             return;
35932         }
35933         
35934         e.preventDefault();
35935         
35936         if (this.activeClass != '') {
35937             this.selectBrick();
35938         }
35939         
35940         this.fireEvent('click', this, e);
35941     },
35942     
35943     enter: function(e, el)
35944     {
35945         e.preventDefault();
35946         
35947         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35948             return;
35949         }
35950         
35951         if(this.bgimage.length && this.html.length){
35952             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35953         }
35954     },
35955     
35956     leave: function(e, el)
35957     {
35958         e.preventDefault();
35959         
35960         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35961             return;
35962         }
35963         
35964         if(this.bgimage.length && this.html.length){
35965             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35966         }
35967     },
35968     
35969     onTouchStart: function(e, el)
35970     {
35971 //        e.preventDefault();
35972         
35973         this.touchmoved = false;
35974         
35975         if(!this.isFitContainer){
35976             return;
35977         }
35978         
35979         if(!this.bgimage.length || !this.html.length){
35980             return;
35981         }
35982         
35983         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35984         
35985         this.timer = new Date().getTime();
35986         
35987     },
35988     
35989     onTouchMove: function(e, el)
35990     {
35991         this.touchmoved = true;
35992     },
35993     
35994     onContextMenu : function(e,el)
35995     {
35996         e.preventDefault();
35997         e.stopPropagation();
35998         return false;
35999     },
36000     
36001     onTouchEnd: function(e, el)
36002     {
36003 //        e.preventDefault();
36004         
36005         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36006         
36007             this.leave(e,el);
36008             
36009             return;
36010         }
36011         
36012         if(!this.bgimage.length || !this.html.length){
36013             
36014             if(this.href.length){
36015                 window.location.href = this.href;
36016             }
36017             
36018             return;
36019         }
36020         
36021         if(!this.isFitContainer){
36022             return;
36023         }
36024         
36025         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36026         
36027         window.location.href = this.href;
36028     },
36029     
36030     //selection on single brick only
36031     selectBrick : function() {
36032         
36033         if (!this.parentId) {
36034             return;
36035         }
36036         
36037         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36038         var index = m.selectedBrick.indexOf(this.id);
36039         
36040         if ( index > -1) {
36041             m.selectedBrick.splice(index,1);
36042             this.el.removeClass(this.activeClass);
36043             return;
36044         }
36045         
36046         for(var i = 0; i < m.selectedBrick.length; i++) {
36047             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36048             b.el.removeClass(b.activeClass);
36049         }
36050         
36051         m.selectedBrick = [];
36052         
36053         m.selectedBrick.push(this.id);
36054         this.el.addClass(this.activeClass);
36055         return;
36056     },
36057     
36058     isSelected : function(){
36059         return this.el.hasClass(this.activeClass);
36060         
36061     }
36062 });
36063
36064 Roo.apply(Roo.bootstrap.MasonryBrick, {
36065     
36066     //groups: {},
36067     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36068      /**
36069     * register a Masonry Brick
36070     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36071     */
36072     
36073     register : function(brick)
36074     {
36075         //this.groups[brick.id] = brick;
36076         this.groups.add(brick.id, brick);
36077     },
36078     /**
36079     * fetch a  masonry brick based on the masonry brick ID
36080     * @param {string} the masonry brick to add
36081     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36082     */
36083     
36084     get: function(brick_id) 
36085     {
36086         // if (typeof(this.groups[brick_id]) == 'undefined') {
36087         //     return false;
36088         // }
36089         // return this.groups[brick_id] ;
36090         
36091         if(this.groups.key(brick_id)) {
36092             return this.groups.key(brick_id);
36093         }
36094         
36095         return false;
36096     }
36097     
36098     
36099     
36100 });
36101
36102  /*
36103  * - LGPL
36104  *
36105  * element
36106  * 
36107  */
36108
36109 /**
36110  * @class Roo.bootstrap.Brick
36111  * @extends Roo.bootstrap.Component
36112  * Bootstrap Brick class
36113  * 
36114  * @constructor
36115  * Create a new Brick
36116  * @param {Object} config The config object
36117  */
36118
36119 Roo.bootstrap.Brick = function(config){
36120     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36121     
36122     this.addEvents({
36123         // raw events
36124         /**
36125          * @event click
36126          * When a Brick is click
36127          * @param {Roo.bootstrap.Brick} this
36128          * @param {Roo.EventObject} e
36129          */
36130         "click" : true
36131     });
36132 };
36133
36134 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36135     
36136     /**
36137      * @cfg {String} title
36138      */   
36139     title : '',
36140     /**
36141      * @cfg {String} html
36142      */   
36143     html : '',
36144     /**
36145      * @cfg {String} bgimage
36146      */   
36147     bgimage : '',
36148     /**
36149      * @cfg {String} cls
36150      */   
36151     cls : '',
36152     /**
36153      * @cfg {String} href
36154      */   
36155     href : '',
36156     /**
36157      * @cfg {String} video
36158      */   
36159     video : '',
36160     /**
36161      * @cfg {Boolean} square
36162      */   
36163     square : true,
36164     
36165     getAutoCreate : function()
36166     {
36167         var cls = 'roo-brick';
36168         
36169         if(this.href.length){
36170             cls += ' roo-brick-link';
36171         }
36172         
36173         if(this.bgimage.length){
36174             cls += ' roo-brick-image';
36175         }
36176         
36177         if(!this.html.length && !this.bgimage.length){
36178             cls += ' roo-brick-center-title';
36179         }
36180         
36181         if(!this.html.length && this.bgimage.length){
36182             cls += ' roo-brick-bottom-title';
36183         }
36184         
36185         if(this.cls){
36186             cls += ' ' + this.cls;
36187         }
36188         
36189         var cfg = {
36190             tag: (this.href.length) ? 'a' : 'div',
36191             cls: cls,
36192             cn: [
36193                 {
36194                     tag: 'div',
36195                     cls: 'roo-brick-paragraph',
36196                     cn: []
36197                 }
36198             ]
36199         };
36200         
36201         if(this.href.length){
36202             cfg.href = this.href;
36203         }
36204         
36205         var cn = cfg.cn[0].cn;
36206         
36207         if(this.title.length){
36208             cn.push({
36209                 tag: 'h4',
36210                 cls: 'roo-brick-title',
36211                 html: this.title
36212             });
36213         }
36214         
36215         if(this.html.length){
36216             cn.push({
36217                 tag: 'p',
36218                 cls: 'roo-brick-text',
36219                 html: this.html
36220             });
36221         } else {
36222             cn.cls += ' hide';
36223         }
36224         
36225         if(this.bgimage.length){
36226             cfg.cn.push({
36227                 tag: 'img',
36228                 cls: 'roo-brick-image-view',
36229                 src: this.bgimage
36230             });
36231         }
36232         
36233         return cfg;
36234     },
36235     
36236     initEvents: function() 
36237     {
36238         if(this.title.length || this.html.length){
36239             this.el.on('mouseenter'  ,this.enter, this);
36240             this.el.on('mouseleave', this.leave, this);
36241         }
36242         
36243         Roo.EventManager.onWindowResize(this.resize, this); 
36244         
36245         if(this.bgimage.length){
36246             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36247             this.imageEl.on('load', this.onImageLoad, this);
36248             return;
36249         }
36250         
36251         this.resize();
36252     },
36253     
36254     onImageLoad : function()
36255     {
36256         this.resize();
36257     },
36258     
36259     resize : function()
36260     {
36261         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36262         
36263         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36264         
36265         if(this.bgimage.length){
36266             var image = this.el.select('.roo-brick-image-view', true).first();
36267             
36268             image.setWidth(paragraph.getWidth());
36269             
36270             if(this.square){
36271                 image.setHeight(paragraph.getWidth());
36272             }
36273             
36274             this.el.setHeight(image.getHeight());
36275             paragraph.setHeight(image.getHeight());
36276             
36277         }
36278         
36279     },
36280     
36281     enter: function(e, el)
36282     {
36283         e.preventDefault();
36284         
36285         if(this.bgimage.length){
36286             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36287             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36288         }
36289     },
36290     
36291     leave: function(e, el)
36292     {
36293         e.preventDefault();
36294         
36295         if(this.bgimage.length){
36296             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36297             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36298         }
36299     }
36300     
36301 });
36302
36303  
36304
36305  /*
36306  * - LGPL
36307  *
36308  * Number field 
36309  */
36310
36311 /**
36312  * @class Roo.bootstrap.NumberField
36313  * @extends Roo.bootstrap.Input
36314  * Bootstrap NumberField class
36315  * 
36316  * 
36317  * 
36318  * 
36319  * @constructor
36320  * Create a new NumberField
36321  * @param {Object} config The config object
36322  */
36323
36324 Roo.bootstrap.NumberField = function(config){
36325     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36326 };
36327
36328 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36329     
36330     /**
36331      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36332      */
36333     allowDecimals : true,
36334     /**
36335      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36336      */
36337     decimalSeparator : ".",
36338     /**
36339      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36340      */
36341     decimalPrecision : 2,
36342     /**
36343      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36344      */
36345     allowNegative : true,
36346     
36347     /**
36348      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36349      */
36350     allowZero: true,
36351     /**
36352      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36353      */
36354     minValue : Number.NEGATIVE_INFINITY,
36355     /**
36356      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36357      */
36358     maxValue : Number.MAX_VALUE,
36359     /**
36360      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36361      */
36362     minText : "The minimum value for this field is {0}",
36363     /**
36364      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36365      */
36366     maxText : "The maximum value for this field is {0}",
36367     /**
36368      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36369      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36370      */
36371     nanText : "{0} is not a valid number",
36372     /**
36373      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36374      */
36375     thousandsDelimiter : false,
36376     /**
36377      * @cfg {String} valueAlign alignment of value
36378      */
36379     valueAlign : "left",
36380
36381     getAutoCreate : function()
36382     {
36383         var hiddenInput = {
36384             tag: 'input',
36385             type: 'hidden',
36386             id: Roo.id(),
36387             cls: 'hidden-number-input'
36388         };
36389         
36390         if (this.name) {
36391             hiddenInput.name = this.name;
36392         }
36393         
36394         this.name = '';
36395         
36396         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36397         
36398         this.name = hiddenInput.name;
36399         
36400         if(cfg.cn.length > 0) {
36401             cfg.cn.push(hiddenInput);
36402         }
36403         
36404         return cfg;
36405     },
36406
36407     // private
36408     initEvents : function()
36409     {   
36410         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36411         
36412         var allowed = "0123456789";
36413         
36414         if(this.allowDecimals){
36415             allowed += this.decimalSeparator;
36416         }
36417         
36418         if(this.allowNegative){
36419             allowed += "-";
36420         }
36421         
36422         if(this.thousandsDelimiter) {
36423             allowed += ",";
36424         }
36425         
36426         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36427         
36428         var keyPress = function(e){
36429             
36430             var k = e.getKey();
36431             
36432             var c = e.getCharCode();
36433             
36434             if(
36435                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36436                     allowed.indexOf(String.fromCharCode(c)) === -1
36437             ){
36438                 e.stopEvent();
36439                 return;
36440             }
36441             
36442             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36443                 return;
36444             }
36445             
36446             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36447                 e.stopEvent();
36448             }
36449         };
36450         
36451         this.el.on("keypress", keyPress, this);
36452     },
36453     
36454     validateValue : function(value)
36455     {
36456         
36457         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36458             return false;
36459         }
36460         
36461         var num = this.parseValue(value);
36462         
36463         if(isNaN(num)){
36464             this.markInvalid(String.format(this.nanText, value));
36465             return false;
36466         }
36467         
36468         if(num < this.minValue){
36469             this.markInvalid(String.format(this.minText, this.minValue));
36470             return false;
36471         }
36472         
36473         if(num > this.maxValue){
36474             this.markInvalid(String.format(this.maxText, this.maxValue));
36475             return false;
36476         }
36477         
36478         return true;
36479     },
36480
36481     getValue : function()
36482     {
36483         var v = this.hiddenEl().getValue();
36484         
36485         return this.fixPrecision(this.parseValue(v));
36486     },
36487
36488     parseValue : function(value)
36489     {
36490         if(this.thousandsDelimiter) {
36491             value += "";
36492             r = new RegExp(",", "g");
36493             value = value.replace(r, "");
36494         }
36495         
36496         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36497         return isNaN(value) ? '' : value;
36498     },
36499
36500     fixPrecision : function(value)
36501     {
36502         if(this.thousandsDelimiter) {
36503             value += "";
36504             r = new RegExp(",", "g");
36505             value = value.replace(r, "");
36506         }
36507         
36508         var nan = isNaN(value);
36509         
36510         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36511             return nan ? '' : value;
36512         }
36513         return parseFloat(value).toFixed(this.decimalPrecision);
36514     },
36515
36516     setValue : function(v)
36517     {
36518         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36519         
36520         this.value = v;
36521         
36522         if(this.rendered){
36523             
36524             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36525             
36526             this.inputEl().dom.value = (v == '') ? '' :
36527                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36528             
36529             if(!this.allowZero && v === '0') {
36530                 this.hiddenEl().dom.value = '';
36531                 this.inputEl().dom.value = '';
36532             }
36533             
36534             this.validate();
36535         }
36536     },
36537
36538     decimalPrecisionFcn : function(v)
36539     {
36540         return Math.floor(v);
36541     },
36542
36543     beforeBlur : function()
36544     {
36545         var v = this.parseValue(this.getRawValue());
36546         
36547         if(v || v === 0 || v === ''){
36548             this.setValue(v);
36549         }
36550     },
36551     
36552     hiddenEl : function()
36553     {
36554         return this.el.select('input.hidden-number-input',true).first();
36555     }
36556     
36557 });
36558
36559  
36560
36561 /*
36562 * Licence: LGPL
36563 */
36564
36565 /**
36566  * @class Roo.bootstrap.DocumentSlider
36567  * @extends Roo.bootstrap.Component
36568  * Bootstrap DocumentSlider class
36569  * 
36570  * @constructor
36571  * Create a new DocumentViewer
36572  * @param {Object} config The config object
36573  */
36574
36575 Roo.bootstrap.DocumentSlider = function(config){
36576     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36577     
36578     this.files = [];
36579     
36580     this.addEvents({
36581         /**
36582          * @event initial
36583          * Fire after initEvent
36584          * @param {Roo.bootstrap.DocumentSlider} this
36585          */
36586         "initial" : true,
36587         /**
36588          * @event update
36589          * Fire after update
36590          * @param {Roo.bootstrap.DocumentSlider} this
36591          */
36592         "update" : true,
36593         /**
36594          * @event click
36595          * Fire after click
36596          * @param {Roo.bootstrap.DocumentSlider} this
36597          */
36598         "click" : true
36599     });
36600 };
36601
36602 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36603     
36604     files : false,
36605     
36606     indicator : 0,
36607     
36608     getAutoCreate : function()
36609     {
36610         var cfg = {
36611             tag : 'div',
36612             cls : 'roo-document-slider',
36613             cn : [
36614                 {
36615                     tag : 'div',
36616                     cls : 'roo-document-slider-header',
36617                     cn : [
36618                         {
36619                             tag : 'div',
36620                             cls : 'roo-document-slider-header-title'
36621                         }
36622                     ]
36623                 },
36624                 {
36625                     tag : 'div',
36626                     cls : 'roo-document-slider-body',
36627                     cn : [
36628                         {
36629                             tag : 'div',
36630                             cls : 'roo-document-slider-prev',
36631                             cn : [
36632                                 {
36633                                     tag : 'i',
36634                                     cls : 'fa fa-chevron-left'
36635                                 }
36636                             ]
36637                         },
36638                         {
36639                             tag : 'div',
36640                             cls : 'roo-document-slider-thumb',
36641                             cn : [
36642                                 {
36643                                     tag : 'img',
36644                                     cls : 'roo-document-slider-image'
36645                                 }
36646                             ]
36647                         },
36648                         {
36649                             tag : 'div',
36650                             cls : 'roo-document-slider-next',
36651                             cn : [
36652                                 {
36653                                     tag : 'i',
36654                                     cls : 'fa fa-chevron-right'
36655                                 }
36656                             ]
36657                         }
36658                     ]
36659                 }
36660             ]
36661         };
36662         
36663         return cfg;
36664     },
36665     
36666     initEvents : function()
36667     {
36668         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36669         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36670         
36671         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36672         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36673         
36674         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36675         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36676         
36677         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36678         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36679         
36680         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36681         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36682         
36683         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36684         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36685         
36686         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36687         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36688         
36689         this.thumbEl.on('click', this.onClick, this);
36690         
36691         this.prevIndicator.on('click', this.prev, this);
36692         
36693         this.nextIndicator.on('click', this.next, this);
36694         
36695     },
36696     
36697     initial : function()
36698     {
36699         if(this.files.length){
36700             this.indicator = 1;
36701             this.update()
36702         }
36703         
36704         this.fireEvent('initial', this);
36705     },
36706     
36707     update : function()
36708     {
36709         this.imageEl.attr('src', this.files[this.indicator - 1]);
36710         
36711         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36712         
36713         this.prevIndicator.show();
36714         
36715         if(this.indicator == 1){
36716             this.prevIndicator.hide();
36717         }
36718         
36719         this.nextIndicator.show();
36720         
36721         if(this.indicator == this.files.length){
36722             this.nextIndicator.hide();
36723         }
36724         
36725         this.thumbEl.scrollTo('top');
36726         
36727         this.fireEvent('update', this);
36728     },
36729     
36730     onClick : function(e)
36731     {
36732         e.preventDefault();
36733         
36734         this.fireEvent('click', this);
36735     },
36736     
36737     prev : function(e)
36738     {
36739         e.preventDefault();
36740         
36741         this.indicator = Math.max(1, this.indicator - 1);
36742         
36743         this.update();
36744     },
36745     
36746     next : function(e)
36747     {
36748         e.preventDefault();
36749         
36750         this.indicator = Math.min(this.files.length, this.indicator + 1);
36751         
36752         this.update();
36753     }
36754 });
36755 /*
36756  * - LGPL
36757  *
36758  * RadioSet
36759  *
36760  *
36761  */
36762
36763 /**
36764  * @class Roo.bootstrap.RadioSet
36765  * @extends Roo.bootstrap.Input
36766  * Bootstrap RadioSet class
36767  * @cfg {String} indicatorpos (left|right) default left
36768  * @cfg {Boolean} inline (true|false) inline the element (default true)
36769  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36770  * @constructor
36771  * Create a new RadioSet
36772  * @param {Object} config The config object
36773  */
36774
36775 Roo.bootstrap.RadioSet = function(config){
36776     
36777     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36778     
36779     this.radioes = [];
36780     
36781     Roo.bootstrap.RadioSet.register(this);
36782     
36783     this.addEvents({
36784         /**
36785         * @event check
36786         * Fires when the element is checked or unchecked.
36787         * @param {Roo.bootstrap.RadioSet} this This radio
36788         * @param {Roo.bootstrap.Radio} item The checked item
36789         */
36790        check : true,
36791        /**
36792         * @event click
36793         * Fires when the element is click.
36794         * @param {Roo.bootstrap.RadioSet} this This radio set
36795         * @param {Roo.bootstrap.Radio} item The checked item
36796         * @param {Roo.EventObject} e The event object
36797         */
36798        click : true
36799     });
36800     
36801 };
36802
36803 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36804
36805     radioes : false,
36806     
36807     inline : true,
36808     
36809     weight : '',
36810     
36811     indicatorpos : 'left',
36812     
36813     getAutoCreate : function()
36814     {
36815         var label = {
36816             tag : 'label',
36817             cls : 'roo-radio-set-label',
36818             cn : [
36819                 {
36820                     tag : 'span',
36821                     html : this.fieldLabel
36822                 }
36823             ]
36824         };
36825         if (Roo.bootstrap.version == 3) {
36826             
36827             
36828             if(this.indicatorpos == 'left'){
36829                 label.cn.unshift({
36830                     tag : 'i',
36831                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36832                     tooltip : 'This field is required'
36833                 });
36834             } else {
36835                 label.cn.push({
36836                     tag : 'i',
36837                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36838                     tooltip : 'This field is required'
36839                 });
36840             }
36841         }
36842         var items = {
36843             tag : 'div',
36844             cls : 'roo-radio-set-items'
36845         };
36846         
36847         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36848         
36849         if (align === 'left' && this.fieldLabel.length) {
36850             
36851             items = {
36852                 cls : "roo-radio-set-right", 
36853                 cn: [
36854                     items
36855                 ]
36856             };
36857             
36858             if(this.labelWidth > 12){
36859                 label.style = "width: " + this.labelWidth + 'px';
36860             }
36861             
36862             if(this.labelWidth < 13 && this.labelmd == 0){
36863                 this.labelmd = this.labelWidth;
36864             }
36865             
36866             if(this.labellg > 0){
36867                 label.cls += ' col-lg-' + this.labellg;
36868                 items.cls += ' col-lg-' + (12 - this.labellg);
36869             }
36870             
36871             if(this.labelmd > 0){
36872                 label.cls += ' col-md-' + this.labelmd;
36873                 items.cls += ' col-md-' + (12 - this.labelmd);
36874             }
36875             
36876             if(this.labelsm > 0){
36877                 label.cls += ' col-sm-' + this.labelsm;
36878                 items.cls += ' col-sm-' + (12 - this.labelsm);
36879             }
36880             
36881             if(this.labelxs > 0){
36882                 label.cls += ' col-xs-' + this.labelxs;
36883                 items.cls += ' col-xs-' + (12 - this.labelxs);
36884             }
36885         }
36886         
36887         var cfg = {
36888             tag : 'div',
36889             cls : 'roo-radio-set',
36890             cn : [
36891                 {
36892                     tag : 'input',
36893                     cls : 'roo-radio-set-input',
36894                     type : 'hidden',
36895                     name : this.name,
36896                     value : this.value ? this.value :  ''
36897                 },
36898                 label,
36899                 items
36900             ]
36901         };
36902         
36903         if(this.weight.length){
36904             cfg.cls += ' roo-radio-' + this.weight;
36905         }
36906         
36907         if(this.inline) {
36908             cfg.cls += ' roo-radio-set-inline';
36909         }
36910         
36911         var settings=this;
36912         ['xs','sm','md','lg'].map(function(size){
36913             if (settings[size]) {
36914                 cfg.cls += ' col-' + size + '-' + settings[size];
36915             }
36916         });
36917         
36918         return cfg;
36919         
36920     },
36921
36922     initEvents : function()
36923     {
36924         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36925         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36926         
36927         if(!this.fieldLabel.length){
36928             this.labelEl.hide();
36929         }
36930         
36931         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36932         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36933         
36934         this.indicator = this.indicatorEl();
36935         
36936         if(this.indicator){
36937             this.indicator.addClass('invisible');
36938         }
36939         
36940         this.originalValue = this.getValue();
36941         
36942     },
36943     
36944     inputEl: function ()
36945     {
36946         return this.el.select('.roo-radio-set-input', true).first();
36947     },
36948     
36949     getChildContainer : function()
36950     {
36951         return this.itemsEl;
36952     },
36953     
36954     register : function(item)
36955     {
36956         this.radioes.push(item);
36957         
36958     },
36959     
36960     validate : function()
36961     {   
36962         if(this.getVisibilityEl().hasClass('hidden')){
36963             return true;
36964         }
36965         
36966         var valid = false;
36967         
36968         Roo.each(this.radioes, function(i){
36969             if(!i.checked){
36970                 return;
36971             }
36972             
36973             valid = true;
36974             return false;
36975         });
36976         
36977         if(this.allowBlank) {
36978             return true;
36979         }
36980         
36981         if(this.disabled || valid){
36982             this.markValid();
36983             return true;
36984         }
36985         
36986         this.markInvalid();
36987         return false;
36988         
36989     },
36990     
36991     markValid : function()
36992     {
36993         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36994             this.indicatorEl().removeClass('visible');
36995             this.indicatorEl().addClass('invisible');
36996         }
36997         
36998         
36999         if (Roo.bootstrap.version == 3) {
37000             this.el.removeClass([this.invalidClass, this.validClass]);
37001             this.el.addClass(this.validClass);
37002         } else {
37003             this.el.removeClass(['is-invalid','is-valid']);
37004             this.el.addClass(['is-valid']);
37005         }
37006         this.fireEvent('valid', this);
37007     },
37008     
37009     markInvalid : function(msg)
37010     {
37011         if(this.allowBlank || this.disabled){
37012             return;
37013         }
37014         
37015         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37016             this.indicatorEl().removeClass('invisible');
37017             this.indicatorEl().addClass('visible');
37018         }
37019         if (Roo.bootstrap.version == 3) {
37020             this.el.removeClass([this.invalidClass, this.validClass]);
37021             this.el.addClass(this.invalidClass);
37022         } else {
37023             this.el.removeClass(['is-invalid','is-valid']);
37024             this.el.addClass(['is-invalid']);
37025         }
37026         
37027         this.fireEvent('invalid', this, msg);
37028         
37029     },
37030     
37031     setValue : function(v, suppressEvent)
37032     {   
37033         if(this.value === v){
37034             return;
37035         }
37036         
37037         this.value = v;
37038         
37039         if(this.rendered){
37040             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37041         }
37042         
37043         Roo.each(this.radioes, function(i){
37044             i.checked = false;
37045             i.el.removeClass('checked');
37046         });
37047         
37048         Roo.each(this.radioes, function(i){
37049             
37050             if(i.value === v || i.value.toString() === v.toString()){
37051                 i.checked = true;
37052                 i.el.addClass('checked');
37053                 
37054                 if(suppressEvent !== true){
37055                     this.fireEvent('check', this, i);
37056                 }
37057                 
37058                 return false;
37059             }
37060             
37061         }, this);
37062         
37063         this.validate();
37064     },
37065     
37066     clearInvalid : function(){
37067         
37068         if(!this.el || this.preventMark){
37069             return;
37070         }
37071         
37072         this.el.removeClass([this.invalidClass]);
37073         
37074         this.fireEvent('valid', this);
37075     }
37076     
37077 });
37078
37079 Roo.apply(Roo.bootstrap.RadioSet, {
37080     
37081     groups: {},
37082     
37083     register : function(set)
37084     {
37085         this.groups[set.name] = set;
37086     },
37087     
37088     get: function(name) 
37089     {
37090         if (typeof(this.groups[name]) == 'undefined') {
37091             return false;
37092         }
37093         
37094         return this.groups[name] ;
37095     }
37096     
37097 });
37098 /*
37099  * Based on:
37100  * Ext JS Library 1.1.1
37101  * Copyright(c) 2006-2007, Ext JS, LLC.
37102  *
37103  * Originally Released Under LGPL - original licence link has changed is not relivant.
37104  *
37105  * Fork - LGPL
37106  * <script type="text/javascript">
37107  */
37108
37109
37110 /**
37111  * @class Roo.bootstrap.SplitBar
37112  * @extends Roo.util.Observable
37113  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37114  * <br><br>
37115  * Usage:
37116  * <pre><code>
37117 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37118                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37119 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37120 split.minSize = 100;
37121 split.maxSize = 600;
37122 split.animate = true;
37123 split.on('moved', splitterMoved);
37124 </code></pre>
37125  * @constructor
37126  * Create a new SplitBar
37127  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37128  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37129  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37130  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37131                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37132                         position of the SplitBar).
37133  */
37134 Roo.bootstrap.SplitBar = function(cfg){
37135     
37136     /** @private */
37137     
37138     //{
37139     //  dragElement : elm
37140     //  resizingElement: el,
37141         // optional..
37142     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37143     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37144         // existingProxy ???
37145     //}
37146     
37147     this.el = Roo.get(cfg.dragElement, true);
37148     this.el.dom.unselectable = "on";
37149     /** @private */
37150     this.resizingEl = Roo.get(cfg.resizingElement, true);
37151
37152     /**
37153      * @private
37154      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37155      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37156      * @type Number
37157      */
37158     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37159     
37160     /**
37161      * The minimum size of the resizing element. (Defaults to 0)
37162      * @type Number
37163      */
37164     this.minSize = 0;
37165     
37166     /**
37167      * The maximum size of the resizing element. (Defaults to 2000)
37168      * @type Number
37169      */
37170     this.maxSize = 2000;
37171     
37172     /**
37173      * Whether to animate the transition to the new size
37174      * @type Boolean
37175      */
37176     this.animate = false;
37177     
37178     /**
37179      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37180      * @type Boolean
37181      */
37182     this.useShim = false;
37183     
37184     /** @private */
37185     this.shim = null;
37186     
37187     if(!cfg.existingProxy){
37188         /** @private */
37189         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37190     }else{
37191         this.proxy = Roo.get(cfg.existingProxy).dom;
37192     }
37193     /** @private */
37194     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37195     
37196     /** @private */
37197     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37198     
37199     /** @private */
37200     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37201     
37202     /** @private */
37203     this.dragSpecs = {};
37204     
37205     /**
37206      * @private The adapter to use to positon and resize elements
37207      */
37208     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37209     this.adapter.init(this);
37210     
37211     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37212         /** @private */
37213         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37214         this.el.addClass("roo-splitbar-h");
37215     }else{
37216         /** @private */
37217         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37218         this.el.addClass("roo-splitbar-v");
37219     }
37220     
37221     this.addEvents({
37222         /**
37223          * @event resize
37224          * Fires when the splitter is moved (alias for {@link #event-moved})
37225          * @param {Roo.bootstrap.SplitBar} this
37226          * @param {Number} newSize the new width or height
37227          */
37228         "resize" : true,
37229         /**
37230          * @event moved
37231          * Fires when the splitter is moved
37232          * @param {Roo.bootstrap.SplitBar} this
37233          * @param {Number} newSize the new width or height
37234          */
37235         "moved" : true,
37236         /**
37237          * @event beforeresize
37238          * Fires before the splitter is dragged
37239          * @param {Roo.bootstrap.SplitBar} this
37240          */
37241         "beforeresize" : true,
37242
37243         "beforeapply" : true
37244     });
37245
37246     Roo.util.Observable.call(this);
37247 };
37248
37249 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37250     onStartProxyDrag : function(x, y){
37251         this.fireEvent("beforeresize", this);
37252         if(!this.overlay){
37253             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37254             o.unselectable();
37255             o.enableDisplayMode("block");
37256             // all splitbars share the same overlay
37257             Roo.bootstrap.SplitBar.prototype.overlay = o;
37258         }
37259         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37260         this.overlay.show();
37261         Roo.get(this.proxy).setDisplayed("block");
37262         var size = this.adapter.getElementSize(this);
37263         this.activeMinSize = this.getMinimumSize();;
37264         this.activeMaxSize = this.getMaximumSize();;
37265         var c1 = size - this.activeMinSize;
37266         var c2 = Math.max(this.activeMaxSize - size, 0);
37267         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37268             this.dd.resetConstraints();
37269             this.dd.setXConstraint(
37270                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37271                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37272             );
37273             this.dd.setYConstraint(0, 0);
37274         }else{
37275             this.dd.resetConstraints();
37276             this.dd.setXConstraint(0, 0);
37277             this.dd.setYConstraint(
37278                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37279                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37280             );
37281          }
37282         this.dragSpecs.startSize = size;
37283         this.dragSpecs.startPoint = [x, y];
37284         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37285     },
37286     
37287     /** 
37288      * @private Called after the drag operation by the DDProxy
37289      */
37290     onEndProxyDrag : function(e){
37291         Roo.get(this.proxy).setDisplayed(false);
37292         var endPoint = Roo.lib.Event.getXY(e);
37293         if(this.overlay){
37294             this.overlay.hide();
37295         }
37296         var newSize;
37297         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37298             newSize = this.dragSpecs.startSize + 
37299                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37300                     endPoint[0] - this.dragSpecs.startPoint[0] :
37301                     this.dragSpecs.startPoint[0] - endPoint[0]
37302                 );
37303         }else{
37304             newSize = this.dragSpecs.startSize + 
37305                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37306                     endPoint[1] - this.dragSpecs.startPoint[1] :
37307                     this.dragSpecs.startPoint[1] - endPoint[1]
37308                 );
37309         }
37310         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37311         if(newSize != this.dragSpecs.startSize){
37312             if(this.fireEvent('beforeapply', this, newSize) !== false){
37313                 this.adapter.setElementSize(this, newSize);
37314                 this.fireEvent("moved", this, newSize);
37315                 this.fireEvent("resize", this, newSize);
37316             }
37317         }
37318     },
37319     
37320     /**
37321      * Get the adapter this SplitBar uses
37322      * @return The adapter object
37323      */
37324     getAdapter : function(){
37325         return this.adapter;
37326     },
37327     
37328     /**
37329      * Set the adapter this SplitBar uses
37330      * @param {Object} adapter A SplitBar adapter object
37331      */
37332     setAdapter : function(adapter){
37333         this.adapter = adapter;
37334         this.adapter.init(this);
37335     },
37336     
37337     /**
37338      * Gets the minimum size for the resizing element
37339      * @return {Number} The minimum size
37340      */
37341     getMinimumSize : function(){
37342         return this.minSize;
37343     },
37344     
37345     /**
37346      * Sets the minimum size for the resizing element
37347      * @param {Number} minSize The minimum size
37348      */
37349     setMinimumSize : function(minSize){
37350         this.minSize = minSize;
37351     },
37352     
37353     /**
37354      * Gets the maximum size for the resizing element
37355      * @return {Number} The maximum size
37356      */
37357     getMaximumSize : function(){
37358         return this.maxSize;
37359     },
37360     
37361     /**
37362      * Sets the maximum size for the resizing element
37363      * @param {Number} maxSize The maximum size
37364      */
37365     setMaximumSize : function(maxSize){
37366         this.maxSize = maxSize;
37367     },
37368     
37369     /**
37370      * Sets the initialize size for the resizing element
37371      * @param {Number} size The initial size
37372      */
37373     setCurrentSize : function(size){
37374         var oldAnimate = this.animate;
37375         this.animate = false;
37376         this.adapter.setElementSize(this, size);
37377         this.animate = oldAnimate;
37378     },
37379     
37380     /**
37381      * Destroy this splitbar. 
37382      * @param {Boolean} removeEl True to remove the element
37383      */
37384     destroy : function(removeEl){
37385         if(this.shim){
37386             this.shim.remove();
37387         }
37388         this.dd.unreg();
37389         this.proxy.parentNode.removeChild(this.proxy);
37390         if(removeEl){
37391             this.el.remove();
37392         }
37393     }
37394 });
37395
37396 /**
37397  * @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.
37398  */
37399 Roo.bootstrap.SplitBar.createProxy = function(dir){
37400     var proxy = new Roo.Element(document.createElement("div"));
37401     proxy.unselectable();
37402     var cls = 'roo-splitbar-proxy';
37403     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37404     document.body.appendChild(proxy.dom);
37405     return proxy.dom;
37406 };
37407
37408 /** 
37409  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37410  * Default Adapter. It assumes the splitter and resizing element are not positioned
37411  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37412  */
37413 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37414 };
37415
37416 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37417     // do nothing for now
37418     init : function(s){
37419     
37420     },
37421     /**
37422      * Called before drag operations to get the current size of the resizing element. 
37423      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37424      */
37425      getElementSize : function(s){
37426         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37427             return s.resizingEl.getWidth();
37428         }else{
37429             return s.resizingEl.getHeight();
37430         }
37431     },
37432     
37433     /**
37434      * Called after drag operations to set the size of the resizing element.
37435      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37436      * @param {Number} newSize The new size to set
37437      * @param {Function} onComplete A function to be invoked when resizing is complete
37438      */
37439     setElementSize : function(s, newSize, onComplete){
37440         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37441             if(!s.animate){
37442                 s.resizingEl.setWidth(newSize);
37443                 if(onComplete){
37444                     onComplete(s, newSize);
37445                 }
37446             }else{
37447                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37448             }
37449         }else{
37450             
37451             if(!s.animate){
37452                 s.resizingEl.setHeight(newSize);
37453                 if(onComplete){
37454                     onComplete(s, newSize);
37455                 }
37456             }else{
37457                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37458             }
37459         }
37460     }
37461 };
37462
37463 /** 
37464  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37465  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37466  * Adapter that  moves the splitter element to align with the resized sizing element. 
37467  * Used with an absolute positioned SplitBar.
37468  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37469  * document.body, make sure you assign an id to the body element.
37470  */
37471 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37472     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37473     this.container = Roo.get(container);
37474 };
37475
37476 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37477     init : function(s){
37478         this.basic.init(s);
37479     },
37480     
37481     getElementSize : function(s){
37482         return this.basic.getElementSize(s);
37483     },
37484     
37485     setElementSize : function(s, newSize, onComplete){
37486         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37487     },
37488     
37489     moveSplitter : function(s){
37490         var yes = Roo.bootstrap.SplitBar;
37491         switch(s.placement){
37492             case yes.LEFT:
37493                 s.el.setX(s.resizingEl.getRight());
37494                 break;
37495             case yes.RIGHT:
37496                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37497                 break;
37498             case yes.TOP:
37499                 s.el.setY(s.resizingEl.getBottom());
37500                 break;
37501             case yes.BOTTOM:
37502                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37503                 break;
37504         }
37505     }
37506 };
37507
37508 /**
37509  * Orientation constant - Create a vertical SplitBar
37510  * @static
37511  * @type Number
37512  */
37513 Roo.bootstrap.SplitBar.VERTICAL = 1;
37514
37515 /**
37516  * Orientation constant - Create a horizontal SplitBar
37517  * @static
37518  * @type Number
37519  */
37520 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37521
37522 /**
37523  * Placement constant - The resizing element is to the left of the splitter element
37524  * @static
37525  * @type Number
37526  */
37527 Roo.bootstrap.SplitBar.LEFT = 1;
37528
37529 /**
37530  * Placement constant - The resizing element is to the right of the splitter element
37531  * @static
37532  * @type Number
37533  */
37534 Roo.bootstrap.SplitBar.RIGHT = 2;
37535
37536 /**
37537  * Placement constant - The resizing element is positioned above the splitter element
37538  * @static
37539  * @type Number
37540  */
37541 Roo.bootstrap.SplitBar.TOP = 3;
37542
37543 /**
37544  * Placement constant - The resizing element is positioned under splitter element
37545  * @static
37546  * @type Number
37547  */
37548 Roo.bootstrap.SplitBar.BOTTOM = 4;
37549 Roo.namespace("Roo.bootstrap.layout");/*
37550  * Based on:
37551  * Ext JS Library 1.1.1
37552  * Copyright(c) 2006-2007, Ext JS, LLC.
37553  *
37554  * Originally Released Under LGPL - original licence link has changed is not relivant.
37555  *
37556  * Fork - LGPL
37557  * <script type="text/javascript">
37558  */
37559
37560 /**
37561  * @class Roo.bootstrap.layout.Manager
37562  * @extends Roo.bootstrap.Component
37563  * Base class for layout managers.
37564  */
37565 Roo.bootstrap.layout.Manager = function(config)
37566 {
37567     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37568
37569
37570
37571
37572
37573     /** false to disable window resize monitoring @type Boolean */
37574     this.monitorWindowResize = true;
37575     this.regions = {};
37576     this.addEvents({
37577         /**
37578          * @event layout
37579          * Fires when a layout is performed.
37580          * @param {Roo.LayoutManager} this
37581          */
37582         "layout" : true,
37583         /**
37584          * @event regionresized
37585          * Fires when the user resizes a region.
37586          * @param {Roo.LayoutRegion} region The resized region
37587          * @param {Number} newSize The new size (width for east/west, height for north/south)
37588          */
37589         "regionresized" : true,
37590         /**
37591          * @event regioncollapsed
37592          * Fires when a region is collapsed.
37593          * @param {Roo.LayoutRegion} region The collapsed region
37594          */
37595         "regioncollapsed" : true,
37596         /**
37597          * @event regionexpanded
37598          * Fires when a region is expanded.
37599          * @param {Roo.LayoutRegion} region The expanded region
37600          */
37601         "regionexpanded" : true
37602     });
37603     this.updating = false;
37604
37605     if (config.el) {
37606         this.el = Roo.get(config.el);
37607         this.initEvents();
37608     }
37609
37610 };
37611
37612 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37613
37614
37615     regions : null,
37616
37617     monitorWindowResize : true,
37618
37619
37620     updating : false,
37621
37622
37623     onRender : function(ct, position)
37624     {
37625         if(!this.el){
37626             this.el = Roo.get(ct);
37627             this.initEvents();
37628         }
37629         //this.fireEvent('render',this);
37630     },
37631
37632
37633     initEvents: function()
37634     {
37635
37636
37637         // ie scrollbar fix
37638         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37639             document.body.scroll = "no";
37640         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37641             this.el.position('relative');
37642         }
37643         this.id = this.el.id;
37644         this.el.addClass("roo-layout-container");
37645         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37646         if(this.el.dom != document.body ) {
37647             this.el.on('resize', this.layout,this);
37648             this.el.on('show', this.layout,this);
37649         }
37650
37651     },
37652
37653     /**
37654      * Returns true if this layout is currently being updated
37655      * @return {Boolean}
37656      */
37657     isUpdating : function(){
37658         return this.updating;
37659     },
37660
37661     /**
37662      * Suspend the LayoutManager from doing auto-layouts while
37663      * making multiple add or remove calls
37664      */
37665     beginUpdate : function(){
37666         this.updating = true;
37667     },
37668
37669     /**
37670      * Restore auto-layouts and optionally disable the manager from performing a layout
37671      * @param {Boolean} noLayout true to disable a layout update
37672      */
37673     endUpdate : function(noLayout){
37674         this.updating = false;
37675         if(!noLayout){
37676             this.layout();
37677         }
37678     },
37679
37680     layout: function(){
37681         // abstract...
37682     },
37683
37684     onRegionResized : function(region, newSize){
37685         this.fireEvent("regionresized", region, newSize);
37686         this.layout();
37687     },
37688
37689     onRegionCollapsed : function(region){
37690         this.fireEvent("regioncollapsed", region);
37691     },
37692
37693     onRegionExpanded : function(region){
37694         this.fireEvent("regionexpanded", region);
37695     },
37696
37697     /**
37698      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37699      * performs box-model adjustments.
37700      * @return {Object} The size as an object {width: (the width), height: (the height)}
37701      */
37702     getViewSize : function()
37703     {
37704         var size;
37705         if(this.el.dom != document.body){
37706             size = this.el.getSize();
37707         }else{
37708             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37709         }
37710         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37711         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37712         return size;
37713     },
37714
37715     /**
37716      * Returns the Element this layout is bound to.
37717      * @return {Roo.Element}
37718      */
37719     getEl : function(){
37720         return this.el;
37721     },
37722
37723     /**
37724      * Returns the specified region.
37725      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37726      * @return {Roo.LayoutRegion}
37727      */
37728     getRegion : function(target){
37729         return this.regions[target.toLowerCase()];
37730     },
37731
37732     onWindowResize : function(){
37733         if(this.monitorWindowResize){
37734             this.layout();
37735         }
37736     }
37737 });
37738 /*
37739  * Based on:
37740  * Ext JS Library 1.1.1
37741  * Copyright(c) 2006-2007, Ext JS, LLC.
37742  *
37743  * Originally Released Under LGPL - original licence link has changed is not relivant.
37744  *
37745  * Fork - LGPL
37746  * <script type="text/javascript">
37747  */
37748 /**
37749  * @class Roo.bootstrap.layout.Border
37750  * @extends Roo.bootstrap.layout.Manager
37751  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37752  * please see: examples/bootstrap/nested.html<br><br>
37753  
37754 <b>The container the layout is rendered into can be either the body element or any other element.
37755 If it is not the body element, the container needs to either be an absolute positioned element,
37756 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37757 the container size if it is not the body element.</b>
37758
37759 * @constructor
37760 * Create a new Border
37761 * @param {Object} config Configuration options
37762  */
37763 Roo.bootstrap.layout.Border = function(config){
37764     config = config || {};
37765     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37766     
37767     
37768     
37769     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37770         if(config[region]){
37771             config[region].region = region;
37772             this.addRegion(config[region]);
37773         }
37774     },this);
37775     
37776 };
37777
37778 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37779
37780 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37781     
37782     parent : false, // this might point to a 'nest' or a ???
37783     
37784     /**
37785      * Creates and adds a new region if it doesn't already exist.
37786      * @param {String} target The target region key (north, south, east, west or center).
37787      * @param {Object} config The regions config object
37788      * @return {BorderLayoutRegion} The new region
37789      */
37790     addRegion : function(config)
37791     {
37792         if(!this.regions[config.region]){
37793             var r = this.factory(config);
37794             this.bindRegion(r);
37795         }
37796         return this.regions[config.region];
37797     },
37798
37799     // private (kinda)
37800     bindRegion : function(r){
37801         this.regions[r.config.region] = r;
37802         
37803         r.on("visibilitychange",    this.layout, this);
37804         r.on("paneladded",          this.layout, this);
37805         r.on("panelremoved",        this.layout, this);
37806         r.on("invalidated",         this.layout, this);
37807         r.on("resized",             this.onRegionResized, this);
37808         r.on("collapsed",           this.onRegionCollapsed, this);
37809         r.on("expanded",            this.onRegionExpanded, this);
37810     },
37811
37812     /**
37813      * Performs a layout update.
37814      */
37815     layout : function()
37816     {
37817         if(this.updating) {
37818             return;
37819         }
37820         
37821         // render all the rebions if they have not been done alreayd?
37822         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37823             if(this.regions[region] && !this.regions[region].bodyEl){
37824                 this.regions[region].onRender(this.el)
37825             }
37826         },this);
37827         
37828         var size = this.getViewSize();
37829         var w = size.width;
37830         var h = size.height;
37831         var centerW = w;
37832         var centerH = h;
37833         var centerY = 0;
37834         var centerX = 0;
37835         //var x = 0, y = 0;
37836
37837         var rs = this.regions;
37838         var north = rs["north"];
37839         var south = rs["south"]; 
37840         var west = rs["west"];
37841         var east = rs["east"];
37842         var center = rs["center"];
37843         //if(this.hideOnLayout){ // not supported anymore
37844             //c.el.setStyle("display", "none");
37845         //}
37846         if(north && north.isVisible()){
37847             var b = north.getBox();
37848             var m = north.getMargins();
37849             b.width = w - (m.left+m.right);
37850             b.x = m.left;
37851             b.y = m.top;
37852             centerY = b.height + b.y + m.bottom;
37853             centerH -= centerY;
37854             north.updateBox(this.safeBox(b));
37855         }
37856         if(south && south.isVisible()){
37857             var b = south.getBox();
37858             var m = south.getMargins();
37859             b.width = w - (m.left+m.right);
37860             b.x = m.left;
37861             var totalHeight = (b.height + m.top + m.bottom);
37862             b.y = h - totalHeight + m.top;
37863             centerH -= totalHeight;
37864             south.updateBox(this.safeBox(b));
37865         }
37866         if(west && west.isVisible()){
37867             var b = west.getBox();
37868             var m = west.getMargins();
37869             b.height = centerH - (m.top+m.bottom);
37870             b.x = m.left;
37871             b.y = centerY + m.top;
37872             var totalWidth = (b.width + m.left + m.right);
37873             centerX += totalWidth;
37874             centerW -= totalWidth;
37875             west.updateBox(this.safeBox(b));
37876         }
37877         if(east && east.isVisible()){
37878             var b = east.getBox();
37879             var m = east.getMargins();
37880             b.height = centerH - (m.top+m.bottom);
37881             var totalWidth = (b.width + m.left + m.right);
37882             b.x = w - totalWidth + m.left;
37883             b.y = centerY + m.top;
37884             centerW -= totalWidth;
37885             east.updateBox(this.safeBox(b));
37886         }
37887         if(center){
37888             var m = center.getMargins();
37889             var centerBox = {
37890                 x: centerX + m.left,
37891                 y: centerY + m.top,
37892                 width: centerW - (m.left+m.right),
37893                 height: centerH - (m.top+m.bottom)
37894             };
37895             //if(this.hideOnLayout){
37896                 //center.el.setStyle("display", "block");
37897             //}
37898             center.updateBox(this.safeBox(centerBox));
37899         }
37900         this.el.repaint();
37901         this.fireEvent("layout", this);
37902     },
37903
37904     // private
37905     safeBox : function(box){
37906         box.width = Math.max(0, box.width);
37907         box.height = Math.max(0, box.height);
37908         return box;
37909     },
37910
37911     /**
37912      * Adds a ContentPanel (or subclass) to this layout.
37913      * @param {String} target The target region key (north, south, east, west or center).
37914      * @param {Roo.ContentPanel} panel The panel to add
37915      * @return {Roo.ContentPanel} The added panel
37916      */
37917     add : function(target, panel){
37918          
37919         target = target.toLowerCase();
37920         return this.regions[target].add(panel);
37921     },
37922
37923     /**
37924      * Remove a ContentPanel (or subclass) to this layout.
37925      * @param {String} target The target region key (north, south, east, west or center).
37926      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37927      * @return {Roo.ContentPanel} The removed panel
37928      */
37929     remove : function(target, panel){
37930         target = target.toLowerCase();
37931         return this.regions[target].remove(panel);
37932     },
37933
37934     /**
37935      * Searches all regions for a panel with the specified id
37936      * @param {String} panelId
37937      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37938      */
37939     findPanel : function(panelId){
37940         var rs = this.regions;
37941         for(var target in rs){
37942             if(typeof rs[target] != "function"){
37943                 var p = rs[target].getPanel(panelId);
37944                 if(p){
37945                     return p;
37946                 }
37947             }
37948         }
37949         return null;
37950     },
37951
37952     /**
37953      * Searches all regions for a panel with the specified id and activates (shows) it.
37954      * @param {String/ContentPanel} panelId The panels id or the panel itself
37955      * @return {Roo.ContentPanel} The shown panel or null
37956      */
37957     showPanel : function(panelId) {
37958       var rs = this.regions;
37959       for(var target in rs){
37960          var r = rs[target];
37961          if(typeof r != "function"){
37962             if(r.hasPanel(panelId)){
37963                return r.showPanel(panelId);
37964             }
37965          }
37966       }
37967       return null;
37968    },
37969
37970    /**
37971      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37972      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37973      */
37974    /*
37975     restoreState : function(provider){
37976         if(!provider){
37977             provider = Roo.state.Manager;
37978         }
37979         var sm = new Roo.LayoutStateManager();
37980         sm.init(this, provider);
37981     },
37982 */
37983  
37984  
37985     /**
37986      * Adds a xtype elements to the layout.
37987      * <pre><code>
37988
37989 layout.addxtype({
37990        xtype : 'ContentPanel',
37991        region: 'west',
37992        items: [ .... ]
37993    }
37994 );
37995
37996 layout.addxtype({
37997         xtype : 'NestedLayoutPanel',
37998         region: 'west',
37999         layout: {
38000            center: { },
38001            west: { }   
38002         },
38003         items : [ ... list of content panels or nested layout panels.. ]
38004    }
38005 );
38006 </code></pre>
38007      * @param {Object} cfg Xtype definition of item to add.
38008      */
38009     addxtype : function(cfg)
38010     {
38011         // basically accepts a pannel...
38012         // can accept a layout region..!?!?
38013         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38014         
38015         
38016         // theory?  children can only be panels??
38017         
38018         //if (!cfg.xtype.match(/Panel$/)) {
38019         //    return false;
38020         //}
38021         var ret = false;
38022         
38023         if (typeof(cfg.region) == 'undefined') {
38024             Roo.log("Failed to add Panel, region was not set");
38025             Roo.log(cfg);
38026             return false;
38027         }
38028         var region = cfg.region;
38029         delete cfg.region;
38030         
38031           
38032         var xitems = [];
38033         if (cfg.items) {
38034             xitems = cfg.items;
38035             delete cfg.items;
38036         }
38037         var nb = false;
38038         
38039         if ( region == 'center') {
38040             Roo.log("Center: " + cfg.title);
38041         }
38042         
38043         
38044         switch(cfg.xtype) 
38045         {
38046             case 'Content':  // ContentPanel (el, cfg)
38047             case 'Scroll':  // ContentPanel (el, cfg)
38048             case 'View': 
38049                 cfg.autoCreate = cfg.autoCreate || true;
38050                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38051                 //} else {
38052                 //    var el = this.el.createChild();
38053                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38054                 //}
38055                 
38056                 this.add(region, ret);
38057                 break;
38058             
38059             /*
38060             case 'TreePanel': // our new panel!
38061                 cfg.el = this.el.createChild();
38062                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38063                 this.add(region, ret);
38064                 break;
38065             */
38066             
38067             case 'Nest': 
38068                 // create a new Layout (which is  a Border Layout...
38069                 
38070                 var clayout = cfg.layout;
38071                 clayout.el  = this.el.createChild();
38072                 clayout.items   = clayout.items  || [];
38073                 
38074                 delete cfg.layout;
38075                 
38076                 // replace this exitems with the clayout ones..
38077                 xitems = clayout.items;
38078                  
38079                 // force background off if it's in center...
38080                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38081                     cfg.background = false;
38082                 }
38083                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38084                 
38085                 
38086                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38087                 //console.log('adding nested layout panel '  + cfg.toSource());
38088                 this.add(region, ret);
38089                 nb = {}; /// find first...
38090                 break;
38091             
38092             case 'Grid':
38093                 
38094                 // needs grid and region
38095                 
38096                 //var el = this.getRegion(region).el.createChild();
38097                 /*
38098                  *var el = this.el.createChild();
38099                 // create the grid first...
38100                 cfg.grid.container = el;
38101                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38102                 */
38103                 
38104                 if (region == 'center' && this.active ) {
38105                     cfg.background = false;
38106                 }
38107                 
38108                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38109                 
38110                 this.add(region, ret);
38111                 /*
38112                 if (cfg.background) {
38113                     // render grid on panel activation (if panel background)
38114                     ret.on('activate', function(gp) {
38115                         if (!gp.grid.rendered) {
38116                     //        gp.grid.render(el);
38117                         }
38118                     });
38119                 } else {
38120                   //  cfg.grid.render(el);
38121                 }
38122                 */
38123                 break;
38124            
38125            
38126             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38127                 // it was the old xcomponent building that caused this before.
38128                 // espeically if border is the top element in the tree.
38129                 ret = this;
38130                 break; 
38131                 
38132                     
38133                 
38134                 
38135                 
38136             default:
38137                 /*
38138                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38139                     
38140                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38141                     this.add(region, ret);
38142                 } else {
38143                 */
38144                     Roo.log(cfg);
38145                     throw "Can not add '" + cfg.xtype + "' to Border";
38146                     return null;
38147              
38148                                 
38149              
38150         }
38151         this.beginUpdate();
38152         // add children..
38153         var region = '';
38154         var abn = {};
38155         Roo.each(xitems, function(i)  {
38156             region = nb && i.region ? i.region : false;
38157             
38158             var add = ret.addxtype(i);
38159            
38160             if (region) {
38161                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38162                 if (!i.background) {
38163                     abn[region] = nb[region] ;
38164                 }
38165             }
38166             
38167         });
38168         this.endUpdate();
38169
38170         // make the last non-background panel active..
38171         //if (nb) { Roo.log(abn); }
38172         if (nb) {
38173             
38174             for(var r in abn) {
38175                 region = this.getRegion(r);
38176                 if (region) {
38177                     // tried using nb[r], but it does not work..
38178                      
38179                     region.showPanel(abn[r]);
38180                    
38181                 }
38182             }
38183         }
38184         return ret;
38185         
38186     },
38187     
38188     
38189 // private
38190     factory : function(cfg)
38191     {
38192         
38193         var validRegions = Roo.bootstrap.layout.Border.regions;
38194
38195         var target = cfg.region;
38196         cfg.mgr = this;
38197         
38198         var r = Roo.bootstrap.layout;
38199         Roo.log(target);
38200         switch(target){
38201             case "north":
38202                 return new r.North(cfg);
38203             case "south":
38204                 return new r.South(cfg);
38205             case "east":
38206                 return new r.East(cfg);
38207             case "west":
38208                 return new r.West(cfg);
38209             case "center":
38210                 return new r.Center(cfg);
38211         }
38212         throw 'Layout region "'+target+'" not supported.';
38213     }
38214     
38215     
38216 });
38217  /*
38218  * Based on:
38219  * Ext JS Library 1.1.1
38220  * Copyright(c) 2006-2007, Ext JS, LLC.
38221  *
38222  * Originally Released Under LGPL - original licence link has changed is not relivant.
38223  *
38224  * Fork - LGPL
38225  * <script type="text/javascript">
38226  */
38227  
38228 /**
38229  * @class Roo.bootstrap.layout.Basic
38230  * @extends Roo.util.Observable
38231  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38232  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38233  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38234  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38235  * @cfg {string}   region  the region that it inhabits..
38236  * @cfg {bool}   skipConfig skip config?
38237  * 
38238
38239  */
38240 Roo.bootstrap.layout.Basic = function(config){
38241     
38242     this.mgr = config.mgr;
38243     
38244     this.position = config.region;
38245     
38246     var skipConfig = config.skipConfig;
38247     
38248     this.events = {
38249         /**
38250          * @scope Roo.BasicLayoutRegion
38251          */
38252         
38253         /**
38254          * @event beforeremove
38255          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38256          * @param {Roo.LayoutRegion} this
38257          * @param {Roo.ContentPanel} panel The panel
38258          * @param {Object} e The cancel event object
38259          */
38260         "beforeremove" : true,
38261         /**
38262          * @event invalidated
38263          * Fires when the layout for this region is changed.
38264          * @param {Roo.LayoutRegion} this
38265          */
38266         "invalidated" : true,
38267         /**
38268          * @event visibilitychange
38269          * Fires when this region is shown or hidden 
38270          * @param {Roo.LayoutRegion} this
38271          * @param {Boolean} visibility true or false
38272          */
38273         "visibilitychange" : true,
38274         /**
38275          * @event paneladded
38276          * Fires when a panel is added. 
38277          * @param {Roo.LayoutRegion} this
38278          * @param {Roo.ContentPanel} panel The panel
38279          */
38280         "paneladded" : true,
38281         /**
38282          * @event panelremoved
38283          * Fires when a panel is removed. 
38284          * @param {Roo.LayoutRegion} this
38285          * @param {Roo.ContentPanel} panel The panel
38286          */
38287         "panelremoved" : true,
38288         /**
38289          * @event beforecollapse
38290          * Fires when this region before collapse.
38291          * @param {Roo.LayoutRegion} this
38292          */
38293         "beforecollapse" : true,
38294         /**
38295          * @event collapsed
38296          * Fires when this region is collapsed.
38297          * @param {Roo.LayoutRegion} this
38298          */
38299         "collapsed" : true,
38300         /**
38301          * @event expanded
38302          * Fires when this region is expanded.
38303          * @param {Roo.LayoutRegion} this
38304          */
38305         "expanded" : true,
38306         /**
38307          * @event slideshow
38308          * Fires when this region is slid into view.
38309          * @param {Roo.LayoutRegion} this
38310          */
38311         "slideshow" : true,
38312         /**
38313          * @event slidehide
38314          * Fires when this region slides out of view. 
38315          * @param {Roo.LayoutRegion} this
38316          */
38317         "slidehide" : true,
38318         /**
38319          * @event panelactivated
38320          * Fires when a panel is activated. 
38321          * @param {Roo.LayoutRegion} this
38322          * @param {Roo.ContentPanel} panel The activated panel
38323          */
38324         "panelactivated" : true,
38325         /**
38326          * @event resized
38327          * Fires when the user resizes this region. 
38328          * @param {Roo.LayoutRegion} this
38329          * @param {Number} newSize The new size (width for east/west, height for north/south)
38330          */
38331         "resized" : true
38332     };
38333     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38334     this.panels = new Roo.util.MixedCollection();
38335     this.panels.getKey = this.getPanelId.createDelegate(this);
38336     this.box = null;
38337     this.activePanel = null;
38338     // ensure listeners are added...
38339     
38340     if (config.listeners || config.events) {
38341         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38342             listeners : config.listeners || {},
38343             events : config.events || {}
38344         });
38345     }
38346     
38347     if(skipConfig !== true){
38348         this.applyConfig(config);
38349     }
38350 };
38351
38352 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38353 {
38354     getPanelId : function(p){
38355         return p.getId();
38356     },
38357     
38358     applyConfig : function(config){
38359         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38360         this.config = config;
38361         
38362     },
38363     
38364     /**
38365      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38366      * the width, for horizontal (north, south) the height.
38367      * @param {Number} newSize The new width or height
38368      */
38369     resizeTo : function(newSize){
38370         var el = this.el ? this.el :
38371                  (this.activePanel ? this.activePanel.getEl() : null);
38372         if(el){
38373             switch(this.position){
38374                 case "east":
38375                 case "west":
38376                     el.setWidth(newSize);
38377                     this.fireEvent("resized", this, newSize);
38378                 break;
38379                 case "north":
38380                 case "south":
38381                     el.setHeight(newSize);
38382                     this.fireEvent("resized", this, newSize);
38383                 break;                
38384             }
38385         }
38386     },
38387     
38388     getBox : function(){
38389         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38390     },
38391     
38392     getMargins : function(){
38393         return this.margins;
38394     },
38395     
38396     updateBox : function(box){
38397         this.box = box;
38398         var el = this.activePanel.getEl();
38399         el.dom.style.left = box.x + "px";
38400         el.dom.style.top = box.y + "px";
38401         this.activePanel.setSize(box.width, box.height);
38402     },
38403     
38404     /**
38405      * Returns the container element for this region.
38406      * @return {Roo.Element}
38407      */
38408     getEl : function(){
38409         return this.activePanel;
38410     },
38411     
38412     /**
38413      * Returns true if this region is currently visible.
38414      * @return {Boolean}
38415      */
38416     isVisible : function(){
38417         return this.activePanel ? true : false;
38418     },
38419     
38420     setActivePanel : function(panel){
38421         panel = this.getPanel(panel);
38422         if(this.activePanel && this.activePanel != panel){
38423             this.activePanel.setActiveState(false);
38424             this.activePanel.getEl().setLeftTop(-10000,-10000);
38425         }
38426         this.activePanel = panel;
38427         panel.setActiveState(true);
38428         if(this.box){
38429             panel.setSize(this.box.width, this.box.height);
38430         }
38431         this.fireEvent("panelactivated", this, panel);
38432         this.fireEvent("invalidated");
38433     },
38434     
38435     /**
38436      * Show the specified panel.
38437      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38438      * @return {Roo.ContentPanel} The shown panel or null
38439      */
38440     showPanel : function(panel){
38441         panel = this.getPanel(panel);
38442         if(panel){
38443             this.setActivePanel(panel);
38444         }
38445         return panel;
38446     },
38447     
38448     /**
38449      * Get the active panel for this region.
38450      * @return {Roo.ContentPanel} The active panel or null
38451      */
38452     getActivePanel : function(){
38453         return this.activePanel;
38454     },
38455     
38456     /**
38457      * Add the passed ContentPanel(s)
38458      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38459      * @return {Roo.ContentPanel} The panel added (if only one was added)
38460      */
38461     add : function(panel){
38462         if(arguments.length > 1){
38463             for(var i = 0, len = arguments.length; i < len; i++) {
38464                 this.add(arguments[i]);
38465             }
38466             return null;
38467         }
38468         if(this.hasPanel(panel)){
38469             this.showPanel(panel);
38470             return panel;
38471         }
38472         var el = panel.getEl();
38473         if(el.dom.parentNode != this.mgr.el.dom){
38474             this.mgr.el.dom.appendChild(el.dom);
38475         }
38476         if(panel.setRegion){
38477             panel.setRegion(this);
38478         }
38479         this.panels.add(panel);
38480         el.setStyle("position", "absolute");
38481         if(!panel.background){
38482             this.setActivePanel(panel);
38483             if(this.config.initialSize && this.panels.getCount()==1){
38484                 this.resizeTo(this.config.initialSize);
38485             }
38486         }
38487         this.fireEvent("paneladded", this, panel);
38488         return panel;
38489     },
38490     
38491     /**
38492      * Returns true if the panel is in this region.
38493      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38494      * @return {Boolean}
38495      */
38496     hasPanel : function(panel){
38497         if(typeof panel == "object"){ // must be panel obj
38498             panel = panel.getId();
38499         }
38500         return this.getPanel(panel) ? true : false;
38501     },
38502     
38503     /**
38504      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38505      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38506      * @param {Boolean} preservePanel Overrides the config preservePanel option
38507      * @return {Roo.ContentPanel} The panel that was removed
38508      */
38509     remove : function(panel, preservePanel){
38510         panel = this.getPanel(panel);
38511         if(!panel){
38512             return null;
38513         }
38514         var e = {};
38515         this.fireEvent("beforeremove", this, panel, e);
38516         if(e.cancel === true){
38517             return null;
38518         }
38519         var panelId = panel.getId();
38520         this.panels.removeKey(panelId);
38521         return panel;
38522     },
38523     
38524     /**
38525      * Returns the panel specified or null if it's not in this region.
38526      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38527      * @return {Roo.ContentPanel}
38528      */
38529     getPanel : function(id){
38530         if(typeof id == "object"){ // must be panel obj
38531             return id;
38532         }
38533         return this.panels.get(id);
38534     },
38535     
38536     /**
38537      * Returns this regions position (north/south/east/west/center).
38538      * @return {String} 
38539      */
38540     getPosition: function(){
38541         return this.position;    
38542     }
38543 });/*
38544  * Based on:
38545  * Ext JS Library 1.1.1
38546  * Copyright(c) 2006-2007, Ext JS, LLC.
38547  *
38548  * Originally Released Under LGPL - original licence link has changed is not relivant.
38549  *
38550  * Fork - LGPL
38551  * <script type="text/javascript">
38552  */
38553  
38554 /**
38555  * @class Roo.bootstrap.layout.Region
38556  * @extends Roo.bootstrap.layout.Basic
38557  * This class represents a region in a layout manager.
38558  
38559  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38560  * @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})
38561  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38562  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38563  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38564  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38565  * @cfg {String}    title           The title for the region (overrides panel titles)
38566  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38567  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38568  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38569  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38570  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38571  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38572  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38573  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38574  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38575  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38576
38577  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38578  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38579  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38580  * @cfg {Number}    width           For East/West panels
38581  * @cfg {Number}    height          For North/South panels
38582  * @cfg {Boolean}   split           To show the splitter
38583  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38584  * 
38585  * @cfg {string}   cls             Extra CSS classes to add to region
38586  * 
38587  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38588  * @cfg {string}   region  the region that it inhabits..
38589  *
38590
38591  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38592  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38593
38594  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38595  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38596  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38597  */
38598 Roo.bootstrap.layout.Region = function(config)
38599 {
38600     this.applyConfig(config);
38601
38602     var mgr = config.mgr;
38603     var pos = config.region;
38604     config.skipConfig = true;
38605     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38606     
38607     if (mgr.el) {
38608         this.onRender(mgr.el);   
38609     }
38610      
38611     this.visible = true;
38612     this.collapsed = false;
38613     this.unrendered_panels = [];
38614 };
38615
38616 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38617
38618     position: '', // set by wrapper (eg. north/south etc..)
38619     unrendered_panels : null,  // unrendered panels.
38620     
38621     tabPosition : false,
38622     
38623     mgr: false, // points to 'Border'
38624     
38625     
38626     createBody : function(){
38627         /** This region's body element 
38628         * @type Roo.Element */
38629         this.bodyEl = this.el.createChild({
38630                 tag: "div",
38631                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38632         });
38633     },
38634
38635     onRender: function(ctr, pos)
38636     {
38637         var dh = Roo.DomHelper;
38638         /** This region's container element 
38639         * @type Roo.Element */
38640         this.el = dh.append(ctr.dom, {
38641                 tag: "div",
38642                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38643             }, true);
38644         /** This region's title element 
38645         * @type Roo.Element */
38646     
38647         this.titleEl = dh.append(this.el.dom,  {
38648                 tag: "div",
38649                 unselectable: "on",
38650                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38651                 children:[
38652                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38653                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38654                 ]
38655             }, true);
38656         
38657         this.titleEl.enableDisplayMode();
38658         /** This region's title text element 
38659         * @type HTMLElement */
38660         this.titleTextEl = this.titleEl.dom.firstChild;
38661         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38662         /*
38663         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38664         this.closeBtn.enableDisplayMode();
38665         this.closeBtn.on("click", this.closeClicked, this);
38666         this.closeBtn.hide();
38667     */
38668         this.createBody(this.config);
38669         if(this.config.hideWhenEmpty){
38670             this.hide();
38671             this.on("paneladded", this.validateVisibility, this);
38672             this.on("panelremoved", this.validateVisibility, this);
38673         }
38674         if(this.autoScroll){
38675             this.bodyEl.setStyle("overflow", "auto");
38676         }else{
38677             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38678         }
38679         //if(c.titlebar !== false){
38680             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38681                 this.titleEl.hide();
38682             }else{
38683                 this.titleEl.show();
38684                 if(this.config.title){
38685                     this.titleTextEl.innerHTML = this.config.title;
38686                 }
38687             }
38688         //}
38689         if(this.config.collapsed){
38690             this.collapse(true);
38691         }
38692         if(this.config.hidden){
38693             this.hide();
38694         }
38695         
38696         if (this.unrendered_panels && this.unrendered_panels.length) {
38697             for (var i =0;i< this.unrendered_panels.length; i++) {
38698                 this.add(this.unrendered_panels[i]);
38699             }
38700             this.unrendered_panels = null;
38701             
38702         }
38703         
38704     },
38705     
38706     applyConfig : function(c)
38707     {
38708         /*
38709          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38710             var dh = Roo.DomHelper;
38711             if(c.titlebar !== false){
38712                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38713                 this.collapseBtn.on("click", this.collapse, this);
38714                 this.collapseBtn.enableDisplayMode();
38715                 /*
38716                 if(c.showPin === true || this.showPin){
38717                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38718                     this.stickBtn.enableDisplayMode();
38719                     this.stickBtn.on("click", this.expand, this);
38720                     this.stickBtn.hide();
38721                 }
38722                 
38723             }
38724             */
38725             /** This region's collapsed element
38726             * @type Roo.Element */
38727             /*
38728              *
38729             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38730                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38731             ]}, true);
38732             
38733             if(c.floatable !== false){
38734                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38735                this.collapsedEl.on("click", this.collapseClick, this);
38736             }
38737
38738             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38739                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38740                    id: "message", unselectable: "on", style:{"float":"left"}});
38741                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38742              }
38743             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38744             this.expandBtn.on("click", this.expand, this);
38745             
38746         }
38747         
38748         if(this.collapseBtn){
38749             this.collapseBtn.setVisible(c.collapsible == true);
38750         }
38751         
38752         this.cmargins = c.cmargins || this.cmargins ||
38753                          (this.position == "west" || this.position == "east" ?
38754                              {top: 0, left: 2, right:2, bottom: 0} :
38755                              {top: 2, left: 0, right:0, bottom: 2});
38756         */
38757         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38758         
38759         
38760         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38761         
38762         this.autoScroll = c.autoScroll || false;
38763         
38764         
38765        
38766         
38767         this.duration = c.duration || .30;
38768         this.slideDuration = c.slideDuration || .45;
38769         this.config = c;
38770        
38771     },
38772     /**
38773      * Returns true if this region is currently visible.
38774      * @return {Boolean}
38775      */
38776     isVisible : function(){
38777         return this.visible;
38778     },
38779
38780     /**
38781      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38782      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38783      */
38784     //setCollapsedTitle : function(title){
38785     //    title = title || "&#160;";
38786      //   if(this.collapsedTitleTextEl){
38787       //      this.collapsedTitleTextEl.innerHTML = title;
38788        // }
38789     //},
38790
38791     getBox : function(){
38792         var b;
38793       //  if(!this.collapsed){
38794             b = this.el.getBox(false, true);
38795        // }else{
38796           //  b = this.collapsedEl.getBox(false, true);
38797         //}
38798         return b;
38799     },
38800
38801     getMargins : function(){
38802         return this.margins;
38803         //return this.collapsed ? this.cmargins : this.margins;
38804     },
38805 /*
38806     highlight : function(){
38807         this.el.addClass("x-layout-panel-dragover");
38808     },
38809
38810     unhighlight : function(){
38811         this.el.removeClass("x-layout-panel-dragover");
38812     },
38813 */
38814     updateBox : function(box)
38815     {
38816         if (!this.bodyEl) {
38817             return; // not rendered yet..
38818         }
38819         
38820         this.box = box;
38821         if(!this.collapsed){
38822             this.el.dom.style.left = box.x + "px";
38823             this.el.dom.style.top = box.y + "px";
38824             this.updateBody(box.width, box.height);
38825         }else{
38826             this.collapsedEl.dom.style.left = box.x + "px";
38827             this.collapsedEl.dom.style.top = box.y + "px";
38828             this.collapsedEl.setSize(box.width, box.height);
38829         }
38830         if(this.tabs){
38831             this.tabs.autoSizeTabs();
38832         }
38833     },
38834
38835     updateBody : function(w, h)
38836     {
38837         if(w !== null){
38838             this.el.setWidth(w);
38839             w -= this.el.getBorderWidth("rl");
38840             if(this.config.adjustments){
38841                 w += this.config.adjustments[0];
38842             }
38843         }
38844         if(h !== null && h > 0){
38845             this.el.setHeight(h);
38846             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38847             h -= this.el.getBorderWidth("tb");
38848             if(this.config.adjustments){
38849                 h += this.config.adjustments[1];
38850             }
38851             this.bodyEl.setHeight(h);
38852             if(this.tabs){
38853                 h = this.tabs.syncHeight(h);
38854             }
38855         }
38856         if(this.panelSize){
38857             w = w !== null ? w : this.panelSize.width;
38858             h = h !== null ? h : this.panelSize.height;
38859         }
38860         if(this.activePanel){
38861             var el = this.activePanel.getEl();
38862             w = w !== null ? w : el.getWidth();
38863             h = h !== null ? h : el.getHeight();
38864             this.panelSize = {width: w, height: h};
38865             this.activePanel.setSize(w, h);
38866         }
38867         if(Roo.isIE && this.tabs){
38868             this.tabs.el.repaint();
38869         }
38870     },
38871
38872     /**
38873      * Returns the container element for this region.
38874      * @return {Roo.Element}
38875      */
38876     getEl : function(){
38877         return this.el;
38878     },
38879
38880     /**
38881      * Hides this region.
38882      */
38883     hide : function(){
38884         //if(!this.collapsed){
38885             this.el.dom.style.left = "-2000px";
38886             this.el.hide();
38887         //}else{
38888          //   this.collapsedEl.dom.style.left = "-2000px";
38889          //   this.collapsedEl.hide();
38890        // }
38891         this.visible = false;
38892         this.fireEvent("visibilitychange", this, false);
38893     },
38894
38895     /**
38896      * Shows this region if it was previously hidden.
38897      */
38898     show : function(){
38899         //if(!this.collapsed){
38900             this.el.show();
38901         //}else{
38902         //    this.collapsedEl.show();
38903        // }
38904         this.visible = true;
38905         this.fireEvent("visibilitychange", this, true);
38906     },
38907 /*
38908     closeClicked : function(){
38909         if(this.activePanel){
38910             this.remove(this.activePanel);
38911         }
38912     },
38913
38914     collapseClick : function(e){
38915         if(this.isSlid){
38916            e.stopPropagation();
38917            this.slideIn();
38918         }else{
38919            e.stopPropagation();
38920            this.slideOut();
38921         }
38922     },
38923 */
38924     /**
38925      * Collapses this region.
38926      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38927      */
38928     /*
38929     collapse : function(skipAnim, skipCheck = false){
38930         if(this.collapsed) {
38931             return;
38932         }
38933         
38934         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38935             
38936             this.collapsed = true;
38937             if(this.split){
38938                 this.split.el.hide();
38939             }
38940             if(this.config.animate && skipAnim !== true){
38941                 this.fireEvent("invalidated", this);
38942                 this.animateCollapse();
38943             }else{
38944                 this.el.setLocation(-20000,-20000);
38945                 this.el.hide();
38946                 this.collapsedEl.show();
38947                 this.fireEvent("collapsed", this);
38948                 this.fireEvent("invalidated", this);
38949             }
38950         }
38951         
38952     },
38953 */
38954     animateCollapse : function(){
38955         // overridden
38956     },
38957
38958     /**
38959      * Expands this region if it was previously collapsed.
38960      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38961      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38962      */
38963     /*
38964     expand : function(e, skipAnim){
38965         if(e) {
38966             e.stopPropagation();
38967         }
38968         if(!this.collapsed || this.el.hasActiveFx()) {
38969             return;
38970         }
38971         if(this.isSlid){
38972             this.afterSlideIn();
38973             skipAnim = true;
38974         }
38975         this.collapsed = false;
38976         if(this.config.animate && skipAnim !== true){
38977             this.animateExpand();
38978         }else{
38979             this.el.show();
38980             if(this.split){
38981                 this.split.el.show();
38982             }
38983             this.collapsedEl.setLocation(-2000,-2000);
38984             this.collapsedEl.hide();
38985             this.fireEvent("invalidated", this);
38986             this.fireEvent("expanded", this);
38987         }
38988     },
38989 */
38990     animateExpand : function(){
38991         // overridden
38992     },
38993
38994     initTabs : function()
38995     {
38996         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38997         
38998         var ts = new Roo.bootstrap.panel.Tabs({
38999             el: this.bodyEl.dom,
39000             region : this,
39001             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39002             disableTooltips: this.config.disableTabTips,
39003             toolbar : this.config.toolbar
39004         });
39005         
39006         if(this.config.hideTabs){
39007             ts.stripWrap.setDisplayed(false);
39008         }
39009         this.tabs = ts;
39010         ts.resizeTabs = this.config.resizeTabs === true;
39011         ts.minTabWidth = this.config.minTabWidth || 40;
39012         ts.maxTabWidth = this.config.maxTabWidth || 250;
39013         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39014         ts.monitorResize = false;
39015         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39016         ts.bodyEl.addClass('roo-layout-tabs-body');
39017         this.panels.each(this.initPanelAsTab, this);
39018     },
39019
39020     initPanelAsTab : function(panel){
39021         var ti = this.tabs.addTab(
39022             panel.getEl().id,
39023             panel.getTitle(),
39024             null,
39025             this.config.closeOnTab && panel.isClosable(),
39026             panel.tpl
39027         );
39028         if(panel.tabTip !== undefined){
39029             ti.setTooltip(panel.tabTip);
39030         }
39031         ti.on("activate", function(){
39032               this.setActivePanel(panel);
39033         }, this);
39034         
39035         if(this.config.closeOnTab){
39036             ti.on("beforeclose", function(t, e){
39037                 e.cancel = true;
39038                 this.remove(panel);
39039             }, this);
39040         }
39041         
39042         panel.tabItem = ti;
39043         
39044         return ti;
39045     },
39046
39047     updatePanelTitle : function(panel, title)
39048     {
39049         if(this.activePanel == panel){
39050             this.updateTitle(title);
39051         }
39052         if(this.tabs){
39053             var ti = this.tabs.getTab(panel.getEl().id);
39054             ti.setText(title);
39055             if(panel.tabTip !== undefined){
39056                 ti.setTooltip(panel.tabTip);
39057             }
39058         }
39059     },
39060
39061     updateTitle : function(title){
39062         if(this.titleTextEl && !this.config.title){
39063             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39064         }
39065     },
39066
39067     setActivePanel : function(panel)
39068     {
39069         panel = this.getPanel(panel);
39070         if(this.activePanel && this.activePanel != panel){
39071             if(this.activePanel.setActiveState(false) === false){
39072                 return;
39073             }
39074         }
39075         this.activePanel = panel;
39076         panel.setActiveState(true);
39077         if(this.panelSize){
39078             panel.setSize(this.panelSize.width, this.panelSize.height);
39079         }
39080         if(this.closeBtn){
39081             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39082         }
39083         this.updateTitle(panel.getTitle());
39084         if(this.tabs){
39085             this.fireEvent("invalidated", this);
39086         }
39087         this.fireEvent("panelactivated", this, panel);
39088     },
39089
39090     /**
39091      * Shows the specified panel.
39092      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39093      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39094      */
39095     showPanel : function(panel)
39096     {
39097         panel = this.getPanel(panel);
39098         if(panel){
39099             if(this.tabs){
39100                 var tab = this.tabs.getTab(panel.getEl().id);
39101                 if(tab.isHidden()){
39102                     this.tabs.unhideTab(tab.id);
39103                 }
39104                 tab.activate();
39105             }else{
39106                 this.setActivePanel(panel);
39107             }
39108         }
39109         return panel;
39110     },
39111
39112     /**
39113      * Get the active panel for this region.
39114      * @return {Roo.ContentPanel} The active panel or null
39115      */
39116     getActivePanel : function(){
39117         return this.activePanel;
39118     },
39119
39120     validateVisibility : function(){
39121         if(this.panels.getCount() < 1){
39122             this.updateTitle("&#160;");
39123             this.closeBtn.hide();
39124             this.hide();
39125         }else{
39126             if(!this.isVisible()){
39127                 this.show();
39128             }
39129         }
39130     },
39131
39132     /**
39133      * Adds the passed ContentPanel(s) to this region.
39134      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39135      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39136      */
39137     add : function(panel)
39138     {
39139         if(arguments.length > 1){
39140             for(var i = 0, len = arguments.length; i < len; i++) {
39141                 this.add(arguments[i]);
39142             }
39143             return null;
39144         }
39145         
39146         // if we have not been rendered yet, then we can not really do much of this..
39147         if (!this.bodyEl) {
39148             this.unrendered_panels.push(panel);
39149             return panel;
39150         }
39151         
39152         
39153         
39154         
39155         if(this.hasPanel(panel)){
39156             this.showPanel(panel);
39157             return panel;
39158         }
39159         panel.setRegion(this);
39160         this.panels.add(panel);
39161        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39162             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39163             // and hide them... ???
39164             this.bodyEl.dom.appendChild(panel.getEl().dom);
39165             if(panel.background !== true){
39166                 this.setActivePanel(panel);
39167             }
39168             this.fireEvent("paneladded", this, panel);
39169             return panel;
39170         }
39171         */
39172         if(!this.tabs){
39173             this.initTabs();
39174         }else{
39175             this.initPanelAsTab(panel);
39176         }
39177         
39178         
39179         if(panel.background !== true){
39180             this.tabs.activate(panel.getEl().id);
39181         }
39182         this.fireEvent("paneladded", this, panel);
39183         return panel;
39184     },
39185
39186     /**
39187      * Hides the tab for the specified panel.
39188      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39189      */
39190     hidePanel : function(panel){
39191         if(this.tabs && (panel = this.getPanel(panel))){
39192             this.tabs.hideTab(panel.getEl().id);
39193         }
39194     },
39195
39196     /**
39197      * Unhides the tab for a previously hidden panel.
39198      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39199      */
39200     unhidePanel : function(panel){
39201         if(this.tabs && (panel = this.getPanel(panel))){
39202             this.tabs.unhideTab(panel.getEl().id);
39203         }
39204     },
39205
39206     clearPanels : function(){
39207         while(this.panels.getCount() > 0){
39208              this.remove(this.panels.first());
39209         }
39210     },
39211
39212     /**
39213      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39214      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39215      * @param {Boolean} preservePanel Overrides the config preservePanel option
39216      * @return {Roo.ContentPanel} The panel that was removed
39217      */
39218     remove : function(panel, preservePanel)
39219     {
39220         panel = this.getPanel(panel);
39221         if(!panel){
39222             return null;
39223         }
39224         var e = {};
39225         this.fireEvent("beforeremove", this, panel, e);
39226         if(e.cancel === true){
39227             return null;
39228         }
39229         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39230         var panelId = panel.getId();
39231         this.panels.removeKey(panelId);
39232         if(preservePanel){
39233             document.body.appendChild(panel.getEl().dom);
39234         }
39235         if(this.tabs){
39236             this.tabs.removeTab(panel.getEl().id);
39237         }else if (!preservePanel){
39238             this.bodyEl.dom.removeChild(panel.getEl().dom);
39239         }
39240         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39241             var p = this.panels.first();
39242             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39243             tempEl.appendChild(p.getEl().dom);
39244             this.bodyEl.update("");
39245             this.bodyEl.dom.appendChild(p.getEl().dom);
39246             tempEl = null;
39247             this.updateTitle(p.getTitle());
39248             this.tabs = null;
39249             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39250             this.setActivePanel(p);
39251         }
39252         panel.setRegion(null);
39253         if(this.activePanel == panel){
39254             this.activePanel = null;
39255         }
39256         if(this.config.autoDestroy !== false && preservePanel !== true){
39257             try{panel.destroy();}catch(e){}
39258         }
39259         this.fireEvent("panelremoved", this, panel);
39260         return panel;
39261     },
39262
39263     /**
39264      * Returns the TabPanel component used by this region
39265      * @return {Roo.TabPanel}
39266      */
39267     getTabs : function(){
39268         return this.tabs;
39269     },
39270
39271     createTool : function(parentEl, className){
39272         var btn = Roo.DomHelper.append(parentEl, {
39273             tag: "div",
39274             cls: "x-layout-tools-button",
39275             children: [ {
39276                 tag: "div",
39277                 cls: "roo-layout-tools-button-inner " + className,
39278                 html: "&#160;"
39279             }]
39280         }, true);
39281         btn.addClassOnOver("roo-layout-tools-button-over");
39282         return btn;
39283     }
39284 });/*
39285  * Based on:
39286  * Ext JS Library 1.1.1
39287  * Copyright(c) 2006-2007, Ext JS, LLC.
39288  *
39289  * Originally Released Under LGPL - original licence link has changed is not relivant.
39290  *
39291  * Fork - LGPL
39292  * <script type="text/javascript">
39293  */
39294  
39295
39296
39297 /**
39298  * @class Roo.SplitLayoutRegion
39299  * @extends Roo.LayoutRegion
39300  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39301  */
39302 Roo.bootstrap.layout.Split = function(config){
39303     this.cursor = config.cursor;
39304     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39305 };
39306
39307 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39308 {
39309     splitTip : "Drag to resize.",
39310     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39311     useSplitTips : false,
39312
39313     applyConfig : function(config){
39314         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39315     },
39316     
39317     onRender : function(ctr,pos) {
39318         
39319         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39320         if(!this.config.split){
39321             return;
39322         }
39323         if(!this.split){
39324             
39325             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39326                             tag: "div",
39327                             id: this.el.id + "-split",
39328                             cls: "roo-layout-split roo-layout-split-"+this.position,
39329                             html: "&#160;"
39330             });
39331             /** The SplitBar for this region 
39332             * @type Roo.SplitBar */
39333             // does not exist yet...
39334             Roo.log([this.position, this.orientation]);
39335             
39336             this.split = new Roo.bootstrap.SplitBar({
39337                 dragElement : splitEl,
39338                 resizingElement: this.el,
39339                 orientation : this.orientation
39340             });
39341             
39342             this.split.on("moved", this.onSplitMove, this);
39343             this.split.useShim = this.config.useShim === true;
39344             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39345             if(this.useSplitTips){
39346                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39347             }
39348             //if(config.collapsible){
39349             //    this.split.el.on("dblclick", this.collapse,  this);
39350             //}
39351         }
39352         if(typeof this.config.minSize != "undefined"){
39353             this.split.minSize = this.config.minSize;
39354         }
39355         if(typeof this.config.maxSize != "undefined"){
39356             this.split.maxSize = this.config.maxSize;
39357         }
39358         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39359             this.hideSplitter();
39360         }
39361         
39362     },
39363
39364     getHMaxSize : function(){
39365          var cmax = this.config.maxSize || 10000;
39366          var center = this.mgr.getRegion("center");
39367          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39368     },
39369
39370     getVMaxSize : function(){
39371          var cmax = this.config.maxSize || 10000;
39372          var center = this.mgr.getRegion("center");
39373          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39374     },
39375
39376     onSplitMove : function(split, newSize){
39377         this.fireEvent("resized", this, newSize);
39378     },
39379     
39380     /** 
39381      * Returns the {@link Roo.SplitBar} for this region.
39382      * @return {Roo.SplitBar}
39383      */
39384     getSplitBar : function(){
39385         return this.split;
39386     },
39387     
39388     hide : function(){
39389         this.hideSplitter();
39390         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39391     },
39392
39393     hideSplitter : function(){
39394         if(this.split){
39395             this.split.el.setLocation(-2000,-2000);
39396             this.split.el.hide();
39397         }
39398     },
39399
39400     show : function(){
39401         if(this.split){
39402             this.split.el.show();
39403         }
39404         Roo.bootstrap.layout.Split.superclass.show.call(this);
39405     },
39406     
39407     beforeSlide: function(){
39408         if(Roo.isGecko){// firefox overflow auto bug workaround
39409             this.bodyEl.clip();
39410             if(this.tabs) {
39411                 this.tabs.bodyEl.clip();
39412             }
39413             if(this.activePanel){
39414                 this.activePanel.getEl().clip();
39415                 
39416                 if(this.activePanel.beforeSlide){
39417                     this.activePanel.beforeSlide();
39418                 }
39419             }
39420         }
39421     },
39422     
39423     afterSlide : function(){
39424         if(Roo.isGecko){// firefox overflow auto bug workaround
39425             this.bodyEl.unclip();
39426             if(this.tabs) {
39427                 this.tabs.bodyEl.unclip();
39428             }
39429             if(this.activePanel){
39430                 this.activePanel.getEl().unclip();
39431                 if(this.activePanel.afterSlide){
39432                     this.activePanel.afterSlide();
39433                 }
39434             }
39435         }
39436     },
39437
39438     initAutoHide : function(){
39439         if(this.autoHide !== false){
39440             if(!this.autoHideHd){
39441                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39442                 this.autoHideHd = {
39443                     "mouseout": function(e){
39444                         if(!e.within(this.el, true)){
39445                             st.delay(500);
39446                         }
39447                     },
39448                     "mouseover" : function(e){
39449                         st.cancel();
39450                     },
39451                     scope : this
39452                 };
39453             }
39454             this.el.on(this.autoHideHd);
39455         }
39456     },
39457
39458     clearAutoHide : function(){
39459         if(this.autoHide !== false){
39460             this.el.un("mouseout", this.autoHideHd.mouseout);
39461             this.el.un("mouseover", this.autoHideHd.mouseover);
39462         }
39463     },
39464
39465     clearMonitor : function(){
39466         Roo.get(document).un("click", this.slideInIf, this);
39467     },
39468
39469     // these names are backwards but not changed for compat
39470     slideOut : function(){
39471         if(this.isSlid || this.el.hasActiveFx()){
39472             return;
39473         }
39474         this.isSlid = true;
39475         if(this.collapseBtn){
39476             this.collapseBtn.hide();
39477         }
39478         this.closeBtnState = this.closeBtn.getStyle('display');
39479         this.closeBtn.hide();
39480         if(this.stickBtn){
39481             this.stickBtn.show();
39482         }
39483         this.el.show();
39484         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39485         this.beforeSlide();
39486         this.el.setStyle("z-index", 10001);
39487         this.el.slideIn(this.getSlideAnchor(), {
39488             callback: function(){
39489                 this.afterSlide();
39490                 this.initAutoHide();
39491                 Roo.get(document).on("click", this.slideInIf, this);
39492                 this.fireEvent("slideshow", this);
39493             },
39494             scope: this,
39495             block: true
39496         });
39497     },
39498
39499     afterSlideIn : function(){
39500         this.clearAutoHide();
39501         this.isSlid = false;
39502         this.clearMonitor();
39503         this.el.setStyle("z-index", "");
39504         if(this.collapseBtn){
39505             this.collapseBtn.show();
39506         }
39507         this.closeBtn.setStyle('display', this.closeBtnState);
39508         if(this.stickBtn){
39509             this.stickBtn.hide();
39510         }
39511         this.fireEvent("slidehide", this);
39512     },
39513
39514     slideIn : function(cb){
39515         if(!this.isSlid || this.el.hasActiveFx()){
39516             Roo.callback(cb);
39517             return;
39518         }
39519         this.isSlid = false;
39520         this.beforeSlide();
39521         this.el.slideOut(this.getSlideAnchor(), {
39522             callback: function(){
39523                 this.el.setLeftTop(-10000, -10000);
39524                 this.afterSlide();
39525                 this.afterSlideIn();
39526                 Roo.callback(cb);
39527             },
39528             scope: this,
39529             block: true
39530         });
39531     },
39532     
39533     slideInIf : function(e){
39534         if(!e.within(this.el)){
39535             this.slideIn();
39536         }
39537     },
39538
39539     animateCollapse : function(){
39540         this.beforeSlide();
39541         this.el.setStyle("z-index", 20000);
39542         var anchor = this.getSlideAnchor();
39543         this.el.slideOut(anchor, {
39544             callback : function(){
39545                 this.el.setStyle("z-index", "");
39546                 this.collapsedEl.slideIn(anchor, {duration:.3});
39547                 this.afterSlide();
39548                 this.el.setLocation(-10000,-10000);
39549                 this.el.hide();
39550                 this.fireEvent("collapsed", this);
39551             },
39552             scope: this,
39553             block: true
39554         });
39555     },
39556
39557     animateExpand : function(){
39558         this.beforeSlide();
39559         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39560         this.el.setStyle("z-index", 20000);
39561         this.collapsedEl.hide({
39562             duration:.1
39563         });
39564         this.el.slideIn(this.getSlideAnchor(), {
39565             callback : function(){
39566                 this.el.setStyle("z-index", "");
39567                 this.afterSlide();
39568                 if(this.split){
39569                     this.split.el.show();
39570                 }
39571                 this.fireEvent("invalidated", this);
39572                 this.fireEvent("expanded", this);
39573             },
39574             scope: this,
39575             block: true
39576         });
39577     },
39578
39579     anchors : {
39580         "west" : "left",
39581         "east" : "right",
39582         "north" : "top",
39583         "south" : "bottom"
39584     },
39585
39586     sanchors : {
39587         "west" : "l",
39588         "east" : "r",
39589         "north" : "t",
39590         "south" : "b"
39591     },
39592
39593     canchors : {
39594         "west" : "tl-tr",
39595         "east" : "tr-tl",
39596         "north" : "tl-bl",
39597         "south" : "bl-tl"
39598     },
39599
39600     getAnchor : function(){
39601         return this.anchors[this.position];
39602     },
39603
39604     getCollapseAnchor : function(){
39605         return this.canchors[this.position];
39606     },
39607
39608     getSlideAnchor : function(){
39609         return this.sanchors[this.position];
39610     },
39611
39612     getAlignAdj : function(){
39613         var cm = this.cmargins;
39614         switch(this.position){
39615             case "west":
39616                 return [0, 0];
39617             break;
39618             case "east":
39619                 return [0, 0];
39620             break;
39621             case "north":
39622                 return [0, 0];
39623             break;
39624             case "south":
39625                 return [0, 0];
39626             break;
39627         }
39628     },
39629
39630     getExpandAdj : function(){
39631         var c = this.collapsedEl, cm = this.cmargins;
39632         switch(this.position){
39633             case "west":
39634                 return [-(cm.right+c.getWidth()+cm.left), 0];
39635             break;
39636             case "east":
39637                 return [cm.right+c.getWidth()+cm.left, 0];
39638             break;
39639             case "north":
39640                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39641             break;
39642             case "south":
39643                 return [0, cm.top+cm.bottom+c.getHeight()];
39644             break;
39645         }
39646     }
39647 });/*
39648  * Based on:
39649  * Ext JS Library 1.1.1
39650  * Copyright(c) 2006-2007, Ext JS, LLC.
39651  *
39652  * Originally Released Under LGPL - original licence link has changed is not relivant.
39653  *
39654  * Fork - LGPL
39655  * <script type="text/javascript">
39656  */
39657 /*
39658  * These classes are private internal classes
39659  */
39660 Roo.bootstrap.layout.Center = function(config){
39661     config.region = "center";
39662     Roo.bootstrap.layout.Region.call(this, config);
39663     this.visible = true;
39664     this.minWidth = config.minWidth || 20;
39665     this.minHeight = config.minHeight || 20;
39666 };
39667
39668 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39669     hide : function(){
39670         // center panel can't be hidden
39671     },
39672     
39673     show : function(){
39674         // center panel can't be hidden
39675     },
39676     
39677     getMinWidth: function(){
39678         return this.minWidth;
39679     },
39680     
39681     getMinHeight: function(){
39682         return this.minHeight;
39683     }
39684 });
39685
39686
39687
39688
39689  
39690
39691
39692
39693
39694
39695
39696 Roo.bootstrap.layout.North = function(config)
39697 {
39698     config.region = 'north';
39699     config.cursor = 'n-resize';
39700     
39701     Roo.bootstrap.layout.Split.call(this, config);
39702     
39703     
39704     if(this.split){
39705         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39706         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39707         this.split.el.addClass("roo-layout-split-v");
39708     }
39709     //var size = config.initialSize || config.height;
39710     //if(this.el && typeof size != "undefined"){
39711     //    this.el.setHeight(size);
39712     //}
39713 };
39714 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39715 {
39716     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39717      
39718      
39719     onRender : function(ctr, pos)
39720     {
39721         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39722         var size = this.config.initialSize || this.config.height;
39723         if(this.el && typeof size != "undefined"){
39724             this.el.setHeight(size);
39725         }
39726     
39727     },
39728     
39729     getBox : function(){
39730         if(this.collapsed){
39731             return this.collapsedEl.getBox();
39732         }
39733         var box = this.el.getBox();
39734         if(this.split){
39735             box.height += this.split.el.getHeight();
39736         }
39737         return box;
39738     },
39739     
39740     updateBox : function(box){
39741         if(this.split && !this.collapsed){
39742             box.height -= this.split.el.getHeight();
39743             this.split.el.setLeft(box.x);
39744             this.split.el.setTop(box.y+box.height);
39745             this.split.el.setWidth(box.width);
39746         }
39747         if(this.collapsed){
39748             this.updateBody(box.width, null);
39749         }
39750         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39751     }
39752 });
39753
39754
39755
39756
39757
39758 Roo.bootstrap.layout.South = function(config){
39759     config.region = 'south';
39760     config.cursor = 's-resize';
39761     Roo.bootstrap.layout.Split.call(this, config);
39762     if(this.split){
39763         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39764         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39765         this.split.el.addClass("roo-layout-split-v");
39766     }
39767     
39768 };
39769
39770 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39771     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39772     
39773     onRender : function(ctr, pos)
39774     {
39775         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39776         var size = this.config.initialSize || this.config.height;
39777         if(this.el && typeof size != "undefined"){
39778             this.el.setHeight(size);
39779         }
39780     
39781     },
39782     
39783     getBox : function(){
39784         if(this.collapsed){
39785             return this.collapsedEl.getBox();
39786         }
39787         var box = this.el.getBox();
39788         if(this.split){
39789             var sh = this.split.el.getHeight();
39790             box.height += sh;
39791             box.y -= sh;
39792         }
39793         return box;
39794     },
39795     
39796     updateBox : function(box){
39797         if(this.split && !this.collapsed){
39798             var sh = this.split.el.getHeight();
39799             box.height -= sh;
39800             box.y += sh;
39801             this.split.el.setLeft(box.x);
39802             this.split.el.setTop(box.y-sh);
39803             this.split.el.setWidth(box.width);
39804         }
39805         if(this.collapsed){
39806             this.updateBody(box.width, null);
39807         }
39808         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39809     }
39810 });
39811
39812 Roo.bootstrap.layout.East = function(config){
39813     config.region = "east";
39814     config.cursor = "e-resize";
39815     Roo.bootstrap.layout.Split.call(this, config);
39816     if(this.split){
39817         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39818         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39819         this.split.el.addClass("roo-layout-split-h");
39820     }
39821     
39822 };
39823 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39824     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39825     
39826     onRender : function(ctr, pos)
39827     {
39828         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39829         var size = this.config.initialSize || this.config.width;
39830         if(this.el && typeof size != "undefined"){
39831             this.el.setWidth(size);
39832         }
39833     
39834     },
39835     
39836     getBox : function(){
39837         if(this.collapsed){
39838             return this.collapsedEl.getBox();
39839         }
39840         var box = this.el.getBox();
39841         if(this.split){
39842             var sw = this.split.el.getWidth();
39843             box.width += sw;
39844             box.x -= sw;
39845         }
39846         return box;
39847     },
39848
39849     updateBox : function(box){
39850         if(this.split && !this.collapsed){
39851             var sw = this.split.el.getWidth();
39852             box.width -= sw;
39853             this.split.el.setLeft(box.x);
39854             this.split.el.setTop(box.y);
39855             this.split.el.setHeight(box.height);
39856             box.x += sw;
39857         }
39858         if(this.collapsed){
39859             this.updateBody(null, box.height);
39860         }
39861         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39862     }
39863 });
39864
39865 Roo.bootstrap.layout.West = function(config){
39866     config.region = "west";
39867     config.cursor = "w-resize";
39868     
39869     Roo.bootstrap.layout.Split.call(this, config);
39870     if(this.split){
39871         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39872         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39873         this.split.el.addClass("roo-layout-split-h");
39874     }
39875     
39876 };
39877 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39878     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39879     
39880     onRender: function(ctr, pos)
39881     {
39882         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39883         var size = this.config.initialSize || this.config.width;
39884         if(typeof size != "undefined"){
39885             this.el.setWidth(size);
39886         }
39887     },
39888     
39889     getBox : function(){
39890         if(this.collapsed){
39891             return this.collapsedEl.getBox();
39892         }
39893         var box = this.el.getBox();
39894         if (box.width == 0) {
39895             box.width = this.config.width; // kludge?
39896         }
39897         if(this.split){
39898             box.width += this.split.el.getWidth();
39899         }
39900         return box;
39901     },
39902     
39903     updateBox : function(box){
39904         if(this.split && !this.collapsed){
39905             var sw = this.split.el.getWidth();
39906             box.width -= sw;
39907             this.split.el.setLeft(box.x+box.width);
39908             this.split.el.setTop(box.y);
39909             this.split.el.setHeight(box.height);
39910         }
39911         if(this.collapsed){
39912             this.updateBody(null, box.height);
39913         }
39914         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39915     }
39916 });Roo.namespace("Roo.bootstrap.panel");/*
39917  * Based on:
39918  * Ext JS Library 1.1.1
39919  * Copyright(c) 2006-2007, Ext JS, LLC.
39920  *
39921  * Originally Released Under LGPL - original licence link has changed is not relivant.
39922  *
39923  * Fork - LGPL
39924  * <script type="text/javascript">
39925  */
39926 /**
39927  * @class Roo.ContentPanel
39928  * @extends Roo.util.Observable
39929  * A basic ContentPanel element.
39930  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39931  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39932  * @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
39933  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39934  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39935  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39936  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39937  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39938  * @cfg {String} title          The title for this panel
39939  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39940  * @cfg {String} url            Calls {@link #setUrl} with this value
39941  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39942  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39943  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39944  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39945  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39946  * @cfg {Boolean} badges render the badges
39947  * @cfg {String} cls  extra classes to use  
39948  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39949
39950  * @constructor
39951  * Create a new ContentPanel.
39952  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39953  * @param {String/Object} config A string to set only the title or a config object
39954  * @param {String} content (optional) Set the HTML content for this panel
39955  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39956  */
39957 Roo.bootstrap.panel.Content = function( config){
39958     
39959     this.tpl = config.tpl || false;
39960     
39961     var el = config.el;
39962     var content = config.content;
39963
39964     if(config.autoCreate){ // xtype is available if this is called from factory
39965         el = Roo.id();
39966     }
39967     this.el = Roo.get(el);
39968     if(!this.el && config && config.autoCreate){
39969         if(typeof config.autoCreate == "object"){
39970             if(!config.autoCreate.id){
39971                 config.autoCreate.id = config.id||el;
39972             }
39973             this.el = Roo.DomHelper.append(document.body,
39974                         config.autoCreate, true);
39975         }else{
39976             var elcfg =  {
39977                 tag: "div",
39978                 cls: (config.cls || '') +
39979                     (config.background ? ' bg-' + config.background : '') +
39980                     " roo-layout-inactive-content",
39981                 id: config.id||el
39982             };
39983             if (config.iframe) {
39984                 elcfg.cn = [
39985                     {
39986                         tag : 'iframe',
39987                         style : 'border: 0px',
39988                         src : 'about:blank'
39989                     }
39990                 ];
39991             }
39992               
39993             if (config.html) {
39994                 elcfg.html = config.html;
39995                 
39996             }
39997                         
39998             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39999             if (config.iframe) {
40000                 this.iframeEl = this.el.select('iframe',true).first();
40001             }
40002             
40003         }
40004     } 
40005     this.closable = false;
40006     this.loaded = false;
40007     this.active = false;
40008    
40009       
40010     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40011         
40012         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40013         
40014         this.wrapEl = this.el; //this.el.wrap();
40015         var ti = [];
40016         if (config.toolbar.items) {
40017             ti = config.toolbar.items ;
40018             delete config.toolbar.items ;
40019         }
40020         
40021         var nitems = [];
40022         this.toolbar.render(this.wrapEl, 'before');
40023         for(var i =0;i < ti.length;i++) {
40024           //  Roo.log(['add child', items[i]]);
40025             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40026         }
40027         this.toolbar.items = nitems;
40028         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40029         delete config.toolbar;
40030         
40031     }
40032     /*
40033     // xtype created footer. - not sure if will work as we normally have to render first..
40034     if (this.footer && !this.footer.el && this.footer.xtype) {
40035         if (!this.wrapEl) {
40036             this.wrapEl = this.el.wrap();
40037         }
40038     
40039         this.footer.container = this.wrapEl.createChild();
40040          
40041         this.footer = Roo.factory(this.footer, Roo);
40042         
40043     }
40044     */
40045     
40046      if(typeof config == "string"){
40047         this.title = config;
40048     }else{
40049         Roo.apply(this, config);
40050     }
40051     
40052     if(this.resizeEl){
40053         this.resizeEl = Roo.get(this.resizeEl, true);
40054     }else{
40055         this.resizeEl = this.el;
40056     }
40057     // handle view.xtype
40058     
40059  
40060     
40061     
40062     this.addEvents({
40063         /**
40064          * @event activate
40065          * Fires when this panel is activated. 
40066          * @param {Roo.ContentPanel} this
40067          */
40068         "activate" : true,
40069         /**
40070          * @event deactivate
40071          * Fires when this panel is activated. 
40072          * @param {Roo.ContentPanel} this
40073          */
40074         "deactivate" : true,
40075
40076         /**
40077          * @event resize
40078          * Fires when this panel is resized if fitToFrame is true.
40079          * @param {Roo.ContentPanel} this
40080          * @param {Number} width The width after any component adjustments
40081          * @param {Number} height The height after any component adjustments
40082          */
40083         "resize" : true,
40084         
40085          /**
40086          * @event render
40087          * Fires when this tab is created
40088          * @param {Roo.ContentPanel} this
40089          */
40090         "render" : true
40091         
40092         
40093         
40094     });
40095     
40096
40097     
40098     
40099     if(this.autoScroll && !this.iframe){
40100         this.resizeEl.setStyle("overflow", "auto");
40101     } else {
40102         // fix randome scrolling
40103         //this.el.on('scroll', function() {
40104         //    Roo.log('fix random scolling');
40105         //    this.scrollTo('top',0); 
40106         //});
40107     }
40108     content = content || this.content;
40109     if(content){
40110         this.setContent(content);
40111     }
40112     if(config && config.url){
40113         this.setUrl(this.url, this.params, this.loadOnce);
40114     }
40115     
40116     
40117     
40118     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40119     
40120     if (this.view && typeof(this.view.xtype) != 'undefined') {
40121         this.view.el = this.el.appendChild(document.createElement("div"));
40122         this.view = Roo.factory(this.view); 
40123         this.view.render  &&  this.view.render(false, '');  
40124     }
40125     
40126     
40127     this.fireEvent('render', this);
40128 };
40129
40130 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40131     
40132     cls : '',
40133     background : '',
40134     
40135     tabTip : '',
40136     
40137     iframe : false,
40138     iframeEl : false,
40139     
40140     setRegion : function(region){
40141         this.region = region;
40142         this.setActiveClass(region && !this.background);
40143     },
40144     
40145     
40146     setActiveClass: function(state)
40147     {
40148         if(state){
40149            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40150            this.el.setStyle('position','relative');
40151         }else{
40152            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40153            this.el.setStyle('position', 'absolute');
40154         } 
40155     },
40156     
40157     /**
40158      * Returns the toolbar for this Panel if one was configured. 
40159      * @return {Roo.Toolbar} 
40160      */
40161     getToolbar : function(){
40162         return this.toolbar;
40163     },
40164     
40165     setActiveState : function(active)
40166     {
40167         this.active = active;
40168         this.setActiveClass(active);
40169         if(!active){
40170             if(this.fireEvent("deactivate", this) === false){
40171                 return false;
40172             }
40173             return true;
40174         }
40175         this.fireEvent("activate", this);
40176         return true;
40177     },
40178     /**
40179      * Updates this panel's element (not for iframe)
40180      * @param {String} content The new content
40181      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40182     */
40183     setContent : function(content, loadScripts){
40184         if (this.iframe) {
40185             return;
40186         }
40187         
40188         this.el.update(content, loadScripts);
40189     },
40190
40191     ignoreResize : function(w, h){
40192         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40193             return true;
40194         }else{
40195             this.lastSize = {width: w, height: h};
40196             return false;
40197         }
40198     },
40199     /**
40200      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40201      * @return {Roo.UpdateManager} The UpdateManager
40202      */
40203     getUpdateManager : function(){
40204         if (this.iframe) {
40205             return false;
40206         }
40207         return this.el.getUpdateManager();
40208     },
40209      /**
40210      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40211      * Does not work with IFRAME contents
40212      * @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:
40213 <pre><code>
40214 panel.load({
40215     url: "your-url.php",
40216     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40217     callback: yourFunction,
40218     scope: yourObject, //(optional scope)
40219     discardUrl: false,
40220     nocache: false,
40221     text: "Loading...",
40222     timeout: 30,
40223     scripts: false
40224 });
40225 </code></pre>
40226      
40227      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40228      * 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.
40229      * @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}
40230      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40231      * @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.
40232      * @return {Roo.ContentPanel} this
40233      */
40234     load : function(){
40235         
40236         if (this.iframe) {
40237             return this;
40238         }
40239         
40240         var um = this.el.getUpdateManager();
40241         um.update.apply(um, arguments);
40242         return this;
40243     },
40244
40245
40246     /**
40247      * 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.
40248      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40249      * @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)
40250      * @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)
40251      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40252      */
40253     setUrl : function(url, params, loadOnce){
40254         if (this.iframe) {
40255             this.iframeEl.dom.src = url;
40256             return false;
40257         }
40258         
40259         if(this.refreshDelegate){
40260             this.removeListener("activate", this.refreshDelegate);
40261         }
40262         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40263         this.on("activate", this.refreshDelegate);
40264         return this.el.getUpdateManager();
40265     },
40266     
40267     _handleRefresh : function(url, params, loadOnce){
40268         if(!loadOnce || !this.loaded){
40269             var updater = this.el.getUpdateManager();
40270             updater.update(url, params, this._setLoaded.createDelegate(this));
40271         }
40272     },
40273     
40274     _setLoaded : function(){
40275         this.loaded = true;
40276     }, 
40277     
40278     /**
40279      * Returns this panel's id
40280      * @return {String} 
40281      */
40282     getId : function(){
40283         return this.el.id;
40284     },
40285     
40286     /** 
40287      * Returns this panel's element - used by regiosn to add.
40288      * @return {Roo.Element} 
40289      */
40290     getEl : function(){
40291         return this.wrapEl || this.el;
40292     },
40293     
40294    
40295     
40296     adjustForComponents : function(width, height)
40297     {
40298         //Roo.log('adjustForComponents ');
40299         if(this.resizeEl != this.el){
40300             width -= this.el.getFrameWidth('lr');
40301             height -= this.el.getFrameWidth('tb');
40302         }
40303         if(this.toolbar){
40304             var te = this.toolbar.getEl();
40305             te.setWidth(width);
40306             height -= te.getHeight();
40307         }
40308         if(this.footer){
40309             var te = this.footer.getEl();
40310             te.setWidth(width);
40311             height -= te.getHeight();
40312         }
40313         
40314         
40315         if(this.adjustments){
40316             width += this.adjustments[0];
40317             height += this.adjustments[1];
40318         }
40319         return {"width": width, "height": height};
40320     },
40321     
40322     setSize : function(width, height){
40323         if(this.fitToFrame && !this.ignoreResize(width, height)){
40324             if(this.fitContainer && this.resizeEl != this.el){
40325                 this.el.setSize(width, height);
40326             }
40327             var size = this.adjustForComponents(width, height);
40328             if (this.iframe) {
40329                 this.iframeEl.setSize(width,height);
40330             }
40331             
40332             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40333             this.fireEvent('resize', this, size.width, size.height);
40334             
40335             
40336         }
40337     },
40338     
40339     /**
40340      * Returns this panel's title
40341      * @return {String} 
40342      */
40343     getTitle : function(){
40344         
40345         if (typeof(this.title) != 'object') {
40346             return this.title;
40347         }
40348         
40349         var t = '';
40350         for (var k in this.title) {
40351             if (!this.title.hasOwnProperty(k)) {
40352                 continue;
40353             }
40354             
40355             if (k.indexOf('-') >= 0) {
40356                 var s = k.split('-');
40357                 for (var i = 0; i<s.length; i++) {
40358                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40359                 }
40360             } else {
40361                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40362             }
40363         }
40364         return t;
40365     },
40366     
40367     /**
40368      * Set this panel's title
40369      * @param {String} title
40370      */
40371     setTitle : function(title){
40372         this.title = title;
40373         if(this.region){
40374             this.region.updatePanelTitle(this, title);
40375         }
40376     },
40377     
40378     /**
40379      * Returns true is this panel was configured to be closable
40380      * @return {Boolean} 
40381      */
40382     isClosable : function(){
40383         return this.closable;
40384     },
40385     
40386     beforeSlide : function(){
40387         this.el.clip();
40388         this.resizeEl.clip();
40389     },
40390     
40391     afterSlide : function(){
40392         this.el.unclip();
40393         this.resizeEl.unclip();
40394     },
40395     
40396     /**
40397      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40398      *   Will fail silently if the {@link #setUrl} method has not been called.
40399      *   This does not activate the panel, just updates its content.
40400      */
40401     refresh : function(){
40402         if(this.refreshDelegate){
40403            this.loaded = false;
40404            this.refreshDelegate();
40405         }
40406     },
40407     
40408     /**
40409      * Destroys this panel
40410      */
40411     destroy : function(){
40412         this.el.removeAllListeners();
40413         var tempEl = document.createElement("span");
40414         tempEl.appendChild(this.el.dom);
40415         tempEl.innerHTML = "";
40416         this.el.remove();
40417         this.el = null;
40418     },
40419     
40420     /**
40421      * form - if the content panel contains a form - this is a reference to it.
40422      * @type {Roo.form.Form}
40423      */
40424     form : false,
40425     /**
40426      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40427      *    This contains a reference to it.
40428      * @type {Roo.View}
40429      */
40430     view : false,
40431     
40432       /**
40433      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40434      * <pre><code>
40435
40436 layout.addxtype({
40437        xtype : 'Form',
40438        items: [ .... ]
40439    }
40440 );
40441
40442 </code></pre>
40443      * @param {Object} cfg Xtype definition of item to add.
40444      */
40445     
40446     
40447     getChildContainer: function () {
40448         return this.getEl();
40449     }
40450     
40451     
40452     /*
40453         var  ret = new Roo.factory(cfg);
40454         return ret;
40455         
40456         
40457         // add form..
40458         if (cfg.xtype.match(/^Form$/)) {
40459             
40460             var el;
40461             //if (this.footer) {
40462             //    el = this.footer.container.insertSibling(false, 'before');
40463             //} else {
40464                 el = this.el.createChild();
40465             //}
40466
40467             this.form = new  Roo.form.Form(cfg);
40468             
40469             
40470             if ( this.form.allItems.length) {
40471                 this.form.render(el.dom);
40472             }
40473             return this.form;
40474         }
40475         // should only have one of theses..
40476         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40477             // views.. should not be just added - used named prop 'view''
40478             
40479             cfg.el = this.el.appendChild(document.createElement("div"));
40480             // factory?
40481             
40482             var ret = new Roo.factory(cfg);
40483              
40484              ret.render && ret.render(false, ''); // render blank..
40485             this.view = ret;
40486             return ret;
40487         }
40488         return false;
40489     }
40490     \*/
40491 });
40492  
40493 /**
40494  * @class Roo.bootstrap.panel.Grid
40495  * @extends Roo.bootstrap.panel.Content
40496  * @constructor
40497  * Create a new GridPanel.
40498  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40499  * @param {Object} config A the config object
40500   
40501  */
40502
40503
40504
40505 Roo.bootstrap.panel.Grid = function(config)
40506 {
40507     
40508       
40509     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40510         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40511
40512     config.el = this.wrapper;
40513     //this.el = this.wrapper;
40514     
40515       if (config.container) {
40516         // ctor'ed from a Border/panel.grid
40517         
40518         
40519         this.wrapper.setStyle("overflow", "hidden");
40520         this.wrapper.addClass('roo-grid-container');
40521
40522     }
40523     
40524     
40525     if(config.toolbar){
40526         var tool_el = this.wrapper.createChild();    
40527         this.toolbar = Roo.factory(config.toolbar);
40528         var ti = [];
40529         if (config.toolbar.items) {
40530             ti = config.toolbar.items ;
40531             delete config.toolbar.items ;
40532         }
40533         
40534         var nitems = [];
40535         this.toolbar.render(tool_el);
40536         for(var i =0;i < ti.length;i++) {
40537           //  Roo.log(['add child', items[i]]);
40538             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40539         }
40540         this.toolbar.items = nitems;
40541         
40542         delete config.toolbar;
40543     }
40544     
40545     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40546     config.grid.scrollBody = true;;
40547     config.grid.monitorWindowResize = false; // turn off autosizing
40548     config.grid.autoHeight = false;
40549     config.grid.autoWidth = false;
40550     
40551     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40552     
40553     if (config.background) {
40554         // render grid on panel activation (if panel background)
40555         this.on('activate', function(gp) {
40556             if (!gp.grid.rendered) {
40557                 gp.grid.render(this.wrapper);
40558                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40559             }
40560         });
40561             
40562     } else {
40563         this.grid.render(this.wrapper);
40564         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40565
40566     }
40567     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40568     // ??? needed ??? config.el = this.wrapper;
40569     
40570     
40571     
40572   
40573     // xtype created footer. - not sure if will work as we normally have to render first..
40574     if (this.footer && !this.footer.el && this.footer.xtype) {
40575         
40576         var ctr = this.grid.getView().getFooterPanel(true);
40577         this.footer.dataSource = this.grid.dataSource;
40578         this.footer = Roo.factory(this.footer, Roo);
40579         this.footer.render(ctr);
40580         
40581     }
40582     
40583     
40584     
40585     
40586      
40587 };
40588
40589 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40590     getId : function(){
40591         return this.grid.id;
40592     },
40593     
40594     /**
40595      * Returns the grid for this panel
40596      * @return {Roo.bootstrap.Table} 
40597      */
40598     getGrid : function(){
40599         return this.grid;    
40600     },
40601     
40602     setSize : function(width, height){
40603         if(!this.ignoreResize(width, height)){
40604             var grid = this.grid;
40605             var size = this.adjustForComponents(width, height);
40606             // tfoot is not a footer?
40607           
40608             
40609             var gridel = grid.getGridEl();
40610             gridel.setSize(size.width, size.height);
40611             
40612             var tbd = grid.getGridEl().select('tbody', true).first();
40613             var thd = grid.getGridEl().select('thead',true).first();
40614             var tbf= grid.getGridEl().select('tfoot', true).first();
40615
40616             if (tbf) {
40617                 size.height -= tbf.getHeight();
40618             }
40619             if (thd) {
40620                 size.height -= thd.getHeight();
40621             }
40622             
40623             tbd.setSize(size.width, size.height );
40624             // this is for the account management tab -seems to work there.
40625             var thd = grid.getGridEl().select('thead',true).first();
40626             //if (tbd) {
40627             //    tbd.setSize(size.width, size.height - thd.getHeight());
40628             //}
40629              
40630             grid.autoSize();
40631         }
40632     },
40633      
40634     
40635     
40636     beforeSlide : function(){
40637         this.grid.getView().scroller.clip();
40638     },
40639     
40640     afterSlide : function(){
40641         this.grid.getView().scroller.unclip();
40642     },
40643     
40644     destroy : function(){
40645         this.grid.destroy();
40646         delete this.grid;
40647         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40648     }
40649 });
40650
40651 /**
40652  * @class Roo.bootstrap.panel.Nest
40653  * @extends Roo.bootstrap.panel.Content
40654  * @constructor
40655  * Create a new Panel, that can contain a layout.Border.
40656  * 
40657  * 
40658  * @param {Roo.BorderLayout} layout The layout for this panel
40659  * @param {String/Object} config A string to set only the title or a config object
40660  */
40661 Roo.bootstrap.panel.Nest = function(config)
40662 {
40663     // construct with only one argument..
40664     /* FIXME - implement nicer consturctors
40665     if (layout.layout) {
40666         config = layout;
40667         layout = config.layout;
40668         delete config.layout;
40669     }
40670     if (layout.xtype && !layout.getEl) {
40671         // then layout needs constructing..
40672         layout = Roo.factory(layout, Roo);
40673     }
40674     */
40675     
40676     config.el =  config.layout.getEl();
40677     
40678     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40679     
40680     config.layout.monitorWindowResize = false; // turn off autosizing
40681     this.layout = config.layout;
40682     this.layout.getEl().addClass("roo-layout-nested-layout");
40683     this.layout.parent = this;
40684     
40685     
40686     
40687     
40688 };
40689
40690 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40691
40692     setSize : function(width, height){
40693         if(!this.ignoreResize(width, height)){
40694             var size = this.adjustForComponents(width, height);
40695             var el = this.layout.getEl();
40696             if (size.height < 1) {
40697                 el.setWidth(size.width);   
40698             } else {
40699                 el.setSize(size.width, size.height);
40700             }
40701             var touch = el.dom.offsetWidth;
40702             this.layout.layout();
40703             // ie requires a double layout on the first pass
40704             if(Roo.isIE && !this.initialized){
40705                 this.initialized = true;
40706                 this.layout.layout();
40707             }
40708         }
40709     },
40710     
40711     // activate all subpanels if not currently active..
40712     
40713     setActiveState : function(active){
40714         this.active = active;
40715         this.setActiveClass(active);
40716         
40717         if(!active){
40718             this.fireEvent("deactivate", this);
40719             return;
40720         }
40721         
40722         this.fireEvent("activate", this);
40723         // not sure if this should happen before or after..
40724         if (!this.layout) {
40725             return; // should not happen..
40726         }
40727         var reg = false;
40728         for (var r in this.layout.regions) {
40729             reg = this.layout.getRegion(r);
40730             if (reg.getActivePanel()) {
40731                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40732                 reg.setActivePanel(reg.getActivePanel());
40733                 continue;
40734             }
40735             if (!reg.panels.length) {
40736                 continue;
40737             }
40738             reg.showPanel(reg.getPanel(0));
40739         }
40740         
40741         
40742         
40743         
40744     },
40745     
40746     /**
40747      * Returns the nested BorderLayout for this panel
40748      * @return {Roo.BorderLayout} 
40749      */
40750     getLayout : function(){
40751         return this.layout;
40752     },
40753     
40754      /**
40755      * Adds a xtype elements to the layout of the nested panel
40756      * <pre><code>
40757
40758 panel.addxtype({
40759        xtype : 'ContentPanel',
40760        region: 'west',
40761        items: [ .... ]
40762    }
40763 );
40764
40765 panel.addxtype({
40766         xtype : 'NestedLayoutPanel',
40767         region: 'west',
40768         layout: {
40769            center: { },
40770            west: { }   
40771         },
40772         items : [ ... list of content panels or nested layout panels.. ]
40773    }
40774 );
40775 </code></pre>
40776      * @param {Object} cfg Xtype definition of item to add.
40777      */
40778     addxtype : function(cfg) {
40779         return this.layout.addxtype(cfg);
40780     
40781     }
40782 });/*
40783  * Based on:
40784  * Ext JS Library 1.1.1
40785  * Copyright(c) 2006-2007, Ext JS, LLC.
40786  *
40787  * Originally Released Under LGPL - original licence link has changed is not relivant.
40788  *
40789  * Fork - LGPL
40790  * <script type="text/javascript">
40791  */
40792 /**
40793  * @class Roo.TabPanel
40794  * @extends Roo.util.Observable
40795  * A lightweight tab container.
40796  * <br><br>
40797  * Usage:
40798  * <pre><code>
40799 // basic tabs 1, built from existing content
40800 var tabs = new Roo.TabPanel("tabs1");
40801 tabs.addTab("script", "View Script");
40802 tabs.addTab("markup", "View Markup");
40803 tabs.activate("script");
40804
40805 // more advanced tabs, built from javascript
40806 var jtabs = new Roo.TabPanel("jtabs");
40807 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40808
40809 // set up the UpdateManager
40810 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40811 var updater = tab2.getUpdateManager();
40812 updater.setDefaultUrl("ajax1.htm");
40813 tab2.on('activate', updater.refresh, updater, true);
40814
40815 // Use setUrl for Ajax loading
40816 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40817 tab3.setUrl("ajax2.htm", null, true);
40818
40819 // Disabled tab
40820 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40821 tab4.disable();
40822
40823 jtabs.activate("jtabs-1");
40824  * </code></pre>
40825  * @constructor
40826  * Create a new TabPanel.
40827  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40828  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40829  */
40830 Roo.bootstrap.panel.Tabs = function(config){
40831     /**
40832     * The container element for this TabPanel.
40833     * @type Roo.Element
40834     */
40835     this.el = Roo.get(config.el);
40836     delete config.el;
40837     if(config){
40838         if(typeof config == "boolean"){
40839             this.tabPosition = config ? "bottom" : "top";
40840         }else{
40841             Roo.apply(this, config);
40842         }
40843     }
40844     
40845     if(this.tabPosition == "bottom"){
40846         // if tabs are at the bottom = create the body first.
40847         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40848         this.el.addClass("roo-tabs-bottom");
40849     }
40850     // next create the tabs holders
40851     
40852     if (this.tabPosition == "west"){
40853         
40854         var reg = this.region; // fake it..
40855         while (reg) {
40856             if (!reg.mgr.parent) {
40857                 break;
40858             }
40859             reg = reg.mgr.parent.region;
40860         }
40861         Roo.log("got nest?");
40862         Roo.log(reg);
40863         if (reg.mgr.getRegion('west')) {
40864             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40865             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40866             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40867             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40868             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40869         
40870             
40871         }
40872         
40873         
40874     } else {
40875      
40876         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40877         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40878         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40879         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40880     }
40881     
40882     
40883     if(Roo.isIE){
40884         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40885     }
40886     
40887     // finally - if tabs are at the top, then create the body last..
40888     if(this.tabPosition != "bottom"){
40889         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40890          * @type Roo.Element
40891          */
40892         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40893         this.el.addClass("roo-tabs-top");
40894     }
40895     this.items = [];
40896
40897     this.bodyEl.setStyle("position", "relative");
40898
40899     this.active = null;
40900     this.activateDelegate = this.activate.createDelegate(this);
40901
40902     this.addEvents({
40903         /**
40904          * @event tabchange
40905          * Fires when the active tab changes
40906          * @param {Roo.TabPanel} this
40907          * @param {Roo.TabPanelItem} activePanel The new active tab
40908          */
40909         "tabchange": true,
40910         /**
40911          * @event beforetabchange
40912          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40913          * @param {Roo.TabPanel} this
40914          * @param {Object} e Set cancel to true on this object to cancel the tab change
40915          * @param {Roo.TabPanelItem} tab The tab being changed to
40916          */
40917         "beforetabchange" : true
40918     });
40919
40920     Roo.EventManager.onWindowResize(this.onResize, this);
40921     this.cpad = this.el.getPadding("lr");
40922     this.hiddenCount = 0;
40923
40924
40925     // toolbar on the tabbar support...
40926     if (this.toolbar) {
40927         alert("no toolbar support yet");
40928         this.toolbar  = false;
40929         /*
40930         var tcfg = this.toolbar;
40931         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40932         this.toolbar = new Roo.Toolbar(tcfg);
40933         if (Roo.isSafari) {
40934             var tbl = tcfg.container.child('table', true);
40935             tbl.setAttribute('width', '100%');
40936         }
40937         */
40938         
40939     }
40940    
40941
40942
40943     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40944 };
40945
40946 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40947     /*
40948      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40949      */
40950     tabPosition : "top",
40951     /*
40952      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40953      */
40954     currentTabWidth : 0,
40955     /*
40956      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40957      */
40958     minTabWidth : 40,
40959     /*
40960      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40961      */
40962     maxTabWidth : 250,
40963     /*
40964      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40965      */
40966     preferredTabWidth : 175,
40967     /*
40968      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40969      */
40970     resizeTabs : false,
40971     /*
40972      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40973      */
40974     monitorResize : true,
40975     /*
40976      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40977      */
40978     toolbar : false,  // set by caller..
40979     
40980     region : false, /// set by caller
40981     
40982     disableTooltips : true, // not used yet...
40983
40984     /**
40985      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40986      * @param {String} id The id of the div to use <b>or create</b>
40987      * @param {String} text The text for the tab
40988      * @param {String} content (optional) Content to put in the TabPanelItem body
40989      * @param {Boolean} closable (optional) True to create a close icon on the tab
40990      * @return {Roo.TabPanelItem} The created TabPanelItem
40991      */
40992     addTab : function(id, text, content, closable, tpl)
40993     {
40994         var item = new Roo.bootstrap.panel.TabItem({
40995             panel: this,
40996             id : id,
40997             text : text,
40998             closable : closable,
40999             tpl : tpl
41000         });
41001         this.addTabItem(item);
41002         if(content){
41003             item.setContent(content);
41004         }
41005         return item;
41006     },
41007
41008     /**
41009      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41010      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41011      * @return {Roo.TabPanelItem}
41012      */
41013     getTab : function(id){
41014         return this.items[id];
41015     },
41016
41017     /**
41018      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41019      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41020      */
41021     hideTab : function(id){
41022         var t = this.items[id];
41023         if(!t.isHidden()){
41024            t.setHidden(true);
41025            this.hiddenCount++;
41026            this.autoSizeTabs();
41027         }
41028     },
41029
41030     /**
41031      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41032      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41033      */
41034     unhideTab : function(id){
41035         var t = this.items[id];
41036         if(t.isHidden()){
41037            t.setHidden(false);
41038            this.hiddenCount--;
41039            this.autoSizeTabs();
41040         }
41041     },
41042
41043     /**
41044      * Adds an existing {@link Roo.TabPanelItem}.
41045      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41046      */
41047     addTabItem : function(item)
41048     {
41049         this.items[item.id] = item;
41050         this.items.push(item);
41051         this.autoSizeTabs();
41052       //  if(this.resizeTabs){
41053     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41054   //         this.autoSizeTabs();
41055 //        }else{
41056 //            item.autoSize();
41057        // }
41058     },
41059
41060     /**
41061      * Removes a {@link Roo.TabPanelItem}.
41062      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41063      */
41064     removeTab : function(id){
41065         var items = this.items;
41066         var tab = items[id];
41067         if(!tab) { return; }
41068         var index = items.indexOf(tab);
41069         if(this.active == tab && items.length > 1){
41070             var newTab = this.getNextAvailable(index);
41071             if(newTab) {
41072                 newTab.activate();
41073             }
41074         }
41075         this.stripEl.dom.removeChild(tab.pnode.dom);
41076         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41077             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41078         }
41079         items.splice(index, 1);
41080         delete this.items[tab.id];
41081         tab.fireEvent("close", tab);
41082         tab.purgeListeners();
41083         this.autoSizeTabs();
41084     },
41085
41086     getNextAvailable : function(start){
41087         var items = this.items;
41088         var index = start;
41089         // look for a next tab that will slide over to
41090         // replace the one being removed
41091         while(index < items.length){
41092             var item = items[++index];
41093             if(item && !item.isHidden()){
41094                 return item;
41095             }
41096         }
41097         // if one isn't found select the previous tab (on the left)
41098         index = start;
41099         while(index >= 0){
41100             var item = items[--index];
41101             if(item && !item.isHidden()){
41102                 return item;
41103             }
41104         }
41105         return null;
41106     },
41107
41108     /**
41109      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41110      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41111      */
41112     disableTab : function(id){
41113         var tab = this.items[id];
41114         if(tab && this.active != tab){
41115             tab.disable();
41116         }
41117     },
41118
41119     /**
41120      * Enables a {@link Roo.TabPanelItem} that is disabled.
41121      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41122      */
41123     enableTab : function(id){
41124         var tab = this.items[id];
41125         tab.enable();
41126     },
41127
41128     /**
41129      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41130      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41131      * @return {Roo.TabPanelItem} The TabPanelItem.
41132      */
41133     activate : function(id)
41134     {
41135         //Roo.log('activite:'  + id);
41136         
41137         var tab = this.items[id];
41138         if(!tab){
41139             return null;
41140         }
41141         if(tab == this.active || tab.disabled){
41142             return tab;
41143         }
41144         var e = {};
41145         this.fireEvent("beforetabchange", this, e, tab);
41146         if(e.cancel !== true && !tab.disabled){
41147             if(this.active){
41148                 this.active.hide();
41149             }
41150             this.active = this.items[id];
41151             this.active.show();
41152             this.fireEvent("tabchange", this, this.active);
41153         }
41154         return tab;
41155     },
41156
41157     /**
41158      * Gets the active {@link Roo.TabPanelItem}.
41159      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41160      */
41161     getActiveTab : function(){
41162         return this.active;
41163     },
41164
41165     /**
41166      * Updates the tab body element to fit the height of the container element
41167      * for overflow scrolling
41168      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41169      */
41170     syncHeight : function(targetHeight){
41171         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41172         var bm = this.bodyEl.getMargins();
41173         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41174         this.bodyEl.setHeight(newHeight);
41175         return newHeight;
41176     },
41177
41178     onResize : function(){
41179         if(this.monitorResize){
41180             this.autoSizeTabs();
41181         }
41182     },
41183
41184     /**
41185      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41186      */
41187     beginUpdate : function(){
41188         this.updating = true;
41189     },
41190
41191     /**
41192      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41193      */
41194     endUpdate : function(){
41195         this.updating = false;
41196         this.autoSizeTabs();
41197     },
41198
41199     /**
41200      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41201      */
41202     autoSizeTabs : function()
41203     {
41204         var count = this.items.length;
41205         var vcount = count - this.hiddenCount;
41206         
41207         if (vcount < 2) {
41208             this.stripEl.hide();
41209         } else {
41210             this.stripEl.show();
41211         }
41212         
41213         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41214             return;
41215         }
41216         
41217         
41218         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41219         var availWidth = Math.floor(w / vcount);
41220         var b = this.stripBody;
41221         if(b.getWidth() > w){
41222             var tabs = this.items;
41223             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41224             if(availWidth < this.minTabWidth){
41225                 /*if(!this.sleft){    // incomplete scrolling code
41226                     this.createScrollButtons();
41227                 }
41228                 this.showScroll();
41229                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41230             }
41231         }else{
41232             if(this.currentTabWidth < this.preferredTabWidth){
41233                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41234             }
41235         }
41236     },
41237
41238     /**
41239      * Returns the number of tabs in this TabPanel.
41240      * @return {Number}
41241      */
41242      getCount : function(){
41243          return this.items.length;
41244      },
41245
41246     /**
41247      * Resizes all the tabs to the passed width
41248      * @param {Number} The new width
41249      */
41250     setTabWidth : function(width){
41251         this.currentTabWidth = width;
41252         for(var i = 0, len = this.items.length; i < len; i++) {
41253                 if(!this.items[i].isHidden()) {
41254                 this.items[i].setWidth(width);
41255             }
41256         }
41257     },
41258
41259     /**
41260      * Destroys this TabPanel
41261      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41262      */
41263     destroy : function(removeEl){
41264         Roo.EventManager.removeResizeListener(this.onResize, this);
41265         for(var i = 0, len = this.items.length; i < len; i++){
41266             this.items[i].purgeListeners();
41267         }
41268         if(removeEl === true){
41269             this.el.update("");
41270             this.el.remove();
41271         }
41272     },
41273     
41274     createStrip : function(container)
41275     {
41276         var strip = document.createElement("nav");
41277         strip.className = Roo.bootstrap.version == 4 ?
41278             "navbar-light bg-light" : 
41279             "navbar navbar-default"; //"x-tabs-wrap";
41280         container.appendChild(strip);
41281         return strip;
41282     },
41283     
41284     createStripList : function(strip)
41285     {
41286         // div wrapper for retard IE
41287         // returns the "tr" element.
41288         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41289         //'<div class="x-tabs-strip-wrap">'+
41290           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41291           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41292         return strip.firstChild; //.firstChild.firstChild.firstChild;
41293     },
41294     createBody : function(container)
41295     {
41296         var body = document.createElement("div");
41297         Roo.id(body, "tab-body");
41298         //Roo.fly(body).addClass("x-tabs-body");
41299         Roo.fly(body).addClass("tab-content");
41300         container.appendChild(body);
41301         return body;
41302     },
41303     createItemBody :function(bodyEl, id){
41304         var body = Roo.getDom(id);
41305         if(!body){
41306             body = document.createElement("div");
41307             body.id = id;
41308         }
41309         //Roo.fly(body).addClass("x-tabs-item-body");
41310         Roo.fly(body).addClass("tab-pane");
41311          bodyEl.insertBefore(body, bodyEl.firstChild);
41312         return body;
41313     },
41314     /** @private */
41315     createStripElements :  function(stripEl, text, closable, tpl)
41316     {
41317         var td = document.createElement("li"); // was td..
41318         td.className = 'nav-item';
41319         
41320         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41321         
41322         
41323         stripEl.appendChild(td);
41324         /*if(closable){
41325             td.className = "x-tabs-closable";
41326             if(!this.closeTpl){
41327                 this.closeTpl = new Roo.Template(
41328                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41329                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41330                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41331                 );
41332             }
41333             var el = this.closeTpl.overwrite(td, {"text": text});
41334             var close = el.getElementsByTagName("div")[0];
41335             var inner = el.getElementsByTagName("em")[0];
41336             return {"el": el, "close": close, "inner": inner};
41337         } else {
41338         */
41339         // not sure what this is..
41340 //            if(!this.tabTpl){
41341                 //this.tabTpl = new Roo.Template(
41342                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41343                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41344                 //);
41345 //                this.tabTpl = new Roo.Template(
41346 //                   '<a href="#">' +
41347 //                   '<span unselectable="on"' +
41348 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41349 //                            ' >{text}</span></a>'
41350 //                );
41351 //                
41352 //            }
41353
41354
41355             var template = tpl || this.tabTpl || false;
41356             
41357             if(!template){
41358                 template =  new Roo.Template(
41359                         Roo.bootstrap.version == 4 ? 
41360                             (
41361                                 '<a class="nav-link" href="#" unselectable="on"' +
41362                                      (this.disableTooltips ? '' : ' title="{text}"') +
41363                                      ' >{text}</a>'
41364                             ) : (
41365                                 '<a class="nav-link" href="#">' +
41366                                 '<span unselectable="on"' +
41367                                          (this.disableTooltips ? '' : ' title="{text}"') +
41368                                     ' >{text}</span></a>'
41369                             )
41370                 );
41371             }
41372             
41373             switch (typeof(template)) {
41374                 case 'object' :
41375                     break;
41376                 case 'string' :
41377                     template = new Roo.Template(template);
41378                     break;
41379                 default :
41380                     break;
41381             }
41382             
41383             var el = template.overwrite(td, {"text": text});
41384             
41385             var inner = el.getElementsByTagName("span")[0];
41386             
41387             return {"el": el, "inner": inner};
41388             
41389     }
41390         
41391     
41392 });
41393
41394 /**
41395  * @class Roo.TabPanelItem
41396  * @extends Roo.util.Observable
41397  * Represents an individual item (tab plus body) in a TabPanel.
41398  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41399  * @param {String} id The id of this TabPanelItem
41400  * @param {String} text The text for the tab of this TabPanelItem
41401  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41402  */
41403 Roo.bootstrap.panel.TabItem = function(config){
41404     /**
41405      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41406      * @type Roo.TabPanel
41407      */
41408     this.tabPanel = config.panel;
41409     /**
41410      * The id for this TabPanelItem
41411      * @type String
41412      */
41413     this.id = config.id;
41414     /** @private */
41415     this.disabled = false;
41416     /** @private */
41417     this.text = config.text;
41418     /** @private */
41419     this.loaded = false;
41420     this.closable = config.closable;
41421
41422     /**
41423      * The body element for this TabPanelItem.
41424      * @type Roo.Element
41425      */
41426     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41427     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41428     this.bodyEl.setStyle("display", "block");
41429     this.bodyEl.setStyle("zoom", "1");
41430     //this.hideAction();
41431
41432     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41433     /** @private */
41434     this.el = Roo.get(els.el);
41435     this.inner = Roo.get(els.inner, true);
41436      this.textEl = Roo.bootstrap.version == 4 ?
41437         this.el : Roo.get(this.el.dom.firstChild, true);
41438
41439     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41440     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41441
41442     
41443 //    this.el.on("mousedown", this.onTabMouseDown, this);
41444     this.el.on("click", this.onTabClick, this);
41445     /** @private */
41446     if(config.closable){
41447         var c = Roo.get(els.close, true);
41448         c.dom.title = this.closeText;
41449         c.addClassOnOver("close-over");
41450         c.on("click", this.closeClick, this);
41451      }
41452
41453     this.addEvents({
41454          /**
41455          * @event activate
41456          * Fires when this tab becomes the active tab.
41457          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41458          * @param {Roo.TabPanelItem} this
41459          */
41460         "activate": true,
41461         /**
41462          * @event beforeclose
41463          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41464          * @param {Roo.TabPanelItem} this
41465          * @param {Object} e Set cancel to true on this object to cancel the close.
41466          */
41467         "beforeclose": true,
41468         /**
41469          * @event close
41470          * Fires when this tab is closed.
41471          * @param {Roo.TabPanelItem} this
41472          */
41473          "close": true,
41474         /**
41475          * @event deactivate
41476          * Fires when this tab is no longer the active tab.
41477          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41478          * @param {Roo.TabPanelItem} this
41479          */
41480          "deactivate" : true
41481     });
41482     this.hidden = false;
41483
41484     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41485 };
41486
41487 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41488            {
41489     purgeListeners : function(){
41490        Roo.util.Observable.prototype.purgeListeners.call(this);
41491        this.el.removeAllListeners();
41492     },
41493     /**
41494      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41495      */
41496     show : function(){
41497         this.status_node.addClass("active");
41498         this.showAction();
41499         if(Roo.isOpera){
41500             this.tabPanel.stripWrap.repaint();
41501         }
41502         this.fireEvent("activate", this.tabPanel, this);
41503     },
41504
41505     /**
41506      * Returns true if this tab is the active tab.
41507      * @return {Boolean}
41508      */
41509     isActive : function(){
41510         return this.tabPanel.getActiveTab() == this;
41511     },
41512
41513     /**
41514      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41515      */
41516     hide : function(){
41517         this.status_node.removeClass("active");
41518         this.hideAction();
41519         this.fireEvent("deactivate", this.tabPanel, this);
41520     },
41521
41522     hideAction : function(){
41523         this.bodyEl.hide();
41524         this.bodyEl.setStyle("position", "absolute");
41525         this.bodyEl.setLeft("-20000px");
41526         this.bodyEl.setTop("-20000px");
41527     },
41528
41529     showAction : function(){
41530         this.bodyEl.setStyle("position", "relative");
41531         this.bodyEl.setTop("");
41532         this.bodyEl.setLeft("");
41533         this.bodyEl.show();
41534     },
41535
41536     /**
41537      * Set the tooltip for the tab.
41538      * @param {String} tooltip The tab's tooltip
41539      */
41540     setTooltip : function(text){
41541         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41542             this.textEl.dom.qtip = text;
41543             this.textEl.dom.removeAttribute('title');
41544         }else{
41545             this.textEl.dom.title = text;
41546         }
41547     },
41548
41549     onTabClick : function(e){
41550         e.preventDefault();
41551         this.tabPanel.activate(this.id);
41552     },
41553
41554     onTabMouseDown : function(e){
41555         e.preventDefault();
41556         this.tabPanel.activate(this.id);
41557     },
41558 /*
41559     getWidth : function(){
41560         return this.inner.getWidth();
41561     },
41562
41563     setWidth : function(width){
41564         var iwidth = width - this.linode.getPadding("lr");
41565         this.inner.setWidth(iwidth);
41566         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41567         this.linode.setWidth(width);
41568     },
41569 */
41570     /**
41571      * Show or hide the tab
41572      * @param {Boolean} hidden True to hide or false to show.
41573      */
41574     setHidden : function(hidden){
41575         this.hidden = hidden;
41576         this.linode.setStyle("display", hidden ? "none" : "");
41577     },
41578
41579     /**
41580      * Returns true if this tab is "hidden"
41581      * @return {Boolean}
41582      */
41583     isHidden : function(){
41584         return this.hidden;
41585     },
41586
41587     /**
41588      * Returns the text for this tab
41589      * @return {String}
41590      */
41591     getText : function(){
41592         return this.text;
41593     },
41594     /*
41595     autoSize : function(){
41596         //this.el.beginMeasure();
41597         this.textEl.setWidth(1);
41598         /*
41599          *  #2804 [new] Tabs in Roojs
41600          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41601          */
41602         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41603         //this.el.endMeasure();
41604     //},
41605
41606     /**
41607      * Sets the text for the tab (Note: this also sets the tooltip text)
41608      * @param {String} text The tab's text and tooltip
41609      */
41610     setText : function(text){
41611         this.text = text;
41612         this.textEl.update(text);
41613         this.setTooltip(text);
41614         //if(!this.tabPanel.resizeTabs){
41615         //    this.autoSize();
41616         //}
41617     },
41618     /**
41619      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41620      */
41621     activate : function(){
41622         this.tabPanel.activate(this.id);
41623     },
41624
41625     /**
41626      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41627      */
41628     disable : function(){
41629         if(this.tabPanel.active != this){
41630             this.disabled = true;
41631             this.status_node.addClass("disabled");
41632         }
41633     },
41634
41635     /**
41636      * Enables this TabPanelItem if it was previously disabled.
41637      */
41638     enable : function(){
41639         this.disabled = false;
41640         this.status_node.removeClass("disabled");
41641     },
41642
41643     /**
41644      * Sets the content for this TabPanelItem.
41645      * @param {String} content The content
41646      * @param {Boolean} loadScripts true to look for and load scripts
41647      */
41648     setContent : function(content, loadScripts){
41649         this.bodyEl.update(content, loadScripts);
41650     },
41651
41652     /**
41653      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41654      * @return {Roo.UpdateManager} The UpdateManager
41655      */
41656     getUpdateManager : function(){
41657         return this.bodyEl.getUpdateManager();
41658     },
41659
41660     /**
41661      * Set a URL to be used to load the content for this TabPanelItem.
41662      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41663      * @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)
41664      * @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)
41665      * @return {Roo.UpdateManager} The UpdateManager
41666      */
41667     setUrl : function(url, params, loadOnce){
41668         if(this.refreshDelegate){
41669             this.un('activate', this.refreshDelegate);
41670         }
41671         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41672         this.on("activate", this.refreshDelegate);
41673         return this.bodyEl.getUpdateManager();
41674     },
41675
41676     /** @private */
41677     _handleRefresh : function(url, params, loadOnce){
41678         if(!loadOnce || !this.loaded){
41679             var updater = this.bodyEl.getUpdateManager();
41680             updater.update(url, params, this._setLoaded.createDelegate(this));
41681         }
41682     },
41683
41684     /**
41685      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41686      *   Will fail silently if the setUrl method has not been called.
41687      *   This does not activate the panel, just updates its content.
41688      */
41689     refresh : function(){
41690         if(this.refreshDelegate){
41691            this.loaded = false;
41692            this.refreshDelegate();
41693         }
41694     },
41695
41696     /** @private */
41697     _setLoaded : function(){
41698         this.loaded = true;
41699     },
41700
41701     /** @private */
41702     closeClick : function(e){
41703         var o = {};
41704         e.stopEvent();
41705         this.fireEvent("beforeclose", this, o);
41706         if(o.cancel !== true){
41707             this.tabPanel.removeTab(this.id);
41708         }
41709     },
41710     /**
41711      * The text displayed in the tooltip for the close icon.
41712      * @type String
41713      */
41714     closeText : "Close this tab"
41715 });
41716 /**
41717 *    This script refer to:
41718 *    Title: International Telephone Input
41719 *    Author: Jack O'Connor
41720 *    Code version:  v12.1.12
41721 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41722 **/
41723
41724 Roo.bootstrap.PhoneInputData = function() {
41725     var d = [
41726       [
41727         "Afghanistan (‫افغانستان‬‎)",
41728         "af",
41729         "93"
41730       ],
41731       [
41732         "Albania (Shqipëri)",
41733         "al",
41734         "355"
41735       ],
41736       [
41737         "Algeria (‫الجزائر‬‎)",
41738         "dz",
41739         "213"
41740       ],
41741       [
41742         "American Samoa",
41743         "as",
41744         "1684"
41745       ],
41746       [
41747         "Andorra",
41748         "ad",
41749         "376"
41750       ],
41751       [
41752         "Angola",
41753         "ao",
41754         "244"
41755       ],
41756       [
41757         "Anguilla",
41758         "ai",
41759         "1264"
41760       ],
41761       [
41762         "Antigua and Barbuda",
41763         "ag",
41764         "1268"
41765       ],
41766       [
41767         "Argentina",
41768         "ar",
41769         "54"
41770       ],
41771       [
41772         "Armenia (Հայաստան)",
41773         "am",
41774         "374"
41775       ],
41776       [
41777         "Aruba",
41778         "aw",
41779         "297"
41780       ],
41781       [
41782         "Australia",
41783         "au",
41784         "61",
41785         0
41786       ],
41787       [
41788         "Austria (Österreich)",
41789         "at",
41790         "43"
41791       ],
41792       [
41793         "Azerbaijan (Azərbaycan)",
41794         "az",
41795         "994"
41796       ],
41797       [
41798         "Bahamas",
41799         "bs",
41800         "1242"
41801       ],
41802       [
41803         "Bahrain (‫البحرين‬‎)",
41804         "bh",
41805         "973"
41806       ],
41807       [
41808         "Bangladesh (বাংলাদেশ)",
41809         "bd",
41810         "880"
41811       ],
41812       [
41813         "Barbados",
41814         "bb",
41815         "1246"
41816       ],
41817       [
41818         "Belarus (Беларусь)",
41819         "by",
41820         "375"
41821       ],
41822       [
41823         "Belgium (België)",
41824         "be",
41825         "32"
41826       ],
41827       [
41828         "Belize",
41829         "bz",
41830         "501"
41831       ],
41832       [
41833         "Benin (Bénin)",
41834         "bj",
41835         "229"
41836       ],
41837       [
41838         "Bermuda",
41839         "bm",
41840         "1441"
41841       ],
41842       [
41843         "Bhutan (འབྲུག)",
41844         "bt",
41845         "975"
41846       ],
41847       [
41848         "Bolivia",
41849         "bo",
41850         "591"
41851       ],
41852       [
41853         "Bosnia and Herzegovina (Босна и Херцеговина)",
41854         "ba",
41855         "387"
41856       ],
41857       [
41858         "Botswana",
41859         "bw",
41860         "267"
41861       ],
41862       [
41863         "Brazil (Brasil)",
41864         "br",
41865         "55"
41866       ],
41867       [
41868         "British Indian Ocean Territory",
41869         "io",
41870         "246"
41871       ],
41872       [
41873         "British Virgin Islands",
41874         "vg",
41875         "1284"
41876       ],
41877       [
41878         "Brunei",
41879         "bn",
41880         "673"
41881       ],
41882       [
41883         "Bulgaria (България)",
41884         "bg",
41885         "359"
41886       ],
41887       [
41888         "Burkina Faso",
41889         "bf",
41890         "226"
41891       ],
41892       [
41893         "Burundi (Uburundi)",
41894         "bi",
41895         "257"
41896       ],
41897       [
41898         "Cambodia (កម្ពុជា)",
41899         "kh",
41900         "855"
41901       ],
41902       [
41903         "Cameroon (Cameroun)",
41904         "cm",
41905         "237"
41906       ],
41907       [
41908         "Canada",
41909         "ca",
41910         "1",
41911         1,
41912         ["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"]
41913       ],
41914       [
41915         "Cape Verde (Kabu Verdi)",
41916         "cv",
41917         "238"
41918       ],
41919       [
41920         "Caribbean Netherlands",
41921         "bq",
41922         "599",
41923         1
41924       ],
41925       [
41926         "Cayman Islands",
41927         "ky",
41928         "1345"
41929       ],
41930       [
41931         "Central African Republic (République centrafricaine)",
41932         "cf",
41933         "236"
41934       ],
41935       [
41936         "Chad (Tchad)",
41937         "td",
41938         "235"
41939       ],
41940       [
41941         "Chile",
41942         "cl",
41943         "56"
41944       ],
41945       [
41946         "China (中国)",
41947         "cn",
41948         "86"
41949       ],
41950       [
41951         "Christmas Island",
41952         "cx",
41953         "61",
41954         2
41955       ],
41956       [
41957         "Cocos (Keeling) Islands",
41958         "cc",
41959         "61",
41960         1
41961       ],
41962       [
41963         "Colombia",
41964         "co",
41965         "57"
41966       ],
41967       [
41968         "Comoros (‫جزر القمر‬‎)",
41969         "km",
41970         "269"
41971       ],
41972       [
41973         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41974         "cd",
41975         "243"
41976       ],
41977       [
41978         "Congo (Republic) (Congo-Brazzaville)",
41979         "cg",
41980         "242"
41981       ],
41982       [
41983         "Cook Islands",
41984         "ck",
41985         "682"
41986       ],
41987       [
41988         "Costa Rica",
41989         "cr",
41990         "506"
41991       ],
41992       [
41993         "Côte d’Ivoire",
41994         "ci",
41995         "225"
41996       ],
41997       [
41998         "Croatia (Hrvatska)",
41999         "hr",
42000         "385"
42001       ],
42002       [
42003         "Cuba",
42004         "cu",
42005         "53"
42006       ],
42007       [
42008         "Curaçao",
42009         "cw",
42010         "599",
42011         0
42012       ],
42013       [
42014         "Cyprus (Κύπρος)",
42015         "cy",
42016         "357"
42017       ],
42018       [
42019         "Czech Republic (Česká republika)",
42020         "cz",
42021         "420"
42022       ],
42023       [
42024         "Denmark (Danmark)",
42025         "dk",
42026         "45"
42027       ],
42028       [
42029         "Djibouti",
42030         "dj",
42031         "253"
42032       ],
42033       [
42034         "Dominica",
42035         "dm",
42036         "1767"
42037       ],
42038       [
42039         "Dominican Republic (República Dominicana)",
42040         "do",
42041         "1",
42042         2,
42043         ["809", "829", "849"]
42044       ],
42045       [
42046         "Ecuador",
42047         "ec",
42048         "593"
42049       ],
42050       [
42051         "Egypt (‫مصر‬‎)",
42052         "eg",
42053         "20"
42054       ],
42055       [
42056         "El Salvador",
42057         "sv",
42058         "503"
42059       ],
42060       [
42061         "Equatorial Guinea (Guinea Ecuatorial)",
42062         "gq",
42063         "240"
42064       ],
42065       [
42066         "Eritrea",
42067         "er",
42068         "291"
42069       ],
42070       [
42071         "Estonia (Eesti)",
42072         "ee",
42073         "372"
42074       ],
42075       [
42076         "Ethiopia",
42077         "et",
42078         "251"
42079       ],
42080       [
42081         "Falkland Islands (Islas Malvinas)",
42082         "fk",
42083         "500"
42084       ],
42085       [
42086         "Faroe Islands (Føroyar)",
42087         "fo",
42088         "298"
42089       ],
42090       [
42091         "Fiji",
42092         "fj",
42093         "679"
42094       ],
42095       [
42096         "Finland (Suomi)",
42097         "fi",
42098         "358",
42099         0
42100       ],
42101       [
42102         "France",
42103         "fr",
42104         "33"
42105       ],
42106       [
42107         "French Guiana (Guyane française)",
42108         "gf",
42109         "594"
42110       ],
42111       [
42112         "French Polynesia (Polynésie française)",
42113         "pf",
42114         "689"
42115       ],
42116       [
42117         "Gabon",
42118         "ga",
42119         "241"
42120       ],
42121       [
42122         "Gambia",
42123         "gm",
42124         "220"
42125       ],
42126       [
42127         "Georgia (საქართველო)",
42128         "ge",
42129         "995"
42130       ],
42131       [
42132         "Germany (Deutschland)",
42133         "de",
42134         "49"
42135       ],
42136       [
42137         "Ghana (Gaana)",
42138         "gh",
42139         "233"
42140       ],
42141       [
42142         "Gibraltar",
42143         "gi",
42144         "350"
42145       ],
42146       [
42147         "Greece (Ελλάδα)",
42148         "gr",
42149         "30"
42150       ],
42151       [
42152         "Greenland (Kalaallit Nunaat)",
42153         "gl",
42154         "299"
42155       ],
42156       [
42157         "Grenada",
42158         "gd",
42159         "1473"
42160       ],
42161       [
42162         "Guadeloupe",
42163         "gp",
42164         "590",
42165         0
42166       ],
42167       [
42168         "Guam",
42169         "gu",
42170         "1671"
42171       ],
42172       [
42173         "Guatemala",
42174         "gt",
42175         "502"
42176       ],
42177       [
42178         "Guernsey",
42179         "gg",
42180         "44",
42181         1
42182       ],
42183       [
42184         "Guinea (Guinée)",
42185         "gn",
42186         "224"
42187       ],
42188       [
42189         "Guinea-Bissau (Guiné Bissau)",
42190         "gw",
42191         "245"
42192       ],
42193       [
42194         "Guyana",
42195         "gy",
42196         "592"
42197       ],
42198       [
42199         "Haiti",
42200         "ht",
42201         "509"
42202       ],
42203       [
42204         "Honduras",
42205         "hn",
42206         "504"
42207       ],
42208       [
42209         "Hong Kong (香港)",
42210         "hk",
42211         "852"
42212       ],
42213       [
42214         "Hungary (Magyarország)",
42215         "hu",
42216         "36"
42217       ],
42218       [
42219         "Iceland (Ísland)",
42220         "is",
42221         "354"
42222       ],
42223       [
42224         "India (भारत)",
42225         "in",
42226         "91"
42227       ],
42228       [
42229         "Indonesia",
42230         "id",
42231         "62"
42232       ],
42233       [
42234         "Iran (‫ایران‬‎)",
42235         "ir",
42236         "98"
42237       ],
42238       [
42239         "Iraq (‫العراق‬‎)",
42240         "iq",
42241         "964"
42242       ],
42243       [
42244         "Ireland",
42245         "ie",
42246         "353"
42247       ],
42248       [
42249         "Isle of Man",
42250         "im",
42251         "44",
42252         2
42253       ],
42254       [
42255         "Israel (‫ישראל‬‎)",
42256         "il",
42257         "972"
42258       ],
42259       [
42260         "Italy (Italia)",
42261         "it",
42262         "39",
42263         0
42264       ],
42265       [
42266         "Jamaica",
42267         "jm",
42268         "1876"
42269       ],
42270       [
42271         "Japan (日本)",
42272         "jp",
42273         "81"
42274       ],
42275       [
42276         "Jersey",
42277         "je",
42278         "44",
42279         3
42280       ],
42281       [
42282         "Jordan (‫الأردن‬‎)",
42283         "jo",
42284         "962"
42285       ],
42286       [
42287         "Kazakhstan (Казахстан)",
42288         "kz",
42289         "7",
42290         1
42291       ],
42292       [
42293         "Kenya",
42294         "ke",
42295         "254"
42296       ],
42297       [
42298         "Kiribati",
42299         "ki",
42300         "686"
42301       ],
42302       [
42303         "Kosovo",
42304         "xk",
42305         "383"
42306       ],
42307       [
42308         "Kuwait (‫الكويت‬‎)",
42309         "kw",
42310         "965"
42311       ],
42312       [
42313         "Kyrgyzstan (Кыргызстан)",
42314         "kg",
42315         "996"
42316       ],
42317       [
42318         "Laos (ລາວ)",
42319         "la",
42320         "856"
42321       ],
42322       [
42323         "Latvia (Latvija)",
42324         "lv",
42325         "371"
42326       ],
42327       [
42328         "Lebanon (‫لبنان‬‎)",
42329         "lb",
42330         "961"
42331       ],
42332       [
42333         "Lesotho",
42334         "ls",
42335         "266"
42336       ],
42337       [
42338         "Liberia",
42339         "lr",
42340         "231"
42341       ],
42342       [
42343         "Libya (‫ليبيا‬‎)",
42344         "ly",
42345         "218"
42346       ],
42347       [
42348         "Liechtenstein",
42349         "li",
42350         "423"
42351       ],
42352       [
42353         "Lithuania (Lietuva)",
42354         "lt",
42355         "370"
42356       ],
42357       [
42358         "Luxembourg",
42359         "lu",
42360         "352"
42361       ],
42362       [
42363         "Macau (澳門)",
42364         "mo",
42365         "853"
42366       ],
42367       [
42368         "Macedonia (FYROM) (Македонија)",
42369         "mk",
42370         "389"
42371       ],
42372       [
42373         "Madagascar (Madagasikara)",
42374         "mg",
42375         "261"
42376       ],
42377       [
42378         "Malawi",
42379         "mw",
42380         "265"
42381       ],
42382       [
42383         "Malaysia",
42384         "my",
42385         "60"
42386       ],
42387       [
42388         "Maldives",
42389         "mv",
42390         "960"
42391       ],
42392       [
42393         "Mali",
42394         "ml",
42395         "223"
42396       ],
42397       [
42398         "Malta",
42399         "mt",
42400         "356"
42401       ],
42402       [
42403         "Marshall Islands",
42404         "mh",
42405         "692"
42406       ],
42407       [
42408         "Martinique",
42409         "mq",
42410         "596"
42411       ],
42412       [
42413         "Mauritania (‫موريتانيا‬‎)",
42414         "mr",
42415         "222"
42416       ],
42417       [
42418         "Mauritius (Moris)",
42419         "mu",
42420         "230"
42421       ],
42422       [
42423         "Mayotte",
42424         "yt",
42425         "262",
42426         1
42427       ],
42428       [
42429         "Mexico (México)",
42430         "mx",
42431         "52"
42432       ],
42433       [
42434         "Micronesia",
42435         "fm",
42436         "691"
42437       ],
42438       [
42439         "Moldova (Republica Moldova)",
42440         "md",
42441         "373"
42442       ],
42443       [
42444         "Monaco",
42445         "mc",
42446         "377"
42447       ],
42448       [
42449         "Mongolia (Монгол)",
42450         "mn",
42451         "976"
42452       ],
42453       [
42454         "Montenegro (Crna Gora)",
42455         "me",
42456         "382"
42457       ],
42458       [
42459         "Montserrat",
42460         "ms",
42461         "1664"
42462       ],
42463       [
42464         "Morocco (‫المغرب‬‎)",
42465         "ma",
42466         "212",
42467         0
42468       ],
42469       [
42470         "Mozambique (Moçambique)",
42471         "mz",
42472         "258"
42473       ],
42474       [
42475         "Myanmar (Burma) (မြန်မာ)",
42476         "mm",
42477         "95"
42478       ],
42479       [
42480         "Namibia (Namibië)",
42481         "na",
42482         "264"
42483       ],
42484       [
42485         "Nauru",
42486         "nr",
42487         "674"
42488       ],
42489       [
42490         "Nepal (नेपाल)",
42491         "np",
42492         "977"
42493       ],
42494       [
42495         "Netherlands (Nederland)",
42496         "nl",
42497         "31"
42498       ],
42499       [
42500         "New Caledonia (Nouvelle-Calédonie)",
42501         "nc",
42502         "687"
42503       ],
42504       [
42505         "New Zealand",
42506         "nz",
42507         "64"
42508       ],
42509       [
42510         "Nicaragua",
42511         "ni",
42512         "505"
42513       ],
42514       [
42515         "Niger (Nijar)",
42516         "ne",
42517         "227"
42518       ],
42519       [
42520         "Nigeria",
42521         "ng",
42522         "234"
42523       ],
42524       [
42525         "Niue",
42526         "nu",
42527         "683"
42528       ],
42529       [
42530         "Norfolk Island",
42531         "nf",
42532         "672"
42533       ],
42534       [
42535         "North Korea (조선 민주주의 인민 공화국)",
42536         "kp",
42537         "850"
42538       ],
42539       [
42540         "Northern Mariana Islands",
42541         "mp",
42542         "1670"
42543       ],
42544       [
42545         "Norway (Norge)",
42546         "no",
42547         "47",
42548         0
42549       ],
42550       [
42551         "Oman (‫عُمان‬‎)",
42552         "om",
42553         "968"
42554       ],
42555       [
42556         "Pakistan (‫پاکستان‬‎)",
42557         "pk",
42558         "92"
42559       ],
42560       [
42561         "Palau",
42562         "pw",
42563         "680"
42564       ],
42565       [
42566         "Palestine (‫فلسطين‬‎)",
42567         "ps",
42568         "970"
42569       ],
42570       [
42571         "Panama (Panamá)",
42572         "pa",
42573         "507"
42574       ],
42575       [
42576         "Papua New Guinea",
42577         "pg",
42578         "675"
42579       ],
42580       [
42581         "Paraguay",
42582         "py",
42583         "595"
42584       ],
42585       [
42586         "Peru (Perú)",
42587         "pe",
42588         "51"
42589       ],
42590       [
42591         "Philippines",
42592         "ph",
42593         "63"
42594       ],
42595       [
42596         "Poland (Polska)",
42597         "pl",
42598         "48"
42599       ],
42600       [
42601         "Portugal",
42602         "pt",
42603         "351"
42604       ],
42605       [
42606         "Puerto Rico",
42607         "pr",
42608         "1",
42609         3,
42610         ["787", "939"]
42611       ],
42612       [
42613         "Qatar (‫قطر‬‎)",
42614         "qa",
42615         "974"
42616       ],
42617       [
42618         "Réunion (La Réunion)",
42619         "re",
42620         "262",
42621         0
42622       ],
42623       [
42624         "Romania (România)",
42625         "ro",
42626         "40"
42627       ],
42628       [
42629         "Russia (Россия)",
42630         "ru",
42631         "7",
42632         0
42633       ],
42634       [
42635         "Rwanda",
42636         "rw",
42637         "250"
42638       ],
42639       [
42640         "Saint Barthélemy",
42641         "bl",
42642         "590",
42643         1
42644       ],
42645       [
42646         "Saint Helena",
42647         "sh",
42648         "290"
42649       ],
42650       [
42651         "Saint Kitts and Nevis",
42652         "kn",
42653         "1869"
42654       ],
42655       [
42656         "Saint Lucia",
42657         "lc",
42658         "1758"
42659       ],
42660       [
42661         "Saint Martin (Saint-Martin (partie française))",
42662         "mf",
42663         "590",
42664         2
42665       ],
42666       [
42667         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42668         "pm",
42669         "508"
42670       ],
42671       [
42672         "Saint Vincent and the Grenadines",
42673         "vc",
42674         "1784"
42675       ],
42676       [
42677         "Samoa",
42678         "ws",
42679         "685"
42680       ],
42681       [
42682         "San Marino",
42683         "sm",
42684         "378"
42685       ],
42686       [
42687         "São Tomé and Príncipe (São Tomé e Príncipe)",
42688         "st",
42689         "239"
42690       ],
42691       [
42692         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42693         "sa",
42694         "966"
42695       ],
42696       [
42697         "Senegal (Sénégal)",
42698         "sn",
42699         "221"
42700       ],
42701       [
42702         "Serbia (Србија)",
42703         "rs",
42704         "381"
42705       ],
42706       [
42707         "Seychelles",
42708         "sc",
42709         "248"
42710       ],
42711       [
42712         "Sierra Leone",
42713         "sl",
42714         "232"
42715       ],
42716       [
42717         "Singapore",
42718         "sg",
42719         "65"
42720       ],
42721       [
42722         "Sint Maarten",
42723         "sx",
42724         "1721"
42725       ],
42726       [
42727         "Slovakia (Slovensko)",
42728         "sk",
42729         "421"
42730       ],
42731       [
42732         "Slovenia (Slovenija)",
42733         "si",
42734         "386"
42735       ],
42736       [
42737         "Solomon Islands",
42738         "sb",
42739         "677"
42740       ],
42741       [
42742         "Somalia (Soomaaliya)",
42743         "so",
42744         "252"
42745       ],
42746       [
42747         "South Africa",
42748         "za",
42749         "27"
42750       ],
42751       [
42752         "South Korea (대한민국)",
42753         "kr",
42754         "82"
42755       ],
42756       [
42757         "South Sudan (‫جنوب السودان‬‎)",
42758         "ss",
42759         "211"
42760       ],
42761       [
42762         "Spain (España)",
42763         "es",
42764         "34"
42765       ],
42766       [
42767         "Sri Lanka (ශ්‍රී ලංකාව)",
42768         "lk",
42769         "94"
42770       ],
42771       [
42772         "Sudan (‫السودان‬‎)",
42773         "sd",
42774         "249"
42775       ],
42776       [
42777         "Suriname",
42778         "sr",
42779         "597"
42780       ],
42781       [
42782         "Svalbard and Jan Mayen",
42783         "sj",
42784         "47",
42785         1
42786       ],
42787       [
42788         "Swaziland",
42789         "sz",
42790         "268"
42791       ],
42792       [
42793         "Sweden (Sverige)",
42794         "se",
42795         "46"
42796       ],
42797       [
42798         "Switzerland (Schweiz)",
42799         "ch",
42800         "41"
42801       ],
42802       [
42803         "Syria (‫سوريا‬‎)",
42804         "sy",
42805         "963"
42806       ],
42807       [
42808         "Taiwan (台灣)",
42809         "tw",
42810         "886"
42811       ],
42812       [
42813         "Tajikistan",
42814         "tj",
42815         "992"
42816       ],
42817       [
42818         "Tanzania",
42819         "tz",
42820         "255"
42821       ],
42822       [
42823         "Thailand (ไทย)",
42824         "th",
42825         "66"
42826       ],
42827       [
42828         "Timor-Leste",
42829         "tl",
42830         "670"
42831       ],
42832       [
42833         "Togo",
42834         "tg",
42835         "228"
42836       ],
42837       [
42838         "Tokelau",
42839         "tk",
42840         "690"
42841       ],
42842       [
42843         "Tonga",
42844         "to",
42845         "676"
42846       ],
42847       [
42848         "Trinidad and Tobago",
42849         "tt",
42850         "1868"
42851       ],
42852       [
42853         "Tunisia (‫تونس‬‎)",
42854         "tn",
42855         "216"
42856       ],
42857       [
42858         "Turkey (Türkiye)",
42859         "tr",
42860         "90"
42861       ],
42862       [
42863         "Turkmenistan",
42864         "tm",
42865         "993"
42866       ],
42867       [
42868         "Turks and Caicos Islands",
42869         "tc",
42870         "1649"
42871       ],
42872       [
42873         "Tuvalu",
42874         "tv",
42875         "688"
42876       ],
42877       [
42878         "U.S. Virgin Islands",
42879         "vi",
42880         "1340"
42881       ],
42882       [
42883         "Uganda",
42884         "ug",
42885         "256"
42886       ],
42887       [
42888         "Ukraine (Україна)",
42889         "ua",
42890         "380"
42891       ],
42892       [
42893         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42894         "ae",
42895         "971"
42896       ],
42897       [
42898         "United Kingdom",
42899         "gb",
42900         "44",
42901         0
42902       ],
42903       [
42904         "United States",
42905         "us",
42906         "1",
42907         0
42908       ],
42909       [
42910         "Uruguay",
42911         "uy",
42912         "598"
42913       ],
42914       [
42915         "Uzbekistan (Oʻzbekiston)",
42916         "uz",
42917         "998"
42918       ],
42919       [
42920         "Vanuatu",
42921         "vu",
42922         "678"
42923       ],
42924       [
42925         "Vatican City (Città del Vaticano)",
42926         "va",
42927         "39",
42928         1
42929       ],
42930       [
42931         "Venezuela",
42932         "ve",
42933         "58"
42934       ],
42935       [
42936         "Vietnam (Việt Nam)",
42937         "vn",
42938         "84"
42939       ],
42940       [
42941         "Wallis and Futuna (Wallis-et-Futuna)",
42942         "wf",
42943         "681"
42944       ],
42945       [
42946         "Western Sahara (‫الصحراء الغربية‬‎)",
42947         "eh",
42948         "212",
42949         1
42950       ],
42951       [
42952         "Yemen (‫اليمن‬‎)",
42953         "ye",
42954         "967"
42955       ],
42956       [
42957         "Zambia",
42958         "zm",
42959         "260"
42960       ],
42961       [
42962         "Zimbabwe",
42963         "zw",
42964         "263"
42965       ],
42966       [
42967         "Åland Islands",
42968         "ax",
42969         "358",
42970         1
42971       ]
42972   ];
42973   
42974   return d;
42975 }/**
42976 *    This script refer to:
42977 *    Title: International Telephone Input
42978 *    Author: Jack O'Connor
42979 *    Code version:  v12.1.12
42980 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42981 **/
42982
42983 /**
42984  * @class Roo.bootstrap.PhoneInput
42985  * @extends Roo.bootstrap.TriggerField
42986  * An input with International dial-code selection
42987  
42988  * @cfg {String} defaultDialCode default '+852'
42989  * @cfg {Array} preferedCountries default []
42990   
42991  * @constructor
42992  * Create a new PhoneInput.
42993  * @param {Object} config Configuration options
42994  */
42995
42996 Roo.bootstrap.PhoneInput = function(config) {
42997     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42998 };
42999
43000 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43001         
43002         listWidth: undefined,
43003         
43004         selectedClass: 'active',
43005         
43006         invalidClass : "has-warning",
43007         
43008         validClass: 'has-success',
43009         
43010         allowed: '0123456789',
43011         
43012         max_length: 15,
43013         
43014         /**
43015          * @cfg {String} defaultDialCode The default dial code when initializing the input
43016          */
43017         defaultDialCode: '+852',
43018         
43019         /**
43020          * @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
43021          */
43022         preferedCountries: false,
43023         
43024         getAutoCreate : function()
43025         {
43026             var data = Roo.bootstrap.PhoneInputData();
43027             var align = this.labelAlign || this.parentLabelAlign();
43028             var id = Roo.id();
43029             
43030             this.allCountries = [];
43031             this.dialCodeMapping = [];
43032             
43033             for (var i = 0; i < data.length; i++) {
43034               var c = data[i];
43035               this.allCountries[i] = {
43036                 name: c[0],
43037                 iso2: c[1],
43038                 dialCode: c[2],
43039                 priority: c[3] || 0,
43040                 areaCodes: c[4] || null
43041               };
43042               this.dialCodeMapping[c[2]] = {
43043                   name: c[0],
43044                   iso2: c[1],
43045                   priority: c[3] || 0,
43046                   areaCodes: c[4] || null
43047               };
43048             }
43049             
43050             var cfg = {
43051                 cls: 'form-group',
43052                 cn: []
43053             };
43054             
43055             var input =  {
43056                 tag: 'input',
43057                 id : id,
43058                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43059                 maxlength: this.max_length,
43060                 cls : 'form-control tel-input',
43061                 autocomplete: 'new-password'
43062             };
43063             
43064             var hiddenInput = {
43065                 tag: 'input',
43066                 type: 'hidden',
43067                 cls: 'hidden-tel-input'
43068             };
43069             
43070             if (this.name) {
43071                 hiddenInput.name = this.name;
43072             }
43073             
43074             if (this.disabled) {
43075                 input.disabled = true;
43076             }
43077             
43078             var flag_container = {
43079                 tag: 'div',
43080                 cls: 'flag-box',
43081                 cn: [
43082                     {
43083                         tag: 'div',
43084                         cls: 'flag'
43085                     },
43086                     {
43087                         tag: 'div',
43088                         cls: 'caret'
43089                     }
43090                 ]
43091             };
43092             
43093             var box = {
43094                 tag: 'div',
43095                 cls: this.hasFeedback ? 'has-feedback' : '',
43096                 cn: [
43097                     hiddenInput,
43098                     input,
43099                     {
43100                         tag: 'input',
43101                         cls: 'dial-code-holder',
43102                         disabled: true
43103                     }
43104                 ]
43105             };
43106             
43107             var container = {
43108                 cls: 'roo-select2-container input-group',
43109                 cn: [
43110                     flag_container,
43111                     box
43112                 ]
43113             };
43114             
43115             if (this.fieldLabel.length) {
43116                 var indicator = {
43117                     tag: 'i',
43118                     tooltip: 'This field is required'
43119                 };
43120                 
43121                 var label = {
43122                     tag: 'label',
43123                     'for':  id,
43124                     cls: 'control-label',
43125                     cn: []
43126                 };
43127                 
43128                 var label_text = {
43129                     tag: 'span',
43130                     html: this.fieldLabel
43131                 };
43132                 
43133                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43134                 label.cn = [
43135                     indicator,
43136                     label_text
43137                 ];
43138                 
43139                 if(this.indicatorpos == 'right') {
43140                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43141                     label.cn = [
43142                         label_text,
43143                         indicator
43144                     ];
43145                 }
43146                 
43147                 if(align == 'left') {
43148                     container = {
43149                         tag: 'div',
43150                         cn: [
43151                             container
43152                         ]
43153                     };
43154                     
43155                     if(this.labelWidth > 12){
43156                         label.style = "width: " + this.labelWidth + 'px';
43157                     }
43158                     if(this.labelWidth < 13 && this.labelmd == 0){
43159                         this.labelmd = this.labelWidth;
43160                     }
43161                     if(this.labellg > 0){
43162                         label.cls += ' col-lg-' + this.labellg;
43163                         input.cls += ' col-lg-' + (12 - this.labellg);
43164                     }
43165                     if(this.labelmd > 0){
43166                         label.cls += ' col-md-' + this.labelmd;
43167                         container.cls += ' col-md-' + (12 - this.labelmd);
43168                     }
43169                     if(this.labelsm > 0){
43170                         label.cls += ' col-sm-' + this.labelsm;
43171                         container.cls += ' col-sm-' + (12 - this.labelsm);
43172                     }
43173                     if(this.labelxs > 0){
43174                         label.cls += ' col-xs-' + this.labelxs;
43175                         container.cls += ' col-xs-' + (12 - this.labelxs);
43176                     }
43177                 }
43178             }
43179             
43180             cfg.cn = [
43181                 label,
43182                 container
43183             ];
43184             
43185             var settings = this;
43186             
43187             ['xs','sm','md','lg'].map(function(size){
43188                 if (settings[size]) {
43189                     cfg.cls += ' col-' + size + '-' + settings[size];
43190                 }
43191             });
43192             
43193             this.store = new Roo.data.Store({
43194                 proxy : new Roo.data.MemoryProxy({}),
43195                 reader : new Roo.data.JsonReader({
43196                     fields : [
43197                         {
43198                             'name' : 'name',
43199                             'type' : 'string'
43200                         },
43201                         {
43202                             'name' : 'iso2',
43203                             'type' : 'string'
43204                         },
43205                         {
43206                             'name' : 'dialCode',
43207                             'type' : 'string'
43208                         },
43209                         {
43210                             'name' : 'priority',
43211                             'type' : 'string'
43212                         },
43213                         {
43214                             'name' : 'areaCodes',
43215                             'type' : 'string'
43216                         }
43217                     ]
43218                 })
43219             });
43220             
43221             if(!this.preferedCountries) {
43222                 this.preferedCountries = [
43223                     'hk',
43224                     'gb',
43225                     'us'
43226                 ];
43227             }
43228             
43229             var p = this.preferedCountries.reverse();
43230             
43231             if(p) {
43232                 for (var i = 0; i < p.length; i++) {
43233                     for (var j = 0; j < this.allCountries.length; j++) {
43234                         if(this.allCountries[j].iso2 == p[i]) {
43235                             var t = this.allCountries[j];
43236                             this.allCountries.splice(j,1);
43237                             this.allCountries.unshift(t);
43238                         }
43239                     } 
43240                 }
43241             }
43242             
43243             this.store.proxy.data = {
43244                 success: true,
43245                 data: this.allCountries
43246             };
43247             
43248             return cfg;
43249         },
43250         
43251         initEvents : function()
43252         {
43253             this.createList();
43254             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43255             
43256             this.indicator = this.indicatorEl();
43257             this.flag = this.flagEl();
43258             this.dialCodeHolder = this.dialCodeHolderEl();
43259             
43260             this.trigger = this.el.select('div.flag-box',true).first();
43261             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43262             
43263             var _this = this;
43264             
43265             (function(){
43266                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43267                 _this.list.setWidth(lw);
43268             }).defer(100);
43269             
43270             this.list.on('mouseover', this.onViewOver, this);
43271             this.list.on('mousemove', this.onViewMove, this);
43272             this.inputEl().on("keyup", this.onKeyUp, this);
43273             this.inputEl().on("keypress", this.onKeyPress, this);
43274             
43275             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43276
43277             this.view = new Roo.View(this.list, this.tpl, {
43278                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43279             });
43280             
43281             this.view.on('click', this.onViewClick, this);
43282             this.setValue(this.defaultDialCode);
43283         },
43284         
43285         onTriggerClick : function(e)
43286         {
43287             Roo.log('trigger click');
43288             if(this.disabled){
43289                 return;
43290             }
43291             
43292             if(this.isExpanded()){
43293                 this.collapse();
43294                 this.hasFocus = false;
43295             }else {
43296                 this.store.load({});
43297                 this.hasFocus = true;
43298                 this.expand();
43299             }
43300         },
43301         
43302         isExpanded : function()
43303         {
43304             return this.list.isVisible();
43305         },
43306         
43307         collapse : function()
43308         {
43309             if(!this.isExpanded()){
43310                 return;
43311             }
43312             this.list.hide();
43313             Roo.get(document).un('mousedown', this.collapseIf, this);
43314             Roo.get(document).un('mousewheel', this.collapseIf, this);
43315             this.fireEvent('collapse', this);
43316             this.validate();
43317         },
43318         
43319         expand : function()
43320         {
43321             Roo.log('expand');
43322
43323             if(this.isExpanded() || !this.hasFocus){
43324                 return;
43325             }
43326             
43327             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43328             this.list.setWidth(lw);
43329             
43330             this.list.show();
43331             this.restrictHeight();
43332             
43333             Roo.get(document).on('mousedown', this.collapseIf, this);
43334             Roo.get(document).on('mousewheel', this.collapseIf, this);
43335             
43336             this.fireEvent('expand', this);
43337         },
43338         
43339         restrictHeight : function()
43340         {
43341             this.list.alignTo(this.inputEl(), this.listAlign);
43342             this.list.alignTo(this.inputEl(), this.listAlign);
43343         },
43344         
43345         onViewOver : function(e, t)
43346         {
43347             if(this.inKeyMode){
43348                 return;
43349             }
43350             var item = this.view.findItemFromChild(t);
43351             
43352             if(item){
43353                 var index = this.view.indexOf(item);
43354                 this.select(index, false);
43355             }
43356         },
43357
43358         // private
43359         onViewClick : function(view, doFocus, el, e)
43360         {
43361             var index = this.view.getSelectedIndexes()[0];
43362             
43363             var r = this.store.getAt(index);
43364             
43365             if(r){
43366                 this.onSelect(r, index);
43367             }
43368             if(doFocus !== false && !this.blockFocus){
43369                 this.inputEl().focus();
43370             }
43371         },
43372         
43373         onViewMove : function(e, t)
43374         {
43375             this.inKeyMode = false;
43376         },
43377         
43378         select : function(index, scrollIntoView)
43379         {
43380             this.selectedIndex = index;
43381             this.view.select(index);
43382             if(scrollIntoView !== false){
43383                 var el = this.view.getNode(index);
43384                 if(el){
43385                     this.list.scrollChildIntoView(el, false);
43386                 }
43387             }
43388         },
43389         
43390         createList : function()
43391         {
43392             this.list = Roo.get(document.body).createChild({
43393                 tag: 'ul',
43394                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43395                 style: 'display:none'
43396             });
43397             
43398             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43399         },
43400         
43401         collapseIf : function(e)
43402         {
43403             var in_combo  = e.within(this.el);
43404             var in_list =  e.within(this.list);
43405             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43406             
43407             if (in_combo || in_list || is_list) {
43408                 return;
43409             }
43410             this.collapse();
43411         },
43412         
43413         onSelect : function(record, index)
43414         {
43415             if(this.fireEvent('beforeselect', this, record, index) !== false){
43416                 
43417                 this.setFlagClass(record.data.iso2);
43418                 this.setDialCode(record.data.dialCode);
43419                 this.hasFocus = false;
43420                 this.collapse();
43421                 this.fireEvent('select', this, record, index);
43422             }
43423         },
43424         
43425         flagEl : function()
43426         {
43427             var flag = this.el.select('div.flag',true).first();
43428             if(!flag){
43429                 return false;
43430             }
43431             return flag;
43432         },
43433         
43434         dialCodeHolderEl : function()
43435         {
43436             var d = this.el.select('input.dial-code-holder',true).first();
43437             if(!d){
43438                 return false;
43439             }
43440             return d;
43441         },
43442         
43443         setDialCode : function(v)
43444         {
43445             this.dialCodeHolder.dom.value = '+'+v;
43446         },
43447         
43448         setFlagClass : function(n)
43449         {
43450             this.flag.dom.className = 'flag '+n;
43451         },
43452         
43453         getValue : function()
43454         {
43455             var v = this.inputEl().getValue();
43456             if(this.dialCodeHolder) {
43457                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43458             }
43459             return v;
43460         },
43461         
43462         setValue : function(v)
43463         {
43464             var d = this.getDialCode(v);
43465             
43466             //invalid dial code
43467             if(v.length == 0 || !d || d.length == 0) {
43468                 if(this.rendered){
43469                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43470                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43471                 }
43472                 return;
43473             }
43474             
43475             //valid dial code
43476             this.setFlagClass(this.dialCodeMapping[d].iso2);
43477             this.setDialCode(d);
43478             this.inputEl().dom.value = v.replace('+'+d,'');
43479             this.hiddenEl().dom.value = this.getValue();
43480             
43481             this.validate();
43482         },
43483         
43484         getDialCode : function(v)
43485         {
43486             v = v ||  '';
43487             
43488             if (v.length == 0) {
43489                 return this.dialCodeHolder.dom.value;
43490             }
43491             
43492             var dialCode = "";
43493             if (v.charAt(0) != "+") {
43494                 return false;
43495             }
43496             var numericChars = "";
43497             for (var i = 1; i < v.length; i++) {
43498               var c = v.charAt(i);
43499               if (!isNaN(c)) {
43500                 numericChars += c;
43501                 if (this.dialCodeMapping[numericChars]) {
43502                   dialCode = v.substr(1, i);
43503                 }
43504                 if (numericChars.length == 4) {
43505                   break;
43506                 }
43507               }
43508             }
43509             return dialCode;
43510         },
43511         
43512         reset : function()
43513         {
43514             this.setValue(this.defaultDialCode);
43515             this.validate();
43516         },
43517         
43518         hiddenEl : function()
43519         {
43520             return this.el.select('input.hidden-tel-input',true).first();
43521         },
43522         
43523         // after setting val
43524         onKeyUp : function(e){
43525             this.setValue(this.getValue());
43526         },
43527         
43528         onKeyPress : function(e){
43529             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43530                 e.stopEvent();
43531             }
43532         }
43533         
43534 });
43535 /**
43536  * @class Roo.bootstrap.MoneyField
43537  * @extends Roo.bootstrap.ComboBox
43538  * Bootstrap MoneyField class
43539  * 
43540  * @constructor
43541  * Create a new MoneyField.
43542  * @param {Object} config Configuration options
43543  */
43544
43545 Roo.bootstrap.MoneyField = function(config) {
43546     
43547     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43548     
43549 };
43550
43551 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43552     
43553     /**
43554      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43555      */
43556     allowDecimals : true,
43557     /**
43558      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43559      */
43560     decimalSeparator : ".",
43561     /**
43562      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43563      */
43564     decimalPrecision : 0,
43565     /**
43566      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43567      */
43568     allowNegative : true,
43569     /**
43570      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43571      */
43572     allowZero: true,
43573     /**
43574      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43575      */
43576     minValue : Number.NEGATIVE_INFINITY,
43577     /**
43578      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43579      */
43580     maxValue : Number.MAX_VALUE,
43581     /**
43582      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43583      */
43584     minText : "The minimum value for this field is {0}",
43585     /**
43586      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43587      */
43588     maxText : "The maximum value for this field is {0}",
43589     /**
43590      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43591      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43592      */
43593     nanText : "{0} is not a valid number",
43594     /**
43595      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43596      */
43597     castInt : true,
43598     /**
43599      * @cfg {String} defaults currency of the MoneyField
43600      * value should be in lkey
43601      */
43602     defaultCurrency : false,
43603     /**
43604      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43605      */
43606     thousandsDelimiter : false,
43607     /**
43608      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43609      */
43610     max_length: false,
43611     
43612     inputlg : 9,
43613     inputmd : 9,
43614     inputsm : 9,
43615     inputxs : 6,
43616     
43617     store : false,
43618     
43619     getAutoCreate : function()
43620     {
43621         var align = this.labelAlign || this.parentLabelAlign();
43622         
43623         var id = Roo.id();
43624
43625         var cfg = {
43626             cls: 'form-group',
43627             cn: []
43628         };
43629
43630         var input =  {
43631             tag: 'input',
43632             id : id,
43633             cls : 'form-control roo-money-amount-input',
43634             autocomplete: 'new-password'
43635         };
43636         
43637         var hiddenInput = {
43638             tag: 'input',
43639             type: 'hidden',
43640             id: Roo.id(),
43641             cls: 'hidden-number-input'
43642         };
43643         
43644         if(this.max_length) {
43645             input.maxlength = this.max_length; 
43646         }
43647         
43648         if (this.name) {
43649             hiddenInput.name = this.name;
43650         }
43651
43652         if (this.disabled) {
43653             input.disabled = true;
43654         }
43655
43656         var clg = 12 - this.inputlg;
43657         var cmd = 12 - this.inputmd;
43658         var csm = 12 - this.inputsm;
43659         var cxs = 12 - this.inputxs;
43660         
43661         var container = {
43662             tag : 'div',
43663             cls : 'row roo-money-field',
43664             cn : [
43665                 {
43666                     tag : 'div',
43667                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43668                     cn : [
43669                         {
43670                             tag : 'div',
43671                             cls: 'roo-select2-container input-group',
43672                             cn: [
43673                                 {
43674                                     tag : 'input',
43675                                     cls : 'form-control roo-money-currency-input',
43676                                     autocomplete: 'new-password',
43677                                     readOnly : 1,
43678                                     name : this.currencyName
43679                                 },
43680                                 {
43681                                     tag :'span',
43682                                     cls : 'input-group-addon',
43683                                     cn : [
43684                                         {
43685                                             tag: 'span',
43686                                             cls: 'caret'
43687                                         }
43688                                     ]
43689                                 }
43690                             ]
43691                         }
43692                     ]
43693                 },
43694                 {
43695                     tag : 'div',
43696                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43697                     cn : [
43698                         {
43699                             tag: 'div',
43700                             cls: this.hasFeedback ? 'has-feedback' : '',
43701                             cn: [
43702                                 input
43703                             ]
43704                         }
43705                     ]
43706                 }
43707             ]
43708             
43709         };
43710         
43711         if (this.fieldLabel.length) {
43712             var indicator = {
43713                 tag: 'i',
43714                 tooltip: 'This field is required'
43715             };
43716
43717             var label = {
43718                 tag: 'label',
43719                 'for':  id,
43720                 cls: 'control-label',
43721                 cn: []
43722             };
43723
43724             var label_text = {
43725                 tag: 'span',
43726                 html: this.fieldLabel
43727             };
43728
43729             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43730             label.cn = [
43731                 indicator,
43732                 label_text
43733             ];
43734
43735             if(this.indicatorpos == 'right') {
43736                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43737                 label.cn = [
43738                     label_text,
43739                     indicator
43740                 ];
43741             }
43742
43743             if(align == 'left') {
43744                 container = {
43745                     tag: 'div',
43746                     cn: [
43747                         container
43748                     ]
43749                 };
43750
43751                 if(this.labelWidth > 12){
43752                     label.style = "width: " + this.labelWidth + 'px';
43753                 }
43754                 if(this.labelWidth < 13 && this.labelmd == 0){
43755                     this.labelmd = this.labelWidth;
43756                 }
43757                 if(this.labellg > 0){
43758                     label.cls += ' col-lg-' + this.labellg;
43759                     input.cls += ' col-lg-' + (12 - this.labellg);
43760                 }
43761                 if(this.labelmd > 0){
43762                     label.cls += ' col-md-' + this.labelmd;
43763                     container.cls += ' col-md-' + (12 - this.labelmd);
43764                 }
43765                 if(this.labelsm > 0){
43766                     label.cls += ' col-sm-' + this.labelsm;
43767                     container.cls += ' col-sm-' + (12 - this.labelsm);
43768                 }
43769                 if(this.labelxs > 0){
43770                     label.cls += ' col-xs-' + this.labelxs;
43771                     container.cls += ' col-xs-' + (12 - this.labelxs);
43772                 }
43773             }
43774         }
43775
43776         cfg.cn = [
43777             label,
43778             container,
43779             hiddenInput
43780         ];
43781         
43782         var settings = this;
43783
43784         ['xs','sm','md','lg'].map(function(size){
43785             if (settings[size]) {
43786                 cfg.cls += ' col-' + size + '-' + settings[size];
43787             }
43788         });
43789         
43790         return cfg;
43791     },
43792     
43793     initEvents : function()
43794     {
43795         this.indicator = this.indicatorEl();
43796         
43797         this.initCurrencyEvent();
43798         
43799         this.initNumberEvent();
43800     },
43801     
43802     initCurrencyEvent : function()
43803     {
43804         if (!this.store) {
43805             throw "can not find store for combo";
43806         }
43807         
43808         this.store = Roo.factory(this.store, Roo.data);
43809         this.store.parent = this;
43810         
43811         this.createList();
43812         
43813         this.triggerEl = this.el.select('.input-group-addon', true).first();
43814         
43815         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43816         
43817         var _this = this;
43818         
43819         (function(){
43820             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43821             _this.list.setWidth(lw);
43822         }).defer(100);
43823         
43824         this.list.on('mouseover', this.onViewOver, this);
43825         this.list.on('mousemove', this.onViewMove, this);
43826         this.list.on('scroll', this.onViewScroll, this);
43827         
43828         if(!this.tpl){
43829             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43830         }
43831         
43832         this.view = new Roo.View(this.list, this.tpl, {
43833             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43834         });
43835         
43836         this.view.on('click', this.onViewClick, this);
43837         
43838         this.store.on('beforeload', this.onBeforeLoad, this);
43839         this.store.on('load', this.onLoad, this);
43840         this.store.on('loadexception', this.onLoadException, this);
43841         
43842         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43843             "up" : function(e){
43844                 this.inKeyMode = true;
43845                 this.selectPrev();
43846             },
43847
43848             "down" : function(e){
43849                 if(!this.isExpanded()){
43850                     this.onTriggerClick();
43851                 }else{
43852                     this.inKeyMode = true;
43853                     this.selectNext();
43854                 }
43855             },
43856
43857             "enter" : function(e){
43858                 this.collapse();
43859                 
43860                 if(this.fireEvent("specialkey", this, e)){
43861                     this.onViewClick(false);
43862                 }
43863                 
43864                 return true;
43865             },
43866
43867             "esc" : function(e){
43868                 this.collapse();
43869             },
43870
43871             "tab" : function(e){
43872                 this.collapse();
43873                 
43874                 if(this.fireEvent("specialkey", this, e)){
43875                     this.onViewClick(false);
43876                 }
43877                 
43878                 return true;
43879             },
43880
43881             scope : this,
43882
43883             doRelay : function(foo, bar, hname){
43884                 if(hname == 'down' || this.scope.isExpanded()){
43885                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43886                 }
43887                 return true;
43888             },
43889
43890             forceKeyDown: true
43891         });
43892         
43893         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43894         
43895     },
43896     
43897     initNumberEvent : function(e)
43898     {
43899         this.inputEl().on("keydown" , this.fireKey,  this);
43900         this.inputEl().on("focus", this.onFocus,  this);
43901         this.inputEl().on("blur", this.onBlur,  this);
43902         
43903         this.inputEl().relayEvent('keyup', this);
43904         
43905         if(this.indicator){
43906             this.indicator.addClass('invisible');
43907         }
43908  
43909         this.originalValue = this.getValue();
43910         
43911         if(this.validationEvent == 'keyup'){
43912             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43913             this.inputEl().on('keyup', this.filterValidation, this);
43914         }
43915         else if(this.validationEvent !== false){
43916             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43917         }
43918         
43919         if(this.selectOnFocus){
43920             this.on("focus", this.preFocus, this);
43921             
43922         }
43923         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43924             this.inputEl().on("keypress", this.filterKeys, this);
43925         } else {
43926             this.inputEl().relayEvent('keypress', this);
43927         }
43928         
43929         var allowed = "0123456789";
43930         
43931         if(this.allowDecimals){
43932             allowed += this.decimalSeparator;
43933         }
43934         
43935         if(this.allowNegative){
43936             allowed += "-";
43937         }
43938         
43939         if(this.thousandsDelimiter) {
43940             allowed += ",";
43941         }
43942         
43943         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43944         
43945         var keyPress = function(e){
43946             
43947             var k = e.getKey();
43948             
43949             var c = e.getCharCode();
43950             
43951             if(
43952                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43953                     allowed.indexOf(String.fromCharCode(c)) === -1
43954             ){
43955                 e.stopEvent();
43956                 return;
43957             }
43958             
43959             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43960                 return;
43961             }
43962             
43963             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43964                 e.stopEvent();
43965             }
43966         };
43967         
43968         this.inputEl().on("keypress", keyPress, this);
43969         
43970     },
43971     
43972     onTriggerClick : function(e)
43973     {   
43974         if(this.disabled){
43975             return;
43976         }
43977         
43978         this.page = 0;
43979         this.loadNext = false;
43980         
43981         if(this.isExpanded()){
43982             this.collapse();
43983             return;
43984         }
43985         
43986         this.hasFocus = true;
43987         
43988         if(this.triggerAction == 'all') {
43989             this.doQuery(this.allQuery, true);
43990             return;
43991         }
43992         
43993         this.doQuery(this.getRawValue());
43994     },
43995     
43996     getCurrency : function()
43997     {   
43998         var v = this.currencyEl().getValue();
43999         
44000         return v;
44001     },
44002     
44003     restrictHeight : function()
44004     {
44005         this.list.alignTo(this.currencyEl(), this.listAlign);
44006         this.list.alignTo(this.currencyEl(), this.listAlign);
44007     },
44008     
44009     onViewClick : function(view, doFocus, el, e)
44010     {
44011         var index = this.view.getSelectedIndexes()[0];
44012         
44013         var r = this.store.getAt(index);
44014         
44015         if(r){
44016             this.onSelect(r, index);
44017         }
44018     },
44019     
44020     onSelect : function(record, index){
44021         
44022         if(this.fireEvent('beforeselect', this, record, index) !== false){
44023         
44024             this.setFromCurrencyData(index > -1 ? record.data : false);
44025             
44026             this.collapse();
44027             
44028             this.fireEvent('select', this, record, index);
44029         }
44030     },
44031     
44032     setFromCurrencyData : function(o)
44033     {
44034         var currency = '';
44035         
44036         this.lastCurrency = o;
44037         
44038         if (this.currencyField) {
44039             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44040         } else {
44041             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44042         }
44043         
44044         this.lastSelectionText = currency;
44045         
44046         //setting default currency
44047         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44048             this.setCurrency(this.defaultCurrency);
44049             return;
44050         }
44051         
44052         this.setCurrency(currency);
44053     },
44054     
44055     setFromData : function(o)
44056     {
44057         var c = {};
44058         
44059         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44060         
44061         this.setFromCurrencyData(c);
44062         
44063         var value = '';
44064         
44065         if (this.name) {
44066             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44067         } else {
44068             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44069         }
44070         
44071         this.setValue(value);
44072         
44073     },
44074     
44075     setCurrency : function(v)
44076     {   
44077         this.currencyValue = v;
44078         
44079         if(this.rendered){
44080             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44081             this.validate();
44082         }
44083     },
44084     
44085     setValue : function(v)
44086     {
44087         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44088         
44089         this.value = v;
44090         
44091         if(this.rendered){
44092             
44093             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44094             
44095             this.inputEl().dom.value = (v == '') ? '' :
44096                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44097             
44098             if(!this.allowZero && v === '0') {
44099                 this.hiddenEl().dom.value = '';
44100                 this.inputEl().dom.value = '';
44101             }
44102             
44103             this.validate();
44104         }
44105     },
44106     
44107     getRawValue : function()
44108     {
44109         var v = this.inputEl().getValue();
44110         
44111         return v;
44112     },
44113     
44114     getValue : function()
44115     {
44116         return this.fixPrecision(this.parseValue(this.getRawValue()));
44117     },
44118     
44119     parseValue : function(value)
44120     {
44121         if(this.thousandsDelimiter) {
44122             value += "";
44123             r = new RegExp(",", "g");
44124             value = value.replace(r, "");
44125         }
44126         
44127         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44128         return isNaN(value) ? '' : value;
44129         
44130     },
44131     
44132     fixPrecision : function(value)
44133     {
44134         if(this.thousandsDelimiter) {
44135             value += "";
44136             r = new RegExp(",", "g");
44137             value = value.replace(r, "");
44138         }
44139         
44140         var nan = isNaN(value);
44141         
44142         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44143             return nan ? '' : value;
44144         }
44145         return parseFloat(value).toFixed(this.decimalPrecision);
44146     },
44147     
44148     decimalPrecisionFcn : function(v)
44149     {
44150         return Math.floor(v);
44151     },
44152     
44153     validateValue : function(value)
44154     {
44155         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44156             return false;
44157         }
44158         
44159         var num = this.parseValue(value);
44160         
44161         if(isNaN(num)){
44162             this.markInvalid(String.format(this.nanText, value));
44163             return false;
44164         }
44165         
44166         if(num < this.minValue){
44167             this.markInvalid(String.format(this.minText, this.minValue));
44168             return false;
44169         }
44170         
44171         if(num > this.maxValue){
44172             this.markInvalid(String.format(this.maxText, this.maxValue));
44173             return false;
44174         }
44175         
44176         return true;
44177     },
44178     
44179     validate : function()
44180     {
44181         if(this.disabled || this.allowBlank){
44182             this.markValid();
44183             return true;
44184         }
44185         
44186         var currency = this.getCurrency();
44187         
44188         if(this.validateValue(this.getRawValue()) && currency.length){
44189             this.markValid();
44190             return true;
44191         }
44192         
44193         this.markInvalid();
44194         return false;
44195     },
44196     
44197     getName: function()
44198     {
44199         return this.name;
44200     },
44201     
44202     beforeBlur : function()
44203     {
44204         if(!this.castInt){
44205             return;
44206         }
44207         
44208         var v = this.parseValue(this.getRawValue());
44209         
44210         if(v || v == 0){
44211             this.setValue(v);
44212         }
44213     },
44214     
44215     onBlur : function()
44216     {
44217         this.beforeBlur();
44218         
44219         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44220             //this.el.removeClass(this.focusClass);
44221         }
44222         
44223         this.hasFocus = false;
44224         
44225         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44226             this.validate();
44227         }
44228         
44229         var v = this.getValue();
44230         
44231         if(String(v) !== String(this.startValue)){
44232             this.fireEvent('change', this, v, this.startValue);
44233         }
44234         
44235         this.fireEvent("blur", this);
44236     },
44237     
44238     inputEl : function()
44239     {
44240         return this.el.select('.roo-money-amount-input', true).first();
44241     },
44242     
44243     currencyEl : function()
44244     {
44245         return this.el.select('.roo-money-currency-input', true).first();
44246     },
44247     
44248     hiddenEl : function()
44249     {
44250         return this.el.select('input.hidden-number-input',true).first();
44251     }
44252     
44253 });/**
44254  * @class Roo.bootstrap.BezierSignature
44255  * @extends Roo.bootstrap.Component
44256  * Bootstrap BezierSignature class
44257  * This script refer to:
44258  *    Title: Signature Pad
44259  *    Author: szimek
44260  *    Availability: https://github.com/szimek/signature_pad
44261  *
44262  * @constructor
44263  * Create a new BezierSignature
44264  * @param {Object} config The config object
44265  */
44266
44267 Roo.bootstrap.BezierSignature = function(config){
44268     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44269     this.addEvents({
44270         "resize" : true
44271     });
44272 };
44273
44274 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44275 {
44276      
44277     curve_data: [],
44278     
44279     is_empty: true,
44280     
44281     mouse_btn_down: true,
44282     
44283     /**
44284      * @cfg {int} canvas height
44285      */
44286     canvas_height: '200px',
44287     
44288     /**
44289      * @cfg {float|function} Radius of a single dot.
44290      */ 
44291     dot_size: false,
44292     
44293     /**
44294      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44295      */
44296     min_width: 0.5,
44297     
44298     /**
44299      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44300      */
44301     max_width: 2.5,
44302     
44303     /**
44304      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44305      */
44306     throttle: 16,
44307     
44308     /**
44309      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44310      */
44311     min_distance: 5,
44312     
44313     /**
44314      * @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.
44315      */
44316     bg_color: 'rgba(0, 0, 0, 0)',
44317     
44318     /**
44319      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44320      */
44321     dot_color: 'black',
44322     
44323     /**
44324      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44325      */ 
44326     velocity_filter_weight: 0.7,
44327     
44328     /**
44329      * @cfg {function} Callback when stroke begin. 
44330      */
44331     onBegin: false,
44332     
44333     /**
44334      * @cfg {function} Callback when stroke end.
44335      */
44336     onEnd: false,
44337     
44338     getAutoCreate : function()
44339     {
44340         var cls = 'roo-signature column';
44341         
44342         if(this.cls){
44343             cls += ' ' + this.cls;
44344         }
44345         
44346         var col_sizes = [
44347             'lg',
44348             'md',
44349             'sm',
44350             'xs'
44351         ];
44352         
44353         for(var i = 0; i < col_sizes.length; i++) {
44354             if(this[col_sizes[i]]) {
44355                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44356             }
44357         }
44358         
44359         var cfg = {
44360             tag: 'div',
44361             cls: cls,
44362             cn: [
44363                 {
44364                     tag: 'div',
44365                     cls: 'roo-signature-body',
44366                     cn: [
44367                         {
44368                             tag: 'canvas',
44369                             cls: 'roo-signature-body-canvas',
44370                             height: this.canvas_height,
44371                             width: this.canvas_width
44372                         }
44373                     ]
44374                 },
44375                 {
44376                     tag: 'input',
44377                     type: 'file',
44378                     style: 'display: none'
44379                 }
44380             ]
44381         };
44382         
44383         return cfg;
44384     },
44385     
44386     initEvents: function() 
44387     {
44388         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44389         
44390         var canvas = this.canvasEl();
44391         
44392         // mouse && touch event swapping...
44393         canvas.dom.style.touchAction = 'none';
44394         canvas.dom.style.msTouchAction = 'none';
44395         
44396         this.mouse_btn_down = false;
44397         canvas.on('mousedown', this._handleMouseDown, this);
44398         canvas.on('mousemove', this._handleMouseMove, this);
44399         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44400         
44401         if (window.PointerEvent) {
44402             canvas.on('pointerdown', this._handleMouseDown, this);
44403             canvas.on('pointermove', this._handleMouseMove, this);
44404             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44405         }
44406         
44407         if ('ontouchstart' in window) {
44408             canvas.on('touchstart', this._handleTouchStart, this);
44409             canvas.on('touchmove', this._handleTouchMove, this);
44410             canvas.on('touchend', this._handleTouchEnd, this);
44411         }
44412         
44413         Roo.EventManager.onWindowResize(this.resize, this, true);
44414         
44415         // file input event
44416         this.fileEl().on('change', this.uploadImage, this);
44417         
44418         this.clear();
44419         
44420         this.resize();
44421     },
44422     
44423     resize: function(){
44424         
44425         var canvas = this.canvasEl().dom;
44426         var ctx = this.canvasElCtx();
44427         var img_data = false;
44428         
44429         if(canvas.width > 0) {
44430             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44431         }
44432         // setting canvas width will clean img data
44433         canvas.width = 0;
44434         
44435         var style = window.getComputedStyle ? 
44436             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44437             
44438         var padding_left = parseInt(style.paddingLeft) || 0;
44439         var padding_right = parseInt(style.paddingRight) || 0;
44440         
44441         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44442         
44443         if(img_data) {
44444             ctx.putImageData(img_data, 0, 0);
44445         }
44446     },
44447     
44448     _handleMouseDown: function(e)
44449     {
44450         if (e.browserEvent.which === 1) {
44451             this.mouse_btn_down = true;
44452             this.strokeBegin(e);
44453         }
44454     },
44455     
44456     _handleMouseMove: function (e)
44457     {
44458         if (this.mouse_btn_down) {
44459             this.strokeMoveUpdate(e);
44460         }
44461     },
44462     
44463     _handleMouseUp: function (e)
44464     {
44465         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44466             this.mouse_btn_down = false;
44467             this.strokeEnd(e);
44468         }
44469     },
44470     
44471     _handleTouchStart: function (e) {
44472         
44473         e.preventDefault();
44474         if (e.browserEvent.targetTouches.length === 1) {
44475             // var touch = e.browserEvent.changedTouches[0];
44476             // this.strokeBegin(touch);
44477             
44478              this.strokeBegin(e); // assume e catching the correct xy...
44479         }
44480     },
44481     
44482     _handleTouchMove: function (e) {
44483         e.preventDefault();
44484         // var touch = event.targetTouches[0];
44485         // _this._strokeMoveUpdate(touch);
44486         this.strokeMoveUpdate(e);
44487     },
44488     
44489     _handleTouchEnd: function (e) {
44490         var wasCanvasTouched = e.target === this.canvasEl().dom;
44491         if (wasCanvasTouched) {
44492             e.preventDefault();
44493             // var touch = event.changedTouches[0];
44494             // _this._strokeEnd(touch);
44495             this.strokeEnd(e);
44496         }
44497     },
44498     
44499     reset: function () {
44500         this._lastPoints = [];
44501         this._lastVelocity = 0;
44502         this._lastWidth = (this.min_width + this.max_width) / 2;
44503         this.canvasElCtx().fillStyle = this.dot_color;
44504     },
44505     
44506     strokeMoveUpdate: function(e)
44507     {
44508         this.strokeUpdate(e);
44509         
44510         if (this.throttle) {
44511             this.throttleStroke(this.strokeUpdate, this.throttle);
44512         }
44513         else {
44514             this.strokeUpdate(e);
44515         }
44516     },
44517     
44518     strokeBegin: function(e)
44519     {
44520         var newPointGroup = {
44521             color: this.dot_color,
44522             points: []
44523         };
44524         
44525         if (typeof this.onBegin === 'function') {
44526             this.onBegin(e);
44527         }
44528         
44529         this.curve_data.push(newPointGroup);
44530         this.reset();
44531         this.strokeUpdate(e);
44532     },
44533     
44534     strokeUpdate: function(e)
44535     {
44536         var rect = this.canvasEl().dom.getBoundingClientRect();
44537         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44538         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44539         var lastPoints = lastPointGroup.points;
44540         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44541         var isLastPointTooClose = lastPoint
44542             ? point.distanceTo(lastPoint) <= this.min_distance
44543             : false;
44544         var color = lastPointGroup.color;
44545         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44546             var curve = this.addPoint(point);
44547             if (!lastPoint) {
44548                 this.drawDot({color: color, point: point});
44549             }
44550             else if (curve) {
44551                 this.drawCurve({color: color, curve: curve});
44552             }
44553             lastPoints.push({
44554                 time: point.time,
44555                 x: point.x,
44556                 y: point.y
44557             });
44558         }
44559     },
44560     
44561     strokeEnd: function(e)
44562     {
44563         this.strokeUpdate(e);
44564         if (typeof this.onEnd === 'function') {
44565             this.onEnd(e);
44566         }
44567     },
44568     
44569     addPoint:  function (point) {
44570         var _lastPoints = this._lastPoints;
44571         _lastPoints.push(point);
44572         if (_lastPoints.length > 2) {
44573             if (_lastPoints.length === 3) {
44574                 _lastPoints.unshift(_lastPoints[0]);
44575             }
44576             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44577             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44578             _lastPoints.shift();
44579             return curve;
44580         }
44581         return null;
44582     },
44583     
44584     calculateCurveWidths: function (startPoint, endPoint) {
44585         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44586             (1 - this.velocity_filter_weight) * this._lastVelocity;
44587
44588         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44589         var widths = {
44590             end: newWidth,
44591             start: this._lastWidth
44592         };
44593         
44594         this._lastVelocity = velocity;
44595         this._lastWidth = newWidth;
44596         return widths;
44597     },
44598     
44599     drawDot: function (_a) {
44600         var color = _a.color, point = _a.point;
44601         var ctx = this.canvasElCtx();
44602         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44603         ctx.beginPath();
44604         this.drawCurveSegment(point.x, point.y, width);
44605         ctx.closePath();
44606         ctx.fillStyle = color;
44607         ctx.fill();
44608     },
44609     
44610     drawCurve: function (_a) {
44611         var color = _a.color, curve = _a.curve;
44612         var ctx = this.canvasElCtx();
44613         var widthDelta = curve.endWidth - curve.startWidth;
44614         var drawSteps = Math.floor(curve.length()) * 2;
44615         ctx.beginPath();
44616         ctx.fillStyle = color;
44617         for (var i = 0; i < drawSteps; i += 1) {
44618         var t = i / drawSteps;
44619         var tt = t * t;
44620         var ttt = tt * t;
44621         var u = 1 - t;
44622         var uu = u * u;
44623         var uuu = uu * u;
44624         var x = uuu * curve.startPoint.x;
44625         x += 3 * uu * t * curve.control1.x;
44626         x += 3 * u * tt * curve.control2.x;
44627         x += ttt * curve.endPoint.x;
44628         var y = uuu * curve.startPoint.y;
44629         y += 3 * uu * t * curve.control1.y;
44630         y += 3 * u * tt * curve.control2.y;
44631         y += ttt * curve.endPoint.y;
44632         var width = curve.startWidth + ttt * widthDelta;
44633         this.drawCurveSegment(x, y, width);
44634         }
44635         ctx.closePath();
44636         ctx.fill();
44637     },
44638     
44639     drawCurveSegment: function (x, y, width) {
44640         var ctx = this.canvasElCtx();
44641         ctx.moveTo(x, y);
44642         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44643         this.is_empty = false;
44644     },
44645     
44646     clear: function()
44647     {
44648         var ctx = this.canvasElCtx();
44649         var canvas = this.canvasEl().dom;
44650         ctx.fillStyle = this.bg_color;
44651         ctx.clearRect(0, 0, canvas.width, canvas.height);
44652         ctx.fillRect(0, 0, canvas.width, canvas.height);
44653         this.curve_data = [];
44654         this.reset();
44655         this.is_empty = true;
44656     },
44657     
44658     fileEl: function()
44659     {
44660         return  this.el.select('input',true).first();
44661     },
44662     
44663     canvasEl: function()
44664     {
44665         return this.el.select('canvas',true).first();
44666     },
44667     
44668     canvasElCtx: function()
44669     {
44670         return this.el.select('canvas',true).first().dom.getContext('2d');
44671     },
44672     
44673     getImage: function(type)
44674     {
44675         if(this.is_empty) {
44676             return false;
44677         }
44678         
44679         // encryption ?
44680         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44681     },
44682     
44683     drawFromImage: function(img_src)
44684     {
44685         var img = new Image();
44686         
44687         img.onload = function(){
44688             this.canvasElCtx().drawImage(img, 0, 0);
44689         }.bind(this);
44690         
44691         img.src = img_src;
44692         
44693         this.is_empty = false;
44694     },
44695     
44696     selectImage: function()
44697     {
44698         this.fileEl().dom.click();
44699     },
44700     
44701     uploadImage: function(e)
44702     {
44703         var reader = new FileReader();
44704         
44705         reader.onload = function(e){
44706             var img = new Image();
44707             img.onload = function(){
44708                 this.reset();
44709                 this.canvasElCtx().drawImage(img, 0, 0);
44710             }.bind(this);
44711             img.src = e.target.result;
44712         }.bind(this);
44713         
44714         reader.readAsDataURL(e.target.files[0]);
44715     },
44716     
44717     // Bezier Point Constructor
44718     Point: (function () {
44719         function Point(x, y, time) {
44720             this.x = x;
44721             this.y = y;
44722             this.time = time || Date.now();
44723         }
44724         Point.prototype.distanceTo = function (start) {
44725             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44726         };
44727         Point.prototype.equals = function (other) {
44728             return this.x === other.x && this.y === other.y && this.time === other.time;
44729         };
44730         Point.prototype.velocityFrom = function (start) {
44731             return this.time !== start.time
44732             ? this.distanceTo(start) / (this.time - start.time)
44733             : 0;
44734         };
44735         return Point;
44736     }()),
44737     
44738     
44739     // Bezier Constructor
44740     Bezier: (function () {
44741         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44742             this.startPoint = startPoint;
44743             this.control2 = control2;
44744             this.control1 = control1;
44745             this.endPoint = endPoint;
44746             this.startWidth = startWidth;
44747             this.endWidth = endWidth;
44748         }
44749         Bezier.fromPoints = function (points, widths, scope) {
44750             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44751             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44752             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44753         };
44754         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44755             var dx1 = s1.x - s2.x;
44756             var dy1 = s1.y - s2.y;
44757             var dx2 = s2.x - s3.x;
44758             var dy2 = s2.y - s3.y;
44759             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44760             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44761             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44762             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44763             var dxm = m1.x - m2.x;
44764             var dym = m1.y - m2.y;
44765             var k = l2 / (l1 + l2);
44766             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44767             var tx = s2.x - cm.x;
44768             var ty = s2.y - cm.y;
44769             return {
44770                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44771                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44772             };
44773         };
44774         Bezier.prototype.length = function () {
44775             var steps = 10;
44776             var length = 0;
44777             var px;
44778             var py;
44779             for (var i = 0; i <= steps; i += 1) {
44780                 var t = i / steps;
44781                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44782                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44783                 if (i > 0) {
44784                     var xdiff = cx - px;
44785                     var ydiff = cy - py;
44786                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44787                 }
44788                 px = cx;
44789                 py = cy;
44790             }
44791             return length;
44792         };
44793         Bezier.prototype.point = function (t, start, c1, c2, end) {
44794             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44795             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44796             + (3.0 * c2 * (1.0 - t) * t * t)
44797             + (end * t * t * t);
44798         };
44799         return Bezier;
44800     }()),
44801     
44802     throttleStroke: function(fn, wait) {
44803       if (wait === void 0) { wait = 250; }
44804       var previous = 0;
44805       var timeout = null;
44806       var result;
44807       var storedContext;
44808       var storedArgs;
44809       var later = function () {
44810           previous = Date.now();
44811           timeout = null;
44812           result = fn.apply(storedContext, storedArgs);
44813           if (!timeout) {
44814               storedContext = null;
44815               storedArgs = [];
44816           }
44817       };
44818       return function wrapper() {
44819           var args = [];
44820           for (var _i = 0; _i < arguments.length; _i++) {
44821               args[_i] = arguments[_i];
44822           }
44823           var now = Date.now();
44824           var remaining = wait - (now - previous);
44825           storedContext = this;
44826           storedArgs = args;
44827           if (remaining <= 0 || remaining > wait) {
44828               if (timeout) {
44829                   clearTimeout(timeout);
44830                   timeout = null;
44831               }
44832               previous = now;
44833               result = fn.apply(storedContext, storedArgs);
44834               if (!timeout) {
44835                   storedContext = null;
44836                   storedArgs = [];
44837               }
44838           }
44839           else if (!timeout) {
44840               timeout = window.setTimeout(later, remaining);
44841           }
44842           return result;
44843       };
44844   }
44845   
44846 });
44847
44848  
44849
44850