menu position calcs based on align
[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} faicon font-awesomeicon
29821  * 
29822  * @constructor
29823  * Create a new alert
29824  * @param {Object} config The config object
29825  */
29826
29827
29828 Roo.bootstrap.Alert = function(config){
29829     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29830     
29831 };
29832
29833 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29834     
29835     title: '',
29836     html: '',
29837     weight: false,
29838     faicon: false,
29839     
29840     getAutoCreate : function()
29841     {
29842         
29843         var cfg = {
29844             tag : 'div',
29845             cls : 'alert',
29846             cn : [
29847                 {
29848                     tag : 'i',
29849                     cls : 'roo-alert-icon'
29850                     
29851                 },
29852                 {
29853                     tag : 'b',
29854                     cls : 'roo-alert-title',
29855                     html : this.title
29856                 },
29857                 {
29858                     tag : 'span',
29859                     cls : 'roo-alert-text',
29860                     html : this.html
29861                 }
29862             ]
29863         };
29864         
29865         if(this.faicon){
29866             cfg.cn[0].cls += ' fa ' + this.faicon;
29867         }
29868         
29869         if(this.weight){
29870             cfg.cls += ' alert-' + this.weight;
29871         }
29872         
29873         return cfg;
29874     },
29875     
29876     initEvents: function() 
29877     {
29878         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29879     },
29880     
29881     setTitle : function(str)
29882     {
29883         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29884     },
29885     
29886     setText : function(str)
29887     {
29888         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29889     },
29890     
29891     setWeight : function(weight)
29892     {
29893         if(this.weight){
29894             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29895         }
29896         
29897         this.weight = weight;
29898         
29899         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29900     },
29901     
29902     setIcon : function(icon)
29903     {
29904         if(this.faicon){
29905             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29906         }
29907         
29908         this.faicon = icon;
29909         
29910         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29911     },
29912     
29913     hide: function() 
29914     {
29915         this.el.hide();   
29916     },
29917     
29918     show: function() 
29919     {  
29920         this.el.show();   
29921     }
29922     
29923 });
29924
29925  
29926 /*
29927 * Licence: LGPL
29928 */
29929
29930 /**
29931  * @class Roo.bootstrap.UploadCropbox
29932  * @extends Roo.bootstrap.Component
29933  * Bootstrap UploadCropbox class
29934  * @cfg {String} emptyText show when image has been loaded
29935  * @cfg {String} rotateNotify show when image too small to rotate
29936  * @cfg {Number} errorTimeout default 3000
29937  * @cfg {Number} minWidth default 300
29938  * @cfg {Number} minHeight default 300
29939  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29940  * @cfg {Boolean} isDocument (true|false) default false
29941  * @cfg {String} url action url
29942  * @cfg {String} paramName default 'imageUpload'
29943  * @cfg {String} method default POST
29944  * @cfg {Boolean} loadMask (true|false) default true
29945  * @cfg {Boolean} loadingText default 'Loading...'
29946  * 
29947  * @constructor
29948  * Create a new UploadCropbox
29949  * @param {Object} config The config object
29950  */
29951
29952 Roo.bootstrap.UploadCropbox = function(config){
29953     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29954     
29955     this.addEvents({
29956         /**
29957          * @event beforeselectfile
29958          * Fire before select file
29959          * @param {Roo.bootstrap.UploadCropbox} this
29960          */
29961         "beforeselectfile" : true,
29962         /**
29963          * @event initial
29964          * Fire after initEvent
29965          * @param {Roo.bootstrap.UploadCropbox} this
29966          */
29967         "initial" : true,
29968         /**
29969          * @event crop
29970          * Fire after initEvent
29971          * @param {Roo.bootstrap.UploadCropbox} this
29972          * @param {String} data
29973          */
29974         "crop" : true,
29975         /**
29976          * @event prepare
29977          * Fire when preparing the file data
29978          * @param {Roo.bootstrap.UploadCropbox} this
29979          * @param {Object} file
29980          */
29981         "prepare" : true,
29982         /**
29983          * @event exception
29984          * Fire when get exception
29985          * @param {Roo.bootstrap.UploadCropbox} this
29986          * @param {XMLHttpRequest} xhr
29987          */
29988         "exception" : true,
29989         /**
29990          * @event beforeloadcanvas
29991          * Fire before load the canvas
29992          * @param {Roo.bootstrap.UploadCropbox} this
29993          * @param {String} src
29994          */
29995         "beforeloadcanvas" : true,
29996         /**
29997          * @event trash
29998          * Fire when trash image
29999          * @param {Roo.bootstrap.UploadCropbox} this
30000          */
30001         "trash" : true,
30002         /**
30003          * @event download
30004          * Fire when download the image
30005          * @param {Roo.bootstrap.UploadCropbox} this
30006          */
30007         "download" : true,
30008         /**
30009          * @event footerbuttonclick
30010          * Fire when footerbuttonclick
30011          * @param {Roo.bootstrap.UploadCropbox} this
30012          * @param {String} type
30013          */
30014         "footerbuttonclick" : true,
30015         /**
30016          * @event resize
30017          * Fire when resize
30018          * @param {Roo.bootstrap.UploadCropbox} this
30019          */
30020         "resize" : true,
30021         /**
30022          * @event rotate
30023          * Fire when rotate the image
30024          * @param {Roo.bootstrap.UploadCropbox} this
30025          * @param {String} pos
30026          */
30027         "rotate" : true,
30028         /**
30029          * @event inspect
30030          * Fire when inspect the file
30031          * @param {Roo.bootstrap.UploadCropbox} this
30032          * @param {Object} file
30033          */
30034         "inspect" : true,
30035         /**
30036          * @event upload
30037          * Fire when xhr upload the file
30038          * @param {Roo.bootstrap.UploadCropbox} this
30039          * @param {Object} data
30040          */
30041         "upload" : true,
30042         /**
30043          * @event arrange
30044          * Fire when arrange the file data
30045          * @param {Roo.bootstrap.UploadCropbox} this
30046          * @param {Object} formData
30047          */
30048         "arrange" : true
30049     });
30050     
30051     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30052 };
30053
30054 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30055     
30056     emptyText : 'Click to upload image',
30057     rotateNotify : 'Image is too small to rotate',
30058     errorTimeout : 3000,
30059     scale : 0,
30060     baseScale : 1,
30061     rotate : 0,
30062     dragable : false,
30063     pinching : false,
30064     mouseX : 0,
30065     mouseY : 0,
30066     cropData : false,
30067     minWidth : 300,
30068     minHeight : 300,
30069     file : false,
30070     exif : {},
30071     baseRotate : 1,
30072     cropType : 'image/jpeg',
30073     buttons : false,
30074     canvasLoaded : false,
30075     isDocument : false,
30076     method : 'POST',
30077     paramName : 'imageUpload',
30078     loadMask : true,
30079     loadingText : 'Loading...',
30080     maskEl : false,
30081     
30082     getAutoCreate : function()
30083     {
30084         var cfg = {
30085             tag : 'div',
30086             cls : 'roo-upload-cropbox',
30087             cn : [
30088                 {
30089                     tag : 'input',
30090                     cls : 'roo-upload-cropbox-selector',
30091                     type : 'file'
30092                 },
30093                 {
30094                     tag : 'div',
30095                     cls : 'roo-upload-cropbox-body',
30096                     style : 'cursor:pointer',
30097                     cn : [
30098                         {
30099                             tag : 'div',
30100                             cls : 'roo-upload-cropbox-preview'
30101                         },
30102                         {
30103                             tag : 'div',
30104                             cls : 'roo-upload-cropbox-thumb'
30105                         },
30106                         {
30107                             tag : 'div',
30108                             cls : 'roo-upload-cropbox-empty-notify',
30109                             html : this.emptyText
30110                         },
30111                         {
30112                             tag : 'div',
30113                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30114                             html : this.rotateNotify
30115                         }
30116                     ]
30117                 },
30118                 {
30119                     tag : 'div',
30120                     cls : 'roo-upload-cropbox-footer',
30121                     cn : {
30122                         tag : 'div',
30123                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30124                         cn : []
30125                     }
30126                 }
30127             ]
30128         };
30129         
30130         return cfg;
30131     },
30132     
30133     onRender : function(ct, position)
30134     {
30135         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30136         
30137         if (this.buttons.length) {
30138             
30139             Roo.each(this.buttons, function(bb) {
30140                 
30141                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30142                 
30143                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30144                 
30145             }, this);
30146         }
30147         
30148         if(this.loadMask){
30149             this.maskEl = this.el;
30150         }
30151     },
30152     
30153     initEvents : function()
30154     {
30155         this.urlAPI = (window.createObjectURL && window) || 
30156                                 (window.URL && URL.revokeObjectURL && URL) || 
30157                                 (window.webkitURL && webkitURL);
30158                         
30159         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30160         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30161         
30162         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30163         this.selectorEl.hide();
30164         
30165         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30166         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30167         
30168         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30169         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30170         this.thumbEl.hide();
30171         
30172         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30173         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30174         
30175         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30176         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30177         this.errorEl.hide();
30178         
30179         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30180         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30181         this.footerEl.hide();
30182         
30183         this.setThumbBoxSize();
30184         
30185         this.bind();
30186         
30187         this.resize();
30188         
30189         this.fireEvent('initial', this);
30190     },
30191
30192     bind : function()
30193     {
30194         var _this = this;
30195         
30196         window.addEventListener("resize", function() { _this.resize(); } );
30197         
30198         this.bodyEl.on('click', this.beforeSelectFile, this);
30199         
30200         if(Roo.isTouch){
30201             this.bodyEl.on('touchstart', this.onTouchStart, this);
30202             this.bodyEl.on('touchmove', this.onTouchMove, this);
30203             this.bodyEl.on('touchend', this.onTouchEnd, this);
30204         }
30205         
30206         if(!Roo.isTouch){
30207             this.bodyEl.on('mousedown', this.onMouseDown, this);
30208             this.bodyEl.on('mousemove', this.onMouseMove, this);
30209             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30210             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30211             Roo.get(document).on('mouseup', this.onMouseUp, this);
30212         }
30213         
30214         this.selectorEl.on('change', this.onFileSelected, this);
30215     },
30216     
30217     reset : function()
30218     {    
30219         this.scale = 0;
30220         this.baseScale = 1;
30221         this.rotate = 0;
30222         this.baseRotate = 1;
30223         this.dragable = false;
30224         this.pinching = false;
30225         this.mouseX = 0;
30226         this.mouseY = 0;
30227         this.cropData = false;
30228         this.notifyEl.dom.innerHTML = this.emptyText;
30229         
30230         this.selectorEl.dom.value = '';
30231         
30232     },
30233     
30234     resize : function()
30235     {
30236         if(this.fireEvent('resize', this) != false){
30237             this.setThumbBoxPosition();
30238             this.setCanvasPosition();
30239         }
30240     },
30241     
30242     onFooterButtonClick : function(e, el, o, type)
30243     {
30244         switch (type) {
30245             case 'rotate-left' :
30246                 this.onRotateLeft(e);
30247                 break;
30248             case 'rotate-right' :
30249                 this.onRotateRight(e);
30250                 break;
30251             case 'picture' :
30252                 this.beforeSelectFile(e);
30253                 break;
30254             case 'trash' :
30255                 this.trash(e);
30256                 break;
30257             case 'crop' :
30258                 this.crop(e);
30259                 break;
30260             case 'download' :
30261                 this.download(e);
30262                 break;
30263             default :
30264                 break;
30265         }
30266         
30267         this.fireEvent('footerbuttonclick', this, type);
30268     },
30269     
30270     beforeSelectFile : function(e)
30271     {
30272         e.preventDefault();
30273         
30274         if(this.fireEvent('beforeselectfile', this) != false){
30275             this.selectorEl.dom.click();
30276         }
30277     },
30278     
30279     onFileSelected : function(e)
30280     {
30281         e.preventDefault();
30282         
30283         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30284             return;
30285         }
30286         
30287         var file = this.selectorEl.dom.files[0];
30288         
30289         if(this.fireEvent('inspect', this, file) != false){
30290             this.prepare(file);
30291         }
30292         
30293     },
30294     
30295     trash : function(e)
30296     {
30297         this.fireEvent('trash', this);
30298     },
30299     
30300     download : function(e)
30301     {
30302         this.fireEvent('download', this);
30303     },
30304     
30305     loadCanvas : function(src)
30306     {   
30307         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30308             
30309             this.reset();
30310             
30311             this.imageEl = document.createElement('img');
30312             
30313             var _this = this;
30314             
30315             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30316             
30317             this.imageEl.src = src;
30318         }
30319     },
30320     
30321     onLoadCanvas : function()
30322     {   
30323         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30324         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30325         
30326         this.bodyEl.un('click', this.beforeSelectFile, this);
30327         
30328         this.notifyEl.hide();
30329         this.thumbEl.show();
30330         this.footerEl.show();
30331         
30332         this.baseRotateLevel();
30333         
30334         if(this.isDocument){
30335             this.setThumbBoxSize();
30336         }
30337         
30338         this.setThumbBoxPosition();
30339         
30340         this.baseScaleLevel();
30341         
30342         this.draw();
30343         
30344         this.resize();
30345         
30346         this.canvasLoaded = true;
30347         
30348         if(this.loadMask){
30349             this.maskEl.unmask();
30350         }
30351         
30352     },
30353     
30354     setCanvasPosition : function()
30355     {   
30356         if(!this.canvasEl){
30357             return;
30358         }
30359         
30360         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30361         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30362         
30363         this.previewEl.setLeft(pw);
30364         this.previewEl.setTop(ph);
30365         
30366     },
30367     
30368     onMouseDown : function(e)
30369     {   
30370         e.stopEvent();
30371         
30372         this.dragable = true;
30373         this.pinching = false;
30374         
30375         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30376             this.dragable = false;
30377             return;
30378         }
30379         
30380         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30381         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30382         
30383     },
30384     
30385     onMouseMove : function(e)
30386     {   
30387         e.stopEvent();
30388         
30389         if(!this.canvasLoaded){
30390             return;
30391         }
30392         
30393         if (!this.dragable){
30394             return;
30395         }
30396         
30397         var minX = Math.ceil(this.thumbEl.getLeft(true));
30398         var minY = Math.ceil(this.thumbEl.getTop(true));
30399         
30400         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30401         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30402         
30403         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30404         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30405         
30406         x = x - this.mouseX;
30407         y = y - this.mouseY;
30408         
30409         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30410         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30411         
30412         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30413         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30414         
30415         this.previewEl.setLeft(bgX);
30416         this.previewEl.setTop(bgY);
30417         
30418         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30419         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30420     },
30421     
30422     onMouseUp : function(e)
30423     {   
30424         e.stopEvent();
30425         
30426         this.dragable = false;
30427     },
30428     
30429     onMouseWheel : function(e)
30430     {   
30431         e.stopEvent();
30432         
30433         this.startScale = this.scale;
30434         
30435         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30436         
30437         if(!this.zoomable()){
30438             this.scale = this.startScale;
30439             return;
30440         }
30441         
30442         this.draw();
30443         
30444         return;
30445     },
30446     
30447     zoomable : function()
30448     {
30449         var minScale = this.thumbEl.getWidth() / this.minWidth;
30450         
30451         if(this.minWidth < this.minHeight){
30452             minScale = this.thumbEl.getHeight() / this.minHeight;
30453         }
30454         
30455         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30456         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30457         
30458         if(
30459                 this.isDocument &&
30460                 (this.rotate == 0 || this.rotate == 180) && 
30461                 (
30462                     width > this.imageEl.OriginWidth || 
30463                     height > this.imageEl.OriginHeight ||
30464                     (width < this.minWidth && height < this.minHeight)
30465                 )
30466         ){
30467             return false;
30468         }
30469         
30470         if(
30471                 this.isDocument &&
30472                 (this.rotate == 90 || this.rotate == 270) && 
30473                 (
30474                     width > this.imageEl.OriginWidth || 
30475                     height > this.imageEl.OriginHeight ||
30476                     (width < this.minHeight && height < this.minWidth)
30477                 )
30478         ){
30479             return false;
30480         }
30481         
30482         if(
30483                 !this.isDocument &&
30484                 (this.rotate == 0 || this.rotate == 180) && 
30485                 (
30486                     width < this.minWidth || 
30487                     width > this.imageEl.OriginWidth || 
30488                     height < this.minHeight || 
30489                     height > this.imageEl.OriginHeight
30490                 )
30491         ){
30492             return false;
30493         }
30494         
30495         if(
30496                 !this.isDocument &&
30497                 (this.rotate == 90 || this.rotate == 270) && 
30498                 (
30499                     width < this.minHeight || 
30500                     width > this.imageEl.OriginWidth || 
30501                     height < this.minWidth || 
30502                     height > this.imageEl.OriginHeight
30503                 )
30504         ){
30505             return false;
30506         }
30507         
30508         return true;
30509         
30510     },
30511     
30512     onRotateLeft : function(e)
30513     {   
30514         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30515             
30516             var minScale = this.thumbEl.getWidth() / this.minWidth;
30517             
30518             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30519             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30520             
30521             this.startScale = this.scale;
30522             
30523             while (this.getScaleLevel() < minScale){
30524             
30525                 this.scale = this.scale + 1;
30526                 
30527                 if(!this.zoomable()){
30528                     break;
30529                 }
30530                 
30531                 if(
30532                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30533                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30534                 ){
30535                     continue;
30536                 }
30537                 
30538                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30539
30540                 this.draw();
30541                 
30542                 return;
30543             }
30544             
30545             this.scale = this.startScale;
30546             
30547             this.onRotateFail();
30548             
30549             return false;
30550         }
30551         
30552         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30553
30554         if(this.isDocument){
30555             this.setThumbBoxSize();
30556             this.setThumbBoxPosition();
30557             this.setCanvasPosition();
30558         }
30559         
30560         this.draw();
30561         
30562         this.fireEvent('rotate', this, 'left');
30563         
30564     },
30565     
30566     onRotateRight : function(e)
30567     {
30568         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30569             
30570             var minScale = this.thumbEl.getWidth() / this.minWidth;
30571         
30572             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30573             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30574             
30575             this.startScale = this.scale;
30576             
30577             while (this.getScaleLevel() < minScale){
30578             
30579                 this.scale = this.scale + 1;
30580                 
30581                 if(!this.zoomable()){
30582                     break;
30583                 }
30584                 
30585                 if(
30586                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30587                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30588                 ){
30589                     continue;
30590                 }
30591                 
30592                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30593
30594                 this.draw();
30595                 
30596                 return;
30597             }
30598             
30599             this.scale = this.startScale;
30600             
30601             this.onRotateFail();
30602             
30603             return false;
30604         }
30605         
30606         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30607
30608         if(this.isDocument){
30609             this.setThumbBoxSize();
30610             this.setThumbBoxPosition();
30611             this.setCanvasPosition();
30612         }
30613         
30614         this.draw();
30615         
30616         this.fireEvent('rotate', this, 'right');
30617     },
30618     
30619     onRotateFail : function()
30620     {
30621         this.errorEl.show(true);
30622         
30623         var _this = this;
30624         
30625         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30626     },
30627     
30628     draw : function()
30629     {
30630         this.previewEl.dom.innerHTML = '';
30631         
30632         var canvasEl = document.createElement("canvas");
30633         
30634         var contextEl = canvasEl.getContext("2d");
30635         
30636         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30637         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30638         var center = this.imageEl.OriginWidth / 2;
30639         
30640         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30641             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30642             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30643             center = this.imageEl.OriginHeight / 2;
30644         }
30645         
30646         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30647         
30648         contextEl.translate(center, center);
30649         contextEl.rotate(this.rotate * Math.PI / 180);
30650
30651         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30652         
30653         this.canvasEl = document.createElement("canvas");
30654         
30655         this.contextEl = this.canvasEl.getContext("2d");
30656         
30657         switch (this.rotate) {
30658             case 0 :
30659                 
30660                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30661                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30662                 
30663                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30664                 
30665                 break;
30666             case 90 : 
30667                 
30668                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30669                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30670                 
30671                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30672                     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);
30673                     break;
30674                 }
30675                 
30676                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30677                 
30678                 break;
30679             case 180 :
30680                 
30681                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30682                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30683                 
30684                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30685                     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);
30686                     break;
30687                 }
30688                 
30689                 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);
30690                 
30691                 break;
30692             case 270 :
30693                 
30694                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30695                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30696         
30697                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30698                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30699                     break;
30700                 }
30701                 
30702                 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);
30703                 
30704                 break;
30705             default : 
30706                 break;
30707         }
30708         
30709         this.previewEl.appendChild(this.canvasEl);
30710         
30711         this.setCanvasPosition();
30712     },
30713     
30714     crop : function()
30715     {
30716         if(!this.canvasLoaded){
30717             return;
30718         }
30719         
30720         var imageCanvas = document.createElement("canvas");
30721         
30722         var imageContext = imageCanvas.getContext("2d");
30723         
30724         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30725         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30726         
30727         var center = imageCanvas.width / 2;
30728         
30729         imageContext.translate(center, center);
30730         
30731         imageContext.rotate(this.rotate * Math.PI / 180);
30732         
30733         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30734         
30735         var canvas = document.createElement("canvas");
30736         
30737         var context = canvas.getContext("2d");
30738                 
30739         canvas.width = this.minWidth;
30740         canvas.height = this.minHeight;
30741
30742         switch (this.rotate) {
30743             case 0 :
30744                 
30745                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30746                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30747                 
30748                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30749                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30750                 
30751                 var targetWidth = this.minWidth - 2 * x;
30752                 var targetHeight = this.minHeight - 2 * y;
30753                 
30754                 var scale = 1;
30755                 
30756                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30757                     scale = targetWidth / width;
30758                 }
30759                 
30760                 if(x > 0 && y == 0){
30761                     scale = targetHeight / height;
30762                 }
30763                 
30764                 if(x > 0 && y > 0){
30765                     scale = targetWidth / width;
30766                     
30767                     if(width < height){
30768                         scale = targetHeight / height;
30769                     }
30770                 }
30771                 
30772                 context.scale(scale, scale);
30773                 
30774                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30775                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30776
30777                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30778                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30779
30780                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30781                 
30782                 break;
30783             case 90 : 
30784                 
30785                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30786                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30787                 
30788                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30789                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30790                 
30791                 var targetWidth = this.minWidth - 2 * x;
30792                 var targetHeight = this.minHeight - 2 * y;
30793                 
30794                 var scale = 1;
30795                 
30796                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30797                     scale = targetWidth / width;
30798                 }
30799                 
30800                 if(x > 0 && y == 0){
30801                     scale = targetHeight / height;
30802                 }
30803                 
30804                 if(x > 0 && y > 0){
30805                     scale = targetWidth / width;
30806                     
30807                     if(width < height){
30808                         scale = targetHeight / height;
30809                     }
30810                 }
30811                 
30812                 context.scale(scale, scale);
30813                 
30814                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30815                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30816
30817                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30818                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30819                 
30820                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30821                 
30822                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30823                 
30824                 break;
30825             case 180 :
30826                 
30827                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30828                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30829                 
30830                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30831                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30832                 
30833                 var targetWidth = this.minWidth - 2 * x;
30834                 var targetHeight = this.minHeight - 2 * y;
30835                 
30836                 var scale = 1;
30837                 
30838                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30839                     scale = targetWidth / width;
30840                 }
30841                 
30842                 if(x > 0 && y == 0){
30843                     scale = targetHeight / height;
30844                 }
30845                 
30846                 if(x > 0 && y > 0){
30847                     scale = targetWidth / width;
30848                     
30849                     if(width < height){
30850                         scale = targetHeight / height;
30851                     }
30852                 }
30853                 
30854                 context.scale(scale, scale);
30855                 
30856                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30857                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30858
30859                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30860                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30861
30862                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30863                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30864                 
30865                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30866                 
30867                 break;
30868             case 270 :
30869                 
30870                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30871                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30872                 
30873                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30874                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30875                 
30876                 var targetWidth = this.minWidth - 2 * x;
30877                 var targetHeight = this.minHeight - 2 * y;
30878                 
30879                 var scale = 1;
30880                 
30881                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30882                     scale = targetWidth / width;
30883                 }
30884                 
30885                 if(x > 0 && y == 0){
30886                     scale = targetHeight / height;
30887                 }
30888                 
30889                 if(x > 0 && y > 0){
30890                     scale = targetWidth / width;
30891                     
30892                     if(width < height){
30893                         scale = targetHeight / height;
30894                     }
30895                 }
30896                 
30897                 context.scale(scale, scale);
30898                 
30899                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30900                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30901
30902                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30903                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30904                 
30905                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30906                 
30907                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30908                 
30909                 break;
30910             default : 
30911                 break;
30912         }
30913         
30914         this.cropData = canvas.toDataURL(this.cropType);
30915         
30916         if(this.fireEvent('crop', this, this.cropData) !== false){
30917             this.process(this.file, this.cropData);
30918         }
30919         
30920         return;
30921         
30922     },
30923     
30924     setThumbBoxSize : function()
30925     {
30926         var width, height;
30927         
30928         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30929             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30930             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30931             
30932             this.minWidth = width;
30933             this.minHeight = height;
30934             
30935             if(this.rotate == 90 || this.rotate == 270){
30936                 this.minWidth = height;
30937                 this.minHeight = width;
30938             }
30939         }
30940         
30941         height = 300;
30942         width = Math.ceil(this.minWidth * height / this.minHeight);
30943         
30944         if(this.minWidth > this.minHeight){
30945             width = 300;
30946             height = Math.ceil(this.minHeight * width / this.minWidth);
30947         }
30948         
30949         this.thumbEl.setStyle({
30950             width : width + 'px',
30951             height : height + 'px'
30952         });
30953
30954         return;
30955             
30956     },
30957     
30958     setThumbBoxPosition : function()
30959     {
30960         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30961         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30962         
30963         this.thumbEl.setLeft(x);
30964         this.thumbEl.setTop(y);
30965         
30966     },
30967     
30968     baseRotateLevel : function()
30969     {
30970         this.baseRotate = 1;
30971         
30972         if(
30973                 typeof(this.exif) != 'undefined' &&
30974                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30975                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30976         ){
30977             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30978         }
30979         
30980         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30981         
30982     },
30983     
30984     baseScaleLevel : function()
30985     {
30986         var width, height;
30987         
30988         if(this.isDocument){
30989             
30990             if(this.baseRotate == 6 || this.baseRotate == 8){
30991             
30992                 height = this.thumbEl.getHeight();
30993                 this.baseScale = height / this.imageEl.OriginWidth;
30994
30995                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30996                     width = this.thumbEl.getWidth();
30997                     this.baseScale = width / this.imageEl.OriginHeight;
30998                 }
30999
31000                 return;
31001             }
31002
31003             height = this.thumbEl.getHeight();
31004             this.baseScale = height / this.imageEl.OriginHeight;
31005
31006             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31007                 width = this.thumbEl.getWidth();
31008                 this.baseScale = width / this.imageEl.OriginWidth;
31009             }
31010
31011             return;
31012         }
31013         
31014         if(this.baseRotate == 6 || this.baseRotate == 8){
31015             
31016             width = this.thumbEl.getHeight();
31017             this.baseScale = width / this.imageEl.OriginHeight;
31018             
31019             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31020                 height = this.thumbEl.getWidth();
31021                 this.baseScale = height / this.imageEl.OriginHeight;
31022             }
31023             
31024             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31025                 height = this.thumbEl.getWidth();
31026                 this.baseScale = height / this.imageEl.OriginHeight;
31027                 
31028                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31029                     width = this.thumbEl.getHeight();
31030                     this.baseScale = width / this.imageEl.OriginWidth;
31031                 }
31032             }
31033             
31034             return;
31035         }
31036         
31037         width = this.thumbEl.getWidth();
31038         this.baseScale = width / this.imageEl.OriginWidth;
31039         
31040         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31041             height = this.thumbEl.getHeight();
31042             this.baseScale = height / this.imageEl.OriginHeight;
31043         }
31044         
31045         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31046             
31047             height = this.thumbEl.getHeight();
31048             this.baseScale = height / this.imageEl.OriginHeight;
31049             
31050             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31051                 width = this.thumbEl.getWidth();
31052                 this.baseScale = width / this.imageEl.OriginWidth;
31053             }
31054             
31055         }
31056         
31057         return;
31058     },
31059     
31060     getScaleLevel : function()
31061     {
31062         return this.baseScale * Math.pow(1.1, this.scale);
31063     },
31064     
31065     onTouchStart : function(e)
31066     {
31067         if(!this.canvasLoaded){
31068             this.beforeSelectFile(e);
31069             return;
31070         }
31071         
31072         var touches = e.browserEvent.touches;
31073         
31074         if(!touches){
31075             return;
31076         }
31077         
31078         if(touches.length == 1){
31079             this.onMouseDown(e);
31080             return;
31081         }
31082         
31083         if(touches.length != 2){
31084             return;
31085         }
31086         
31087         var coords = [];
31088         
31089         for(var i = 0, finger; finger = touches[i]; i++){
31090             coords.push(finger.pageX, finger.pageY);
31091         }
31092         
31093         var x = Math.pow(coords[0] - coords[2], 2);
31094         var y = Math.pow(coords[1] - coords[3], 2);
31095         
31096         this.startDistance = Math.sqrt(x + y);
31097         
31098         this.startScale = this.scale;
31099         
31100         this.pinching = true;
31101         this.dragable = false;
31102         
31103     },
31104     
31105     onTouchMove : function(e)
31106     {
31107         if(!this.pinching && !this.dragable){
31108             return;
31109         }
31110         
31111         var touches = e.browserEvent.touches;
31112         
31113         if(!touches){
31114             return;
31115         }
31116         
31117         if(this.dragable){
31118             this.onMouseMove(e);
31119             return;
31120         }
31121         
31122         var coords = [];
31123         
31124         for(var i = 0, finger; finger = touches[i]; i++){
31125             coords.push(finger.pageX, finger.pageY);
31126         }
31127         
31128         var x = Math.pow(coords[0] - coords[2], 2);
31129         var y = Math.pow(coords[1] - coords[3], 2);
31130         
31131         this.endDistance = Math.sqrt(x + y);
31132         
31133         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31134         
31135         if(!this.zoomable()){
31136             this.scale = this.startScale;
31137             return;
31138         }
31139         
31140         this.draw();
31141         
31142     },
31143     
31144     onTouchEnd : function(e)
31145     {
31146         this.pinching = false;
31147         this.dragable = false;
31148         
31149     },
31150     
31151     process : function(file, crop)
31152     {
31153         if(this.loadMask){
31154             this.maskEl.mask(this.loadingText);
31155         }
31156         
31157         this.xhr = new XMLHttpRequest();
31158         
31159         file.xhr = this.xhr;
31160
31161         this.xhr.open(this.method, this.url, true);
31162         
31163         var headers = {
31164             "Accept": "application/json",
31165             "Cache-Control": "no-cache",
31166             "X-Requested-With": "XMLHttpRequest"
31167         };
31168         
31169         for (var headerName in headers) {
31170             var headerValue = headers[headerName];
31171             if (headerValue) {
31172                 this.xhr.setRequestHeader(headerName, headerValue);
31173             }
31174         }
31175         
31176         var _this = this;
31177         
31178         this.xhr.onload = function()
31179         {
31180             _this.xhrOnLoad(_this.xhr);
31181         }
31182         
31183         this.xhr.onerror = function()
31184         {
31185             _this.xhrOnError(_this.xhr);
31186         }
31187         
31188         var formData = new FormData();
31189
31190         formData.append('returnHTML', 'NO');
31191         
31192         if(crop){
31193             formData.append('crop', crop);
31194         }
31195         
31196         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31197             formData.append(this.paramName, file, file.name);
31198         }
31199         
31200         if(typeof(file.filename) != 'undefined'){
31201             formData.append('filename', file.filename);
31202         }
31203         
31204         if(typeof(file.mimetype) != 'undefined'){
31205             formData.append('mimetype', file.mimetype);
31206         }
31207         
31208         if(this.fireEvent('arrange', this, formData) != false){
31209             this.xhr.send(formData);
31210         };
31211     },
31212     
31213     xhrOnLoad : function(xhr)
31214     {
31215         if(this.loadMask){
31216             this.maskEl.unmask();
31217         }
31218         
31219         if (xhr.readyState !== 4) {
31220             this.fireEvent('exception', this, xhr);
31221             return;
31222         }
31223
31224         var response = Roo.decode(xhr.responseText);
31225         
31226         if(!response.success){
31227             this.fireEvent('exception', this, xhr);
31228             return;
31229         }
31230         
31231         var response = Roo.decode(xhr.responseText);
31232         
31233         this.fireEvent('upload', this, response);
31234         
31235     },
31236     
31237     xhrOnError : function()
31238     {
31239         if(this.loadMask){
31240             this.maskEl.unmask();
31241         }
31242         
31243         Roo.log('xhr on error');
31244         
31245         var response = Roo.decode(xhr.responseText);
31246           
31247         Roo.log(response);
31248         
31249     },
31250     
31251     prepare : function(file)
31252     {   
31253         if(this.loadMask){
31254             this.maskEl.mask(this.loadingText);
31255         }
31256         
31257         this.file = false;
31258         this.exif = {};
31259         
31260         if(typeof(file) === 'string'){
31261             this.loadCanvas(file);
31262             return;
31263         }
31264         
31265         if(!file || !this.urlAPI){
31266             return;
31267         }
31268         
31269         this.file = file;
31270         this.cropType = file.type;
31271         
31272         var _this = this;
31273         
31274         if(this.fireEvent('prepare', this, this.file) != false){
31275             
31276             var reader = new FileReader();
31277             
31278             reader.onload = function (e) {
31279                 if (e.target.error) {
31280                     Roo.log(e.target.error);
31281                     return;
31282                 }
31283                 
31284                 var buffer = e.target.result,
31285                     dataView = new DataView(buffer),
31286                     offset = 2,
31287                     maxOffset = dataView.byteLength - 4,
31288                     markerBytes,
31289                     markerLength;
31290                 
31291                 if (dataView.getUint16(0) === 0xffd8) {
31292                     while (offset < maxOffset) {
31293                         markerBytes = dataView.getUint16(offset);
31294                         
31295                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31296                             markerLength = dataView.getUint16(offset + 2) + 2;
31297                             if (offset + markerLength > dataView.byteLength) {
31298                                 Roo.log('Invalid meta data: Invalid segment size.');
31299                                 break;
31300                             }
31301                             
31302                             if(markerBytes == 0xffe1){
31303                                 _this.parseExifData(
31304                                     dataView,
31305                                     offset,
31306                                     markerLength
31307                                 );
31308                             }
31309                             
31310                             offset += markerLength;
31311                             
31312                             continue;
31313                         }
31314                         
31315                         break;
31316                     }
31317                     
31318                 }
31319                 
31320                 var url = _this.urlAPI.createObjectURL(_this.file);
31321                 
31322                 _this.loadCanvas(url);
31323                 
31324                 return;
31325             }
31326             
31327             reader.readAsArrayBuffer(this.file);
31328             
31329         }
31330         
31331     },
31332     
31333     parseExifData : function(dataView, offset, length)
31334     {
31335         var tiffOffset = offset + 10,
31336             littleEndian,
31337             dirOffset;
31338     
31339         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31340             // No Exif data, might be XMP data instead
31341             return;
31342         }
31343         
31344         // Check for the ASCII code for "Exif" (0x45786966):
31345         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31346             // No Exif data, might be XMP data instead
31347             return;
31348         }
31349         if (tiffOffset + 8 > dataView.byteLength) {
31350             Roo.log('Invalid Exif data: Invalid segment size.');
31351             return;
31352         }
31353         // Check for the two null bytes:
31354         if (dataView.getUint16(offset + 8) !== 0x0000) {
31355             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31356             return;
31357         }
31358         // Check the byte alignment:
31359         switch (dataView.getUint16(tiffOffset)) {
31360         case 0x4949:
31361             littleEndian = true;
31362             break;
31363         case 0x4D4D:
31364             littleEndian = false;
31365             break;
31366         default:
31367             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31368             return;
31369         }
31370         // Check for the TIFF tag marker (0x002A):
31371         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31372             Roo.log('Invalid Exif data: Missing TIFF marker.');
31373             return;
31374         }
31375         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31376         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31377         
31378         this.parseExifTags(
31379             dataView,
31380             tiffOffset,
31381             tiffOffset + dirOffset,
31382             littleEndian
31383         );
31384     },
31385     
31386     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31387     {
31388         var tagsNumber,
31389             dirEndOffset,
31390             i;
31391         if (dirOffset + 6 > dataView.byteLength) {
31392             Roo.log('Invalid Exif data: Invalid directory offset.');
31393             return;
31394         }
31395         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31396         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31397         if (dirEndOffset + 4 > dataView.byteLength) {
31398             Roo.log('Invalid Exif data: Invalid directory size.');
31399             return;
31400         }
31401         for (i = 0; i < tagsNumber; i += 1) {
31402             this.parseExifTag(
31403                 dataView,
31404                 tiffOffset,
31405                 dirOffset + 2 + 12 * i, // tag offset
31406                 littleEndian
31407             );
31408         }
31409         // Return the offset to the next directory:
31410         return dataView.getUint32(dirEndOffset, littleEndian);
31411     },
31412     
31413     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31414     {
31415         var tag = dataView.getUint16(offset, littleEndian);
31416         
31417         this.exif[tag] = this.getExifValue(
31418             dataView,
31419             tiffOffset,
31420             offset,
31421             dataView.getUint16(offset + 2, littleEndian), // tag type
31422             dataView.getUint32(offset + 4, littleEndian), // tag length
31423             littleEndian
31424         );
31425     },
31426     
31427     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31428     {
31429         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31430             tagSize,
31431             dataOffset,
31432             values,
31433             i,
31434             str,
31435             c;
31436     
31437         if (!tagType) {
31438             Roo.log('Invalid Exif data: Invalid tag type.');
31439             return;
31440         }
31441         
31442         tagSize = tagType.size * length;
31443         // Determine if the value is contained in the dataOffset bytes,
31444         // or if the value at the dataOffset is a pointer to the actual data:
31445         dataOffset = tagSize > 4 ?
31446                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31447         if (dataOffset + tagSize > dataView.byteLength) {
31448             Roo.log('Invalid Exif data: Invalid data offset.');
31449             return;
31450         }
31451         if (length === 1) {
31452             return tagType.getValue(dataView, dataOffset, littleEndian);
31453         }
31454         values = [];
31455         for (i = 0; i < length; i += 1) {
31456             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31457         }
31458         
31459         if (tagType.ascii) {
31460             str = '';
31461             // Concatenate the chars:
31462             for (i = 0; i < values.length; i += 1) {
31463                 c = values[i];
31464                 // Ignore the terminating NULL byte(s):
31465                 if (c === '\u0000') {
31466                     break;
31467                 }
31468                 str += c;
31469             }
31470             return str;
31471         }
31472         return values;
31473     }
31474     
31475 });
31476
31477 Roo.apply(Roo.bootstrap.UploadCropbox, {
31478     tags : {
31479         'Orientation': 0x0112
31480     },
31481     
31482     Orientation: {
31483             1: 0, //'top-left',
31484 //            2: 'top-right',
31485             3: 180, //'bottom-right',
31486 //            4: 'bottom-left',
31487 //            5: 'left-top',
31488             6: 90, //'right-top',
31489 //            7: 'right-bottom',
31490             8: 270 //'left-bottom'
31491     },
31492     
31493     exifTagTypes : {
31494         // byte, 8-bit unsigned int:
31495         1: {
31496             getValue: function (dataView, dataOffset) {
31497                 return dataView.getUint8(dataOffset);
31498             },
31499             size: 1
31500         },
31501         // ascii, 8-bit byte:
31502         2: {
31503             getValue: function (dataView, dataOffset) {
31504                 return String.fromCharCode(dataView.getUint8(dataOffset));
31505             },
31506             size: 1,
31507             ascii: true
31508         },
31509         // short, 16 bit int:
31510         3: {
31511             getValue: function (dataView, dataOffset, littleEndian) {
31512                 return dataView.getUint16(dataOffset, littleEndian);
31513             },
31514             size: 2
31515         },
31516         // long, 32 bit int:
31517         4: {
31518             getValue: function (dataView, dataOffset, littleEndian) {
31519                 return dataView.getUint32(dataOffset, littleEndian);
31520             },
31521             size: 4
31522         },
31523         // rational = two long values, first is numerator, second is denominator:
31524         5: {
31525             getValue: function (dataView, dataOffset, littleEndian) {
31526                 return dataView.getUint32(dataOffset, littleEndian) /
31527                     dataView.getUint32(dataOffset + 4, littleEndian);
31528             },
31529             size: 8
31530         },
31531         // slong, 32 bit signed int:
31532         9: {
31533             getValue: function (dataView, dataOffset, littleEndian) {
31534                 return dataView.getInt32(dataOffset, littleEndian);
31535             },
31536             size: 4
31537         },
31538         // srational, two slongs, first is numerator, second is denominator:
31539         10: {
31540             getValue: function (dataView, dataOffset, littleEndian) {
31541                 return dataView.getInt32(dataOffset, littleEndian) /
31542                     dataView.getInt32(dataOffset + 4, littleEndian);
31543             },
31544             size: 8
31545         }
31546     },
31547     
31548     footer : {
31549         STANDARD : [
31550             {
31551                 tag : 'div',
31552                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31553                 action : 'rotate-left',
31554                 cn : [
31555                     {
31556                         tag : 'button',
31557                         cls : 'btn btn-default',
31558                         html : '<i class="fa fa-undo"></i>'
31559                     }
31560                 ]
31561             },
31562             {
31563                 tag : 'div',
31564                 cls : 'btn-group roo-upload-cropbox-picture',
31565                 action : 'picture',
31566                 cn : [
31567                     {
31568                         tag : 'button',
31569                         cls : 'btn btn-default',
31570                         html : '<i class="fa fa-picture-o"></i>'
31571                     }
31572                 ]
31573             },
31574             {
31575                 tag : 'div',
31576                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31577                 action : 'rotate-right',
31578                 cn : [
31579                     {
31580                         tag : 'button',
31581                         cls : 'btn btn-default',
31582                         html : '<i class="fa fa-repeat"></i>'
31583                     }
31584                 ]
31585             }
31586         ],
31587         DOCUMENT : [
31588             {
31589                 tag : 'div',
31590                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31591                 action : 'rotate-left',
31592                 cn : [
31593                     {
31594                         tag : 'button',
31595                         cls : 'btn btn-default',
31596                         html : '<i class="fa fa-undo"></i>'
31597                     }
31598                 ]
31599             },
31600             {
31601                 tag : 'div',
31602                 cls : 'btn-group roo-upload-cropbox-download',
31603                 action : 'download',
31604                 cn : [
31605                     {
31606                         tag : 'button',
31607                         cls : 'btn btn-default',
31608                         html : '<i class="fa fa-download"></i>'
31609                     }
31610                 ]
31611             },
31612             {
31613                 tag : 'div',
31614                 cls : 'btn-group roo-upload-cropbox-crop',
31615                 action : 'crop',
31616                 cn : [
31617                     {
31618                         tag : 'button',
31619                         cls : 'btn btn-default',
31620                         html : '<i class="fa fa-crop"></i>'
31621                     }
31622                 ]
31623             },
31624             {
31625                 tag : 'div',
31626                 cls : 'btn-group roo-upload-cropbox-trash',
31627                 action : 'trash',
31628                 cn : [
31629                     {
31630                         tag : 'button',
31631                         cls : 'btn btn-default',
31632                         html : '<i class="fa fa-trash"></i>'
31633                     }
31634                 ]
31635             },
31636             {
31637                 tag : 'div',
31638                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31639                 action : 'rotate-right',
31640                 cn : [
31641                     {
31642                         tag : 'button',
31643                         cls : 'btn btn-default',
31644                         html : '<i class="fa fa-repeat"></i>'
31645                     }
31646                 ]
31647             }
31648         ],
31649         ROTATOR : [
31650             {
31651                 tag : 'div',
31652                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31653                 action : 'rotate-left',
31654                 cn : [
31655                     {
31656                         tag : 'button',
31657                         cls : 'btn btn-default',
31658                         html : '<i class="fa fa-undo"></i>'
31659                     }
31660                 ]
31661             },
31662             {
31663                 tag : 'div',
31664                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31665                 action : 'rotate-right',
31666                 cn : [
31667                     {
31668                         tag : 'button',
31669                         cls : 'btn btn-default',
31670                         html : '<i class="fa fa-repeat"></i>'
31671                     }
31672                 ]
31673             }
31674         ]
31675     }
31676 });
31677
31678 /*
31679 * Licence: LGPL
31680 */
31681
31682 /**
31683  * @class Roo.bootstrap.DocumentManager
31684  * @extends Roo.bootstrap.Component
31685  * Bootstrap DocumentManager class
31686  * @cfg {String} paramName default 'imageUpload'
31687  * @cfg {String} toolTipName default 'filename'
31688  * @cfg {String} method default POST
31689  * @cfg {String} url action url
31690  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31691  * @cfg {Boolean} multiple multiple upload default true
31692  * @cfg {Number} thumbSize default 300
31693  * @cfg {String} fieldLabel
31694  * @cfg {Number} labelWidth default 4
31695  * @cfg {String} labelAlign (left|top) default left
31696  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31697 * @cfg {Number} labellg set the width of label (1-12)
31698  * @cfg {Number} labelmd set the width of label (1-12)
31699  * @cfg {Number} labelsm set the width of label (1-12)
31700  * @cfg {Number} labelxs set the width of label (1-12)
31701  * 
31702  * @constructor
31703  * Create a new DocumentManager
31704  * @param {Object} config The config object
31705  */
31706
31707 Roo.bootstrap.DocumentManager = function(config){
31708     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31709     
31710     this.files = [];
31711     this.delegates = [];
31712     
31713     this.addEvents({
31714         /**
31715          * @event initial
31716          * Fire when initial the DocumentManager
31717          * @param {Roo.bootstrap.DocumentManager} this
31718          */
31719         "initial" : true,
31720         /**
31721          * @event inspect
31722          * inspect selected file
31723          * @param {Roo.bootstrap.DocumentManager} this
31724          * @param {File} file
31725          */
31726         "inspect" : true,
31727         /**
31728          * @event exception
31729          * Fire when xhr load exception
31730          * @param {Roo.bootstrap.DocumentManager} this
31731          * @param {XMLHttpRequest} xhr
31732          */
31733         "exception" : true,
31734         /**
31735          * @event afterupload
31736          * Fire when xhr load exception
31737          * @param {Roo.bootstrap.DocumentManager} this
31738          * @param {XMLHttpRequest} xhr
31739          */
31740         "afterupload" : true,
31741         /**
31742          * @event prepare
31743          * prepare the form data
31744          * @param {Roo.bootstrap.DocumentManager} this
31745          * @param {Object} formData
31746          */
31747         "prepare" : true,
31748         /**
31749          * @event remove
31750          * Fire when remove the file
31751          * @param {Roo.bootstrap.DocumentManager} this
31752          * @param {Object} file
31753          */
31754         "remove" : true,
31755         /**
31756          * @event refresh
31757          * Fire after refresh the file
31758          * @param {Roo.bootstrap.DocumentManager} this
31759          */
31760         "refresh" : true,
31761         /**
31762          * @event click
31763          * Fire after click the image
31764          * @param {Roo.bootstrap.DocumentManager} this
31765          * @param {Object} file
31766          */
31767         "click" : true,
31768         /**
31769          * @event edit
31770          * Fire when upload a image and editable set to true
31771          * @param {Roo.bootstrap.DocumentManager} this
31772          * @param {Object} file
31773          */
31774         "edit" : true,
31775         /**
31776          * @event beforeselectfile
31777          * Fire before select file
31778          * @param {Roo.bootstrap.DocumentManager} this
31779          */
31780         "beforeselectfile" : true,
31781         /**
31782          * @event process
31783          * Fire before process file
31784          * @param {Roo.bootstrap.DocumentManager} this
31785          * @param {Object} file
31786          */
31787         "process" : true,
31788         /**
31789          * @event previewrendered
31790          * Fire when preview rendered
31791          * @param {Roo.bootstrap.DocumentManager} this
31792          * @param {Object} file
31793          */
31794         "previewrendered" : true,
31795         /**
31796          */
31797         "previewResize" : true
31798         
31799     });
31800 };
31801
31802 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31803     
31804     boxes : 0,
31805     inputName : '',
31806     thumbSize : 300,
31807     multiple : true,
31808     files : false,
31809     method : 'POST',
31810     url : '',
31811     paramName : 'imageUpload',
31812     toolTipName : 'filename',
31813     fieldLabel : '',
31814     labelWidth : 4,
31815     labelAlign : 'left',
31816     editable : true,
31817     delegates : false,
31818     xhr : false, 
31819     
31820     labellg : 0,
31821     labelmd : 0,
31822     labelsm : 0,
31823     labelxs : 0,
31824     
31825     getAutoCreate : function()
31826     {   
31827         var managerWidget = {
31828             tag : 'div',
31829             cls : 'roo-document-manager',
31830             cn : [
31831                 {
31832                     tag : 'input',
31833                     cls : 'roo-document-manager-selector',
31834                     type : 'file'
31835                 },
31836                 {
31837                     tag : 'div',
31838                     cls : 'roo-document-manager-uploader',
31839                     cn : [
31840                         {
31841                             tag : 'div',
31842                             cls : 'roo-document-manager-upload-btn',
31843                             html : '<i class="fa fa-plus"></i>'
31844                         }
31845                     ]
31846                     
31847                 }
31848             ]
31849         };
31850         
31851         var content = [
31852             {
31853                 tag : 'div',
31854                 cls : 'column col-md-12',
31855                 cn : managerWidget
31856             }
31857         ];
31858         
31859         if(this.fieldLabel.length){
31860             
31861             content = [
31862                 {
31863                     tag : 'div',
31864                     cls : 'column col-md-12',
31865                     html : this.fieldLabel
31866                 },
31867                 {
31868                     tag : 'div',
31869                     cls : 'column col-md-12',
31870                     cn : managerWidget
31871                 }
31872             ];
31873
31874             if(this.labelAlign == 'left'){
31875                 content = [
31876                     {
31877                         tag : 'div',
31878                         cls : 'column',
31879                         html : this.fieldLabel
31880                     },
31881                     {
31882                         tag : 'div',
31883                         cls : 'column',
31884                         cn : managerWidget
31885                     }
31886                 ];
31887                 
31888                 if(this.labelWidth > 12){
31889                     content[0].style = "width: " + this.labelWidth + 'px';
31890                 }
31891
31892                 if(this.labelWidth < 13 && this.labelmd == 0){
31893                     this.labelmd = this.labelWidth;
31894                 }
31895
31896                 if(this.labellg > 0){
31897                     content[0].cls += ' col-lg-' + this.labellg;
31898                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31899                 }
31900
31901                 if(this.labelmd > 0){
31902                     content[0].cls += ' col-md-' + this.labelmd;
31903                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31904                 }
31905
31906                 if(this.labelsm > 0){
31907                     content[0].cls += ' col-sm-' + this.labelsm;
31908                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31909                 }
31910
31911                 if(this.labelxs > 0){
31912                     content[0].cls += ' col-xs-' + this.labelxs;
31913                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31914                 }
31915                 
31916             }
31917         }
31918         
31919         var cfg = {
31920             tag : 'div',
31921             cls : 'row clearfix',
31922             cn : content
31923         };
31924         
31925         return cfg;
31926         
31927     },
31928     
31929     initEvents : function()
31930     {
31931         this.managerEl = this.el.select('.roo-document-manager', true).first();
31932         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31933         
31934         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31935         this.selectorEl.hide();
31936         
31937         if(this.multiple){
31938             this.selectorEl.attr('multiple', 'multiple');
31939         }
31940         
31941         this.selectorEl.on('change', this.onFileSelected, this);
31942         
31943         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31944         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31945         
31946         this.uploader.on('click', this.onUploaderClick, this);
31947         
31948         this.renderProgressDialog();
31949         
31950         var _this = this;
31951         
31952         window.addEventListener("resize", function() { _this.refresh(); } );
31953         
31954         this.fireEvent('initial', this);
31955     },
31956     
31957     renderProgressDialog : function()
31958     {
31959         var _this = this;
31960         
31961         this.progressDialog = new Roo.bootstrap.Modal({
31962             cls : 'roo-document-manager-progress-dialog',
31963             allow_close : false,
31964             animate : false,
31965             title : '',
31966             buttons : [
31967                 {
31968                     name  :'cancel',
31969                     weight : 'danger',
31970                     html : 'Cancel'
31971                 }
31972             ], 
31973             listeners : { 
31974                 btnclick : function() {
31975                     _this.uploadCancel();
31976                     this.hide();
31977                 }
31978             }
31979         });
31980          
31981         this.progressDialog.render(Roo.get(document.body));
31982          
31983         this.progress = new Roo.bootstrap.Progress({
31984             cls : 'roo-document-manager-progress',
31985             active : true,
31986             striped : true
31987         });
31988         
31989         this.progress.render(this.progressDialog.getChildContainer());
31990         
31991         this.progressBar = new Roo.bootstrap.ProgressBar({
31992             cls : 'roo-document-manager-progress-bar',
31993             aria_valuenow : 0,
31994             aria_valuemin : 0,
31995             aria_valuemax : 12,
31996             panel : 'success'
31997         });
31998         
31999         this.progressBar.render(this.progress.getChildContainer());
32000     },
32001     
32002     onUploaderClick : function(e)
32003     {
32004         e.preventDefault();
32005      
32006         if(this.fireEvent('beforeselectfile', this) != false){
32007             this.selectorEl.dom.click();
32008         }
32009         
32010     },
32011     
32012     onFileSelected : function(e)
32013     {
32014         e.preventDefault();
32015         
32016         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32017             return;
32018         }
32019         
32020         Roo.each(this.selectorEl.dom.files, function(file){
32021             if(this.fireEvent('inspect', this, file) != false){
32022                 this.files.push(file);
32023             }
32024         }, this);
32025         
32026         this.queue();
32027         
32028     },
32029     
32030     queue : function()
32031     {
32032         this.selectorEl.dom.value = '';
32033         
32034         if(!this.files || !this.files.length){
32035             return;
32036         }
32037         
32038         if(this.boxes > 0 && this.files.length > this.boxes){
32039             this.files = this.files.slice(0, this.boxes);
32040         }
32041         
32042         this.uploader.show();
32043         
32044         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32045             this.uploader.hide();
32046         }
32047         
32048         var _this = this;
32049         
32050         var files = [];
32051         
32052         var docs = [];
32053         
32054         Roo.each(this.files, function(file){
32055             
32056             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32057                 var f = this.renderPreview(file);
32058                 files.push(f);
32059                 return;
32060             }
32061             
32062             if(file.type.indexOf('image') != -1){
32063                 this.delegates.push(
32064                     (function(){
32065                         _this.process(file);
32066                     }).createDelegate(this)
32067                 );
32068         
32069                 return;
32070             }
32071             
32072             docs.push(
32073                 (function(){
32074                     _this.process(file);
32075                 }).createDelegate(this)
32076             );
32077             
32078         }, this);
32079         
32080         this.files = files;
32081         
32082         this.delegates = this.delegates.concat(docs);
32083         
32084         if(!this.delegates.length){
32085             this.refresh();
32086             return;
32087         }
32088         
32089         this.progressBar.aria_valuemax = this.delegates.length;
32090         
32091         this.arrange();
32092         
32093         return;
32094     },
32095     
32096     arrange : function()
32097     {
32098         if(!this.delegates.length){
32099             this.progressDialog.hide();
32100             this.refresh();
32101             return;
32102         }
32103         
32104         var delegate = this.delegates.shift();
32105         
32106         this.progressDialog.show();
32107         
32108         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32109         
32110         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32111         
32112         delegate();
32113     },
32114     
32115     refresh : function()
32116     {
32117         this.uploader.show();
32118         
32119         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32120             this.uploader.hide();
32121         }
32122         
32123         Roo.isTouch ? this.closable(false) : this.closable(true);
32124         
32125         this.fireEvent('refresh', this);
32126     },
32127     
32128     onRemove : function(e, el, o)
32129     {
32130         e.preventDefault();
32131         
32132         this.fireEvent('remove', this, o);
32133         
32134     },
32135     
32136     remove : function(o)
32137     {
32138         var files = [];
32139         
32140         Roo.each(this.files, function(file){
32141             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32142                 files.push(file);
32143                 return;
32144             }
32145
32146             o.target.remove();
32147
32148         }, this);
32149         
32150         this.files = files;
32151         
32152         this.refresh();
32153     },
32154     
32155     clear : function()
32156     {
32157         Roo.each(this.files, function(file){
32158             if(!file.target){
32159                 return;
32160             }
32161             
32162             file.target.remove();
32163
32164         }, this);
32165         
32166         this.files = [];
32167         
32168         this.refresh();
32169     },
32170     
32171     onClick : function(e, el, o)
32172     {
32173         e.preventDefault();
32174         
32175         this.fireEvent('click', this, o);
32176         
32177     },
32178     
32179     closable : function(closable)
32180     {
32181         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32182             
32183             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32184             
32185             if(closable){
32186                 el.show();
32187                 return;
32188             }
32189             
32190             el.hide();
32191             
32192         }, this);
32193     },
32194     
32195     xhrOnLoad : function(xhr)
32196     {
32197         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32198             el.remove();
32199         }, this);
32200         
32201         if (xhr.readyState !== 4) {
32202             this.arrange();
32203             this.fireEvent('exception', this, xhr);
32204             return;
32205         }
32206
32207         var response = Roo.decode(xhr.responseText);
32208         
32209         if(!response.success){
32210             this.arrange();
32211             this.fireEvent('exception', this, xhr);
32212             return;
32213         }
32214         
32215         var file = this.renderPreview(response.data);
32216         
32217         this.files.push(file);
32218         
32219         this.arrange();
32220         
32221         this.fireEvent('afterupload', this, xhr);
32222         
32223     },
32224     
32225     xhrOnError : function(xhr)
32226     {
32227         Roo.log('xhr on error');
32228         
32229         var response = Roo.decode(xhr.responseText);
32230           
32231         Roo.log(response);
32232         
32233         this.arrange();
32234     },
32235     
32236     process : function(file)
32237     {
32238         if(this.fireEvent('process', this, file) !== false){
32239             if(this.editable && file.type.indexOf('image') != -1){
32240                 this.fireEvent('edit', this, file);
32241                 return;
32242             }
32243
32244             this.uploadStart(file, false);
32245
32246             return;
32247         }
32248         
32249     },
32250     
32251     uploadStart : function(file, crop)
32252     {
32253         this.xhr = new XMLHttpRequest();
32254         
32255         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32256             this.arrange();
32257             return;
32258         }
32259         
32260         file.xhr = this.xhr;
32261             
32262         this.managerEl.createChild({
32263             tag : 'div',
32264             cls : 'roo-document-manager-loading',
32265             cn : [
32266                 {
32267                     tag : 'div',
32268                     tooltip : file.name,
32269                     cls : 'roo-document-manager-thumb',
32270                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32271                 }
32272             ]
32273
32274         });
32275
32276         this.xhr.open(this.method, this.url, true);
32277         
32278         var headers = {
32279             "Accept": "application/json",
32280             "Cache-Control": "no-cache",
32281             "X-Requested-With": "XMLHttpRequest"
32282         };
32283         
32284         for (var headerName in headers) {
32285             var headerValue = headers[headerName];
32286             if (headerValue) {
32287                 this.xhr.setRequestHeader(headerName, headerValue);
32288             }
32289         }
32290         
32291         var _this = this;
32292         
32293         this.xhr.onload = function()
32294         {
32295             _this.xhrOnLoad(_this.xhr);
32296         }
32297         
32298         this.xhr.onerror = function()
32299         {
32300             _this.xhrOnError(_this.xhr);
32301         }
32302         
32303         var formData = new FormData();
32304
32305         formData.append('returnHTML', 'NO');
32306         
32307         if(crop){
32308             formData.append('crop', crop);
32309         }
32310         
32311         formData.append(this.paramName, file, file.name);
32312         
32313         var options = {
32314             file : file, 
32315             manually : false
32316         };
32317         
32318         if(this.fireEvent('prepare', this, formData, options) != false){
32319             
32320             if(options.manually){
32321                 return;
32322             }
32323             
32324             this.xhr.send(formData);
32325             return;
32326         };
32327         
32328         this.uploadCancel();
32329     },
32330     
32331     uploadCancel : function()
32332     {
32333         if (this.xhr) {
32334             this.xhr.abort();
32335         }
32336         
32337         this.delegates = [];
32338         
32339         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32340             el.remove();
32341         }, this);
32342         
32343         this.arrange();
32344     },
32345     
32346     renderPreview : function(file)
32347     {
32348         if(typeof(file.target) != 'undefined' && file.target){
32349             return file;
32350         }
32351         
32352         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32353         
32354         var previewEl = this.managerEl.createChild({
32355             tag : 'div',
32356             cls : 'roo-document-manager-preview',
32357             cn : [
32358                 {
32359                     tag : 'div',
32360                     tooltip : file[this.toolTipName],
32361                     cls : 'roo-document-manager-thumb',
32362                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32363                 },
32364                 {
32365                     tag : 'button',
32366                     cls : 'close',
32367                     html : '<i class="fa fa-times-circle"></i>'
32368                 }
32369             ]
32370         });
32371
32372         var close = previewEl.select('button.close', true).first();
32373
32374         close.on('click', this.onRemove, this, file);
32375
32376         file.target = previewEl;
32377
32378         var image = previewEl.select('img', true).first();
32379         
32380         var _this = this;
32381         
32382         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32383         
32384         image.on('click', this.onClick, this, file);
32385         
32386         this.fireEvent('previewrendered', this, file);
32387         
32388         return file;
32389         
32390     },
32391     
32392     onPreviewLoad : function(file, image)
32393     {
32394         if(typeof(file.target) == 'undefined' || !file.target){
32395             return;
32396         }
32397         
32398         var width = image.dom.naturalWidth || image.dom.width;
32399         var height = image.dom.naturalHeight || image.dom.height;
32400         
32401         if(!this.previewResize) {
32402             return;
32403         }
32404         
32405         if(width > height){
32406             file.target.addClass('wide');
32407             return;
32408         }
32409         
32410         file.target.addClass('tall');
32411         return;
32412         
32413     },
32414     
32415     uploadFromSource : function(file, crop)
32416     {
32417         this.xhr = new XMLHttpRequest();
32418         
32419         this.managerEl.createChild({
32420             tag : 'div',
32421             cls : 'roo-document-manager-loading',
32422             cn : [
32423                 {
32424                     tag : 'div',
32425                     tooltip : file.name,
32426                     cls : 'roo-document-manager-thumb',
32427                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32428                 }
32429             ]
32430
32431         });
32432
32433         this.xhr.open(this.method, this.url, true);
32434         
32435         var headers = {
32436             "Accept": "application/json",
32437             "Cache-Control": "no-cache",
32438             "X-Requested-With": "XMLHttpRequest"
32439         };
32440         
32441         for (var headerName in headers) {
32442             var headerValue = headers[headerName];
32443             if (headerValue) {
32444                 this.xhr.setRequestHeader(headerName, headerValue);
32445             }
32446         }
32447         
32448         var _this = this;
32449         
32450         this.xhr.onload = function()
32451         {
32452             _this.xhrOnLoad(_this.xhr);
32453         }
32454         
32455         this.xhr.onerror = function()
32456         {
32457             _this.xhrOnError(_this.xhr);
32458         }
32459         
32460         var formData = new FormData();
32461
32462         formData.append('returnHTML', 'NO');
32463         
32464         formData.append('crop', crop);
32465         
32466         if(typeof(file.filename) != 'undefined'){
32467             formData.append('filename', file.filename);
32468         }
32469         
32470         if(typeof(file.mimetype) != 'undefined'){
32471             formData.append('mimetype', file.mimetype);
32472         }
32473         
32474         Roo.log(formData);
32475         
32476         if(this.fireEvent('prepare', this, formData) != false){
32477             this.xhr.send(formData);
32478         };
32479     }
32480 });
32481
32482 /*
32483 * Licence: LGPL
32484 */
32485
32486 /**
32487  * @class Roo.bootstrap.DocumentViewer
32488  * @extends Roo.bootstrap.Component
32489  * Bootstrap DocumentViewer class
32490  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32491  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32492  * 
32493  * @constructor
32494  * Create a new DocumentViewer
32495  * @param {Object} config The config object
32496  */
32497
32498 Roo.bootstrap.DocumentViewer = function(config){
32499     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32500     
32501     this.addEvents({
32502         /**
32503          * @event initial
32504          * Fire after initEvent
32505          * @param {Roo.bootstrap.DocumentViewer} this
32506          */
32507         "initial" : true,
32508         /**
32509          * @event click
32510          * Fire after click
32511          * @param {Roo.bootstrap.DocumentViewer} this
32512          */
32513         "click" : true,
32514         /**
32515          * @event download
32516          * Fire after download button
32517          * @param {Roo.bootstrap.DocumentViewer} this
32518          */
32519         "download" : true,
32520         /**
32521          * @event trash
32522          * Fire after trash button
32523          * @param {Roo.bootstrap.DocumentViewer} this
32524          */
32525         "trash" : true
32526         
32527     });
32528 };
32529
32530 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32531     
32532     showDownload : true,
32533     
32534     showTrash : true,
32535     
32536     getAutoCreate : function()
32537     {
32538         var cfg = {
32539             tag : 'div',
32540             cls : 'roo-document-viewer',
32541             cn : [
32542                 {
32543                     tag : 'div',
32544                     cls : 'roo-document-viewer-body',
32545                     cn : [
32546                         {
32547                             tag : 'div',
32548                             cls : 'roo-document-viewer-thumb',
32549                             cn : [
32550                                 {
32551                                     tag : 'img',
32552                                     cls : 'roo-document-viewer-image'
32553                                 }
32554                             ]
32555                         }
32556                     ]
32557                 },
32558                 {
32559                     tag : 'div',
32560                     cls : 'roo-document-viewer-footer',
32561                     cn : {
32562                         tag : 'div',
32563                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32564                         cn : [
32565                             {
32566                                 tag : 'div',
32567                                 cls : 'btn-group roo-document-viewer-download',
32568                                 cn : [
32569                                     {
32570                                         tag : 'button',
32571                                         cls : 'btn btn-default',
32572                                         html : '<i class="fa fa-download"></i>'
32573                                     }
32574                                 ]
32575                             },
32576                             {
32577                                 tag : 'div',
32578                                 cls : 'btn-group roo-document-viewer-trash',
32579                                 cn : [
32580                                     {
32581                                         tag : 'button',
32582                                         cls : 'btn btn-default',
32583                                         html : '<i class="fa fa-trash"></i>'
32584                                     }
32585                                 ]
32586                             }
32587                         ]
32588                     }
32589                 }
32590             ]
32591         };
32592         
32593         return cfg;
32594     },
32595     
32596     initEvents : function()
32597     {
32598         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32599         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32600         
32601         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32602         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32603         
32604         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32605         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32606         
32607         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32608         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32609         
32610         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32611         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32612         
32613         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32614         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32615         
32616         this.bodyEl.on('click', this.onClick, this);
32617         this.downloadBtn.on('click', this.onDownload, this);
32618         this.trashBtn.on('click', this.onTrash, this);
32619         
32620         this.downloadBtn.hide();
32621         this.trashBtn.hide();
32622         
32623         if(this.showDownload){
32624             this.downloadBtn.show();
32625         }
32626         
32627         if(this.showTrash){
32628             this.trashBtn.show();
32629         }
32630         
32631         if(!this.showDownload && !this.showTrash) {
32632             this.footerEl.hide();
32633         }
32634         
32635     },
32636     
32637     initial : function()
32638     {
32639         this.fireEvent('initial', this);
32640         
32641     },
32642     
32643     onClick : function(e)
32644     {
32645         e.preventDefault();
32646         
32647         this.fireEvent('click', this);
32648     },
32649     
32650     onDownload : function(e)
32651     {
32652         e.preventDefault();
32653         
32654         this.fireEvent('download', this);
32655     },
32656     
32657     onTrash : function(e)
32658     {
32659         e.preventDefault();
32660         
32661         this.fireEvent('trash', this);
32662     }
32663     
32664 });
32665 /*
32666  * - LGPL
32667  *
32668  * nav progress bar
32669  * 
32670  */
32671
32672 /**
32673  * @class Roo.bootstrap.NavProgressBar
32674  * @extends Roo.bootstrap.Component
32675  * Bootstrap NavProgressBar class
32676  * 
32677  * @constructor
32678  * Create a new nav progress bar
32679  * @param {Object} config The config object
32680  */
32681
32682 Roo.bootstrap.NavProgressBar = function(config){
32683     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32684
32685     this.bullets = this.bullets || [];
32686    
32687 //    Roo.bootstrap.NavProgressBar.register(this);
32688      this.addEvents({
32689         /**
32690              * @event changed
32691              * Fires when the active item changes
32692              * @param {Roo.bootstrap.NavProgressBar} this
32693              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32694              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32695          */
32696         'changed': true
32697      });
32698     
32699 };
32700
32701 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32702     
32703     bullets : [],
32704     barItems : [],
32705     
32706     getAutoCreate : function()
32707     {
32708         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32709         
32710         cfg = {
32711             tag : 'div',
32712             cls : 'roo-navigation-bar-group',
32713             cn : [
32714                 {
32715                     tag : 'div',
32716                     cls : 'roo-navigation-top-bar'
32717                 },
32718                 {
32719                     tag : 'div',
32720                     cls : 'roo-navigation-bullets-bar',
32721                     cn : [
32722                         {
32723                             tag : 'ul',
32724                             cls : 'roo-navigation-bar'
32725                         }
32726                     ]
32727                 },
32728                 
32729                 {
32730                     tag : 'div',
32731                     cls : 'roo-navigation-bottom-bar'
32732                 }
32733             ]
32734             
32735         };
32736         
32737         return cfg;
32738         
32739     },
32740     
32741     initEvents: function() 
32742     {
32743         
32744     },
32745     
32746     onRender : function(ct, position) 
32747     {
32748         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32749         
32750         if(this.bullets.length){
32751             Roo.each(this.bullets, function(b){
32752                this.addItem(b);
32753             }, this);
32754         }
32755         
32756         this.format();
32757         
32758     },
32759     
32760     addItem : function(cfg)
32761     {
32762         var item = new Roo.bootstrap.NavProgressItem(cfg);
32763         
32764         item.parentId = this.id;
32765         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32766         
32767         if(cfg.html){
32768             var top = new Roo.bootstrap.Element({
32769                 tag : 'div',
32770                 cls : 'roo-navigation-bar-text'
32771             });
32772             
32773             var bottom = new Roo.bootstrap.Element({
32774                 tag : 'div',
32775                 cls : 'roo-navigation-bar-text'
32776             });
32777             
32778             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32779             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32780             
32781             var topText = new Roo.bootstrap.Element({
32782                 tag : 'span',
32783                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32784             });
32785             
32786             var bottomText = new Roo.bootstrap.Element({
32787                 tag : 'span',
32788                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32789             });
32790             
32791             topText.onRender(top.el, null);
32792             bottomText.onRender(bottom.el, null);
32793             
32794             item.topEl = top;
32795             item.bottomEl = bottom;
32796         }
32797         
32798         this.barItems.push(item);
32799         
32800         return item;
32801     },
32802     
32803     getActive : function()
32804     {
32805         var active = false;
32806         
32807         Roo.each(this.barItems, function(v){
32808             
32809             if (!v.isActive()) {
32810                 return;
32811             }
32812             
32813             active = v;
32814             return false;
32815             
32816         });
32817         
32818         return active;
32819     },
32820     
32821     setActiveItem : function(item)
32822     {
32823         var prev = false;
32824         
32825         Roo.each(this.barItems, function(v){
32826             if (v.rid == item.rid) {
32827                 return ;
32828             }
32829             
32830             if (v.isActive()) {
32831                 v.setActive(false);
32832                 prev = v;
32833             }
32834         });
32835
32836         item.setActive(true);
32837         
32838         this.fireEvent('changed', this, item, prev);
32839     },
32840     
32841     getBarItem: function(rid)
32842     {
32843         var ret = false;
32844         
32845         Roo.each(this.barItems, function(e) {
32846             if (e.rid != rid) {
32847                 return;
32848             }
32849             
32850             ret =  e;
32851             return false;
32852         });
32853         
32854         return ret;
32855     },
32856     
32857     indexOfItem : function(item)
32858     {
32859         var index = false;
32860         
32861         Roo.each(this.barItems, function(v, i){
32862             
32863             if (v.rid != item.rid) {
32864                 return;
32865             }
32866             
32867             index = i;
32868             return false
32869         });
32870         
32871         return index;
32872     },
32873     
32874     setActiveNext : function()
32875     {
32876         var i = this.indexOfItem(this.getActive());
32877         
32878         if (i > this.barItems.length) {
32879             return;
32880         }
32881         
32882         this.setActiveItem(this.barItems[i+1]);
32883     },
32884     
32885     setActivePrev : function()
32886     {
32887         var i = this.indexOfItem(this.getActive());
32888         
32889         if (i  < 1) {
32890             return;
32891         }
32892         
32893         this.setActiveItem(this.barItems[i-1]);
32894     },
32895     
32896     format : function()
32897     {
32898         if(!this.barItems.length){
32899             return;
32900         }
32901      
32902         var width = 100 / this.barItems.length;
32903         
32904         Roo.each(this.barItems, function(i){
32905             i.el.setStyle('width', width + '%');
32906             i.topEl.el.setStyle('width', width + '%');
32907             i.bottomEl.el.setStyle('width', width + '%');
32908         }, this);
32909         
32910     }
32911     
32912 });
32913 /*
32914  * - LGPL
32915  *
32916  * Nav Progress Item
32917  * 
32918  */
32919
32920 /**
32921  * @class Roo.bootstrap.NavProgressItem
32922  * @extends Roo.bootstrap.Component
32923  * Bootstrap NavProgressItem class
32924  * @cfg {String} rid the reference id
32925  * @cfg {Boolean} active (true|false) Is item active default false
32926  * @cfg {Boolean} disabled (true|false) Is item active default false
32927  * @cfg {String} html
32928  * @cfg {String} position (top|bottom) text position default bottom
32929  * @cfg {String} icon show icon instead of number
32930  * 
32931  * @constructor
32932  * Create a new NavProgressItem
32933  * @param {Object} config The config object
32934  */
32935 Roo.bootstrap.NavProgressItem = function(config){
32936     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32937     this.addEvents({
32938         // raw events
32939         /**
32940          * @event click
32941          * The raw click event for the entire grid.
32942          * @param {Roo.bootstrap.NavProgressItem} this
32943          * @param {Roo.EventObject} e
32944          */
32945         "click" : true
32946     });
32947    
32948 };
32949
32950 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32951     
32952     rid : '',
32953     active : false,
32954     disabled : false,
32955     html : '',
32956     position : 'bottom',
32957     icon : false,
32958     
32959     getAutoCreate : function()
32960     {
32961         var iconCls = 'roo-navigation-bar-item-icon';
32962         
32963         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32964         
32965         var cfg = {
32966             tag: 'li',
32967             cls: 'roo-navigation-bar-item',
32968             cn : [
32969                 {
32970                     tag : 'i',
32971                     cls : iconCls
32972                 }
32973             ]
32974         };
32975         
32976         if(this.active){
32977             cfg.cls += ' active';
32978         }
32979         if(this.disabled){
32980             cfg.cls += ' disabled';
32981         }
32982         
32983         return cfg;
32984     },
32985     
32986     disable : function()
32987     {
32988         this.setDisabled(true);
32989     },
32990     
32991     enable : function()
32992     {
32993         this.setDisabled(false);
32994     },
32995     
32996     initEvents: function() 
32997     {
32998         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32999         
33000         this.iconEl.on('click', this.onClick, this);
33001     },
33002     
33003     onClick : function(e)
33004     {
33005         e.preventDefault();
33006         
33007         if(this.disabled){
33008             return;
33009         }
33010         
33011         if(this.fireEvent('click', this, e) === false){
33012             return;
33013         };
33014         
33015         this.parent().setActiveItem(this);
33016     },
33017     
33018     isActive: function () 
33019     {
33020         return this.active;
33021     },
33022     
33023     setActive : function(state)
33024     {
33025         if(this.active == state){
33026             return;
33027         }
33028         
33029         this.active = state;
33030         
33031         if (state) {
33032             this.el.addClass('active');
33033             return;
33034         }
33035         
33036         this.el.removeClass('active');
33037         
33038         return;
33039     },
33040     
33041     setDisabled : function(state)
33042     {
33043         if(this.disabled == state){
33044             return;
33045         }
33046         
33047         this.disabled = state;
33048         
33049         if (state) {
33050             this.el.addClass('disabled');
33051             return;
33052         }
33053         
33054         this.el.removeClass('disabled');
33055     },
33056     
33057     tooltipEl : function()
33058     {
33059         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33060     }
33061 });
33062  
33063
33064  /*
33065  * - LGPL
33066  *
33067  * FieldLabel
33068  * 
33069  */
33070
33071 /**
33072  * @class Roo.bootstrap.FieldLabel
33073  * @extends Roo.bootstrap.Component
33074  * Bootstrap FieldLabel class
33075  * @cfg {String} html contents of the element
33076  * @cfg {String} tag tag of the element default label
33077  * @cfg {String} cls class of the element
33078  * @cfg {String} target label target 
33079  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33080  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33081  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33082  * @cfg {String} iconTooltip default "This field is required"
33083  * @cfg {String} indicatorpos (left|right) default left
33084  * 
33085  * @constructor
33086  * Create a new FieldLabel
33087  * @param {Object} config The config object
33088  */
33089
33090 Roo.bootstrap.FieldLabel = function(config){
33091     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33092     
33093     this.addEvents({
33094             /**
33095              * @event invalid
33096              * Fires after the field has been marked as invalid.
33097              * @param {Roo.form.FieldLabel} this
33098              * @param {String} msg The validation message
33099              */
33100             invalid : true,
33101             /**
33102              * @event valid
33103              * Fires after the field has been validated with no errors.
33104              * @param {Roo.form.FieldLabel} this
33105              */
33106             valid : true
33107         });
33108 };
33109
33110 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33111     
33112     tag: 'label',
33113     cls: '',
33114     html: '',
33115     target: '',
33116     allowBlank : true,
33117     invalidClass : 'has-warning',
33118     validClass : 'has-success',
33119     iconTooltip : 'This field is required',
33120     indicatorpos : 'left',
33121     
33122     getAutoCreate : function(){
33123         
33124         var cls = "";
33125         if (!this.allowBlank) {
33126             cls  = "visible";
33127         }
33128         
33129         var cfg = {
33130             tag : this.tag,
33131             cls : 'roo-bootstrap-field-label ' + this.cls,
33132             for : this.target,
33133             cn : [
33134                 {
33135                     tag : 'i',
33136                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33137                     tooltip : this.iconTooltip
33138                 },
33139                 {
33140                     tag : 'span',
33141                     html : this.html
33142                 }
33143             ] 
33144         };
33145         
33146         if(this.indicatorpos == 'right'){
33147             var cfg = {
33148                 tag : this.tag,
33149                 cls : 'roo-bootstrap-field-label ' + this.cls,
33150                 for : this.target,
33151                 cn : [
33152                     {
33153                         tag : 'span',
33154                         html : this.html
33155                     },
33156                     {
33157                         tag : 'i',
33158                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33159                         tooltip : this.iconTooltip
33160                     }
33161                 ] 
33162             };
33163         }
33164         
33165         return cfg;
33166     },
33167     
33168     initEvents: function() 
33169     {
33170         Roo.bootstrap.Element.superclass.initEvents.call(this);
33171         
33172         this.indicator = this.indicatorEl();
33173         
33174         if(this.indicator){
33175             this.indicator.removeClass('visible');
33176             this.indicator.addClass('invisible');
33177         }
33178         
33179         Roo.bootstrap.FieldLabel.register(this);
33180     },
33181     
33182     indicatorEl : function()
33183     {
33184         var indicator = this.el.select('i.roo-required-indicator',true).first();
33185         
33186         if(!indicator){
33187             return false;
33188         }
33189         
33190         return indicator;
33191         
33192     },
33193     
33194     /**
33195      * Mark this field as valid
33196      */
33197     markValid : function()
33198     {
33199         if(this.indicator){
33200             this.indicator.removeClass('visible');
33201             this.indicator.addClass('invisible');
33202         }
33203         if (Roo.bootstrap.version == 3) {
33204             this.el.removeClass(this.invalidClass);
33205             this.el.addClass(this.validClass);
33206         } else {
33207             this.el.removeClass('is-invalid');
33208             this.el.addClass('is-valid');
33209         }
33210         
33211         
33212         this.fireEvent('valid', this);
33213     },
33214     
33215     /**
33216      * Mark this field as invalid
33217      * @param {String} msg The validation message
33218      */
33219     markInvalid : function(msg)
33220     {
33221         if(this.indicator){
33222             this.indicator.removeClass('invisible');
33223             this.indicator.addClass('visible');
33224         }
33225           if (Roo.bootstrap.version == 3) {
33226             this.el.removeClass(this.validClass);
33227             this.el.addClass(this.invalidClass);
33228         } else {
33229             this.el.removeClass('is-valid');
33230             this.el.addClass('is-invalid');
33231         }
33232         
33233         
33234         this.fireEvent('invalid', this, msg);
33235     }
33236     
33237    
33238 });
33239
33240 Roo.apply(Roo.bootstrap.FieldLabel, {
33241     
33242     groups: {},
33243     
33244      /**
33245     * register a FieldLabel Group
33246     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33247     */
33248     register : function(label)
33249     {
33250         if(this.groups.hasOwnProperty(label.target)){
33251             return;
33252         }
33253      
33254         this.groups[label.target] = label;
33255         
33256     },
33257     /**
33258     * fetch a FieldLabel Group based on the target
33259     * @param {string} target
33260     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33261     */
33262     get: function(target) {
33263         if (typeof(this.groups[target]) == 'undefined') {
33264             return false;
33265         }
33266         
33267         return this.groups[target] ;
33268     }
33269 });
33270
33271  
33272
33273  /*
33274  * - LGPL
33275  *
33276  * page DateSplitField.
33277  * 
33278  */
33279
33280
33281 /**
33282  * @class Roo.bootstrap.DateSplitField
33283  * @extends Roo.bootstrap.Component
33284  * Bootstrap DateSplitField class
33285  * @cfg {string} fieldLabel - the label associated
33286  * @cfg {Number} labelWidth set the width of label (0-12)
33287  * @cfg {String} labelAlign (top|left)
33288  * @cfg {Boolean} dayAllowBlank (true|false) default false
33289  * @cfg {Boolean} monthAllowBlank (true|false) default false
33290  * @cfg {Boolean} yearAllowBlank (true|false) default false
33291  * @cfg {string} dayPlaceholder 
33292  * @cfg {string} monthPlaceholder
33293  * @cfg {string} yearPlaceholder
33294  * @cfg {string} dayFormat default 'd'
33295  * @cfg {string} monthFormat default 'm'
33296  * @cfg {string} yearFormat default 'Y'
33297  * @cfg {Number} labellg set the width of label (1-12)
33298  * @cfg {Number} labelmd set the width of label (1-12)
33299  * @cfg {Number} labelsm set the width of label (1-12)
33300  * @cfg {Number} labelxs set the width of label (1-12)
33301
33302  *     
33303  * @constructor
33304  * Create a new DateSplitField
33305  * @param {Object} config The config object
33306  */
33307
33308 Roo.bootstrap.DateSplitField = function(config){
33309     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33310     
33311     this.addEvents({
33312         // raw events
33313          /**
33314          * @event years
33315          * getting the data of years
33316          * @param {Roo.bootstrap.DateSplitField} this
33317          * @param {Object} years
33318          */
33319         "years" : true,
33320         /**
33321          * @event days
33322          * getting the data of days
33323          * @param {Roo.bootstrap.DateSplitField} this
33324          * @param {Object} days
33325          */
33326         "days" : true,
33327         /**
33328          * @event invalid
33329          * Fires after the field has been marked as invalid.
33330          * @param {Roo.form.Field} this
33331          * @param {String} msg The validation message
33332          */
33333         invalid : true,
33334        /**
33335          * @event valid
33336          * Fires after the field has been validated with no errors.
33337          * @param {Roo.form.Field} this
33338          */
33339         valid : true
33340     });
33341 };
33342
33343 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33344     
33345     fieldLabel : '',
33346     labelAlign : 'top',
33347     labelWidth : 3,
33348     dayAllowBlank : false,
33349     monthAllowBlank : false,
33350     yearAllowBlank : false,
33351     dayPlaceholder : '',
33352     monthPlaceholder : '',
33353     yearPlaceholder : '',
33354     dayFormat : 'd',
33355     monthFormat : 'm',
33356     yearFormat : 'Y',
33357     isFormField : true,
33358     labellg : 0,
33359     labelmd : 0,
33360     labelsm : 0,
33361     labelxs : 0,
33362     
33363     getAutoCreate : function()
33364     {
33365         var cfg = {
33366             tag : 'div',
33367             cls : 'row roo-date-split-field-group',
33368             cn : [
33369                 {
33370                     tag : 'input',
33371                     type : 'hidden',
33372                     cls : 'form-hidden-field roo-date-split-field-group-value',
33373                     name : this.name
33374                 }
33375             ]
33376         };
33377         
33378         var labelCls = 'col-md-12';
33379         var contentCls = 'col-md-4';
33380         
33381         if(this.fieldLabel){
33382             
33383             var label = {
33384                 tag : 'div',
33385                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33386                 cn : [
33387                     {
33388                         tag : 'label',
33389                         html : this.fieldLabel
33390                     }
33391                 ]
33392             };
33393             
33394             if(this.labelAlign == 'left'){
33395             
33396                 if(this.labelWidth > 12){
33397                     label.style = "width: " + this.labelWidth + 'px';
33398                 }
33399
33400                 if(this.labelWidth < 13 && this.labelmd == 0){
33401                     this.labelmd = this.labelWidth;
33402                 }
33403
33404                 if(this.labellg > 0){
33405                     labelCls = ' col-lg-' + this.labellg;
33406                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33407                 }
33408
33409                 if(this.labelmd > 0){
33410                     labelCls = ' col-md-' + this.labelmd;
33411                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33412                 }
33413
33414                 if(this.labelsm > 0){
33415                     labelCls = ' col-sm-' + this.labelsm;
33416                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33417                 }
33418
33419                 if(this.labelxs > 0){
33420                     labelCls = ' col-xs-' + this.labelxs;
33421                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33422                 }
33423             }
33424             
33425             label.cls += ' ' + labelCls;
33426             
33427             cfg.cn.push(label);
33428         }
33429         
33430         Roo.each(['day', 'month', 'year'], function(t){
33431             cfg.cn.push({
33432                 tag : 'div',
33433                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33434             });
33435         }, this);
33436         
33437         return cfg;
33438     },
33439     
33440     inputEl: function ()
33441     {
33442         return this.el.select('.roo-date-split-field-group-value', true).first();
33443     },
33444     
33445     onRender : function(ct, position) 
33446     {
33447         var _this = this;
33448         
33449         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33450         
33451         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33452         
33453         this.dayField = new Roo.bootstrap.ComboBox({
33454             allowBlank : this.dayAllowBlank,
33455             alwaysQuery : true,
33456             displayField : 'value',
33457             editable : false,
33458             fieldLabel : '',
33459             forceSelection : true,
33460             mode : 'local',
33461             placeholder : this.dayPlaceholder,
33462             selectOnFocus : true,
33463             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33464             triggerAction : 'all',
33465             typeAhead : true,
33466             valueField : 'value',
33467             store : new Roo.data.SimpleStore({
33468                 data : (function() {    
33469                     var days = [];
33470                     _this.fireEvent('days', _this, days);
33471                     return days;
33472                 })(),
33473                 fields : [ 'value' ]
33474             }),
33475             listeners : {
33476                 select : function (_self, record, index)
33477                 {
33478                     _this.setValue(_this.getValue());
33479                 }
33480             }
33481         });
33482
33483         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33484         
33485         this.monthField = new Roo.bootstrap.MonthField({
33486             after : '<i class=\"fa fa-calendar\"></i>',
33487             allowBlank : this.monthAllowBlank,
33488             placeholder : this.monthPlaceholder,
33489             readOnly : true,
33490             listeners : {
33491                 render : function (_self)
33492                 {
33493                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33494                         e.preventDefault();
33495                         _self.focus();
33496                     });
33497                 },
33498                 select : function (_self, oldvalue, newvalue)
33499                 {
33500                     _this.setValue(_this.getValue());
33501                 }
33502             }
33503         });
33504         
33505         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33506         
33507         this.yearField = new Roo.bootstrap.ComboBox({
33508             allowBlank : this.yearAllowBlank,
33509             alwaysQuery : true,
33510             displayField : 'value',
33511             editable : false,
33512             fieldLabel : '',
33513             forceSelection : true,
33514             mode : 'local',
33515             placeholder : this.yearPlaceholder,
33516             selectOnFocus : true,
33517             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33518             triggerAction : 'all',
33519             typeAhead : true,
33520             valueField : 'value',
33521             store : new Roo.data.SimpleStore({
33522                 data : (function() {
33523                     var years = [];
33524                     _this.fireEvent('years', _this, years);
33525                     return years;
33526                 })(),
33527                 fields : [ 'value' ]
33528             }),
33529             listeners : {
33530                 select : function (_self, record, index)
33531                 {
33532                     _this.setValue(_this.getValue());
33533                 }
33534             }
33535         });
33536
33537         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33538     },
33539     
33540     setValue : function(v, format)
33541     {
33542         this.inputEl.dom.value = v;
33543         
33544         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33545         
33546         var d = Date.parseDate(v, f);
33547         
33548         if(!d){
33549             this.validate();
33550             return;
33551         }
33552         
33553         this.setDay(d.format(this.dayFormat));
33554         this.setMonth(d.format(this.monthFormat));
33555         this.setYear(d.format(this.yearFormat));
33556         
33557         this.validate();
33558         
33559         return;
33560     },
33561     
33562     setDay : function(v)
33563     {
33564         this.dayField.setValue(v);
33565         this.inputEl.dom.value = this.getValue();
33566         this.validate();
33567         return;
33568     },
33569     
33570     setMonth : function(v)
33571     {
33572         this.monthField.setValue(v, true);
33573         this.inputEl.dom.value = this.getValue();
33574         this.validate();
33575         return;
33576     },
33577     
33578     setYear : function(v)
33579     {
33580         this.yearField.setValue(v);
33581         this.inputEl.dom.value = this.getValue();
33582         this.validate();
33583         return;
33584     },
33585     
33586     getDay : function()
33587     {
33588         return this.dayField.getValue();
33589     },
33590     
33591     getMonth : function()
33592     {
33593         return this.monthField.getValue();
33594     },
33595     
33596     getYear : function()
33597     {
33598         return this.yearField.getValue();
33599     },
33600     
33601     getValue : function()
33602     {
33603         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33604         
33605         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33606         
33607         return date;
33608     },
33609     
33610     reset : function()
33611     {
33612         this.setDay('');
33613         this.setMonth('');
33614         this.setYear('');
33615         this.inputEl.dom.value = '';
33616         this.validate();
33617         return;
33618     },
33619     
33620     validate : function()
33621     {
33622         var d = this.dayField.validate();
33623         var m = this.monthField.validate();
33624         var y = this.yearField.validate();
33625         
33626         var valid = true;
33627         
33628         if(
33629                 (!this.dayAllowBlank && !d) ||
33630                 (!this.monthAllowBlank && !m) ||
33631                 (!this.yearAllowBlank && !y)
33632         ){
33633             valid = false;
33634         }
33635         
33636         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33637             return valid;
33638         }
33639         
33640         if(valid){
33641             this.markValid();
33642             return valid;
33643         }
33644         
33645         this.markInvalid();
33646         
33647         return valid;
33648     },
33649     
33650     markValid : function()
33651     {
33652         
33653         var label = this.el.select('label', true).first();
33654         var icon = this.el.select('i.fa-star', true).first();
33655
33656         if(label && icon){
33657             icon.remove();
33658         }
33659         
33660         this.fireEvent('valid', this);
33661     },
33662     
33663      /**
33664      * Mark this field as invalid
33665      * @param {String} msg The validation message
33666      */
33667     markInvalid : function(msg)
33668     {
33669         
33670         var label = this.el.select('label', true).first();
33671         var icon = this.el.select('i.fa-star', true).first();
33672
33673         if(label && !icon){
33674             this.el.select('.roo-date-split-field-label', true).createChild({
33675                 tag : 'i',
33676                 cls : 'text-danger fa fa-lg fa-star',
33677                 tooltip : 'This field is required',
33678                 style : 'margin-right:5px;'
33679             }, label, true);
33680         }
33681         
33682         this.fireEvent('invalid', this, msg);
33683     },
33684     
33685     clearInvalid : function()
33686     {
33687         var label = this.el.select('label', true).first();
33688         var icon = this.el.select('i.fa-star', true).first();
33689
33690         if(label && icon){
33691             icon.remove();
33692         }
33693         
33694         this.fireEvent('valid', this);
33695     },
33696     
33697     getName: function()
33698     {
33699         return this.name;
33700     }
33701     
33702 });
33703
33704  /**
33705  *
33706  * This is based on 
33707  * http://masonry.desandro.com
33708  *
33709  * The idea is to render all the bricks based on vertical width...
33710  *
33711  * The original code extends 'outlayer' - we might need to use that....
33712  * 
33713  */
33714
33715
33716 /**
33717  * @class Roo.bootstrap.LayoutMasonry
33718  * @extends Roo.bootstrap.Component
33719  * Bootstrap Layout Masonry class
33720  * 
33721  * @constructor
33722  * Create a new Element
33723  * @param {Object} config The config object
33724  */
33725
33726 Roo.bootstrap.LayoutMasonry = function(config){
33727     
33728     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33729     
33730     this.bricks = [];
33731     
33732     Roo.bootstrap.LayoutMasonry.register(this);
33733     
33734     this.addEvents({
33735         // raw events
33736         /**
33737          * @event layout
33738          * Fire after layout the items
33739          * @param {Roo.bootstrap.LayoutMasonry} this
33740          * @param {Roo.EventObject} e
33741          */
33742         "layout" : true
33743     });
33744     
33745 };
33746
33747 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33748     
33749     /**
33750      * @cfg {Boolean} isLayoutInstant = no animation?
33751      */   
33752     isLayoutInstant : false, // needed?
33753    
33754     /**
33755      * @cfg {Number} boxWidth  width of the columns
33756      */   
33757     boxWidth : 450,
33758     
33759       /**
33760      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33761      */   
33762     boxHeight : 0,
33763     
33764     /**
33765      * @cfg {Number} padWidth padding below box..
33766      */   
33767     padWidth : 10, 
33768     
33769     /**
33770      * @cfg {Number} gutter gutter width..
33771      */   
33772     gutter : 10,
33773     
33774      /**
33775      * @cfg {Number} maxCols maximum number of columns
33776      */   
33777     
33778     maxCols: 0,
33779     
33780     /**
33781      * @cfg {Boolean} isAutoInitial defalut true
33782      */   
33783     isAutoInitial : true, 
33784     
33785     containerWidth: 0,
33786     
33787     /**
33788      * @cfg {Boolean} isHorizontal defalut false
33789      */   
33790     isHorizontal : false, 
33791
33792     currentSize : null,
33793     
33794     tag: 'div',
33795     
33796     cls: '',
33797     
33798     bricks: null, //CompositeElement
33799     
33800     cols : 1,
33801     
33802     _isLayoutInited : false,
33803     
33804 //    isAlternative : false, // only use for vertical layout...
33805     
33806     /**
33807      * @cfg {Number} alternativePadWidth padding below box..
33808      */   
33809     alternativePadWidth : 50,
33810     
33811     selectedBrick : [],
33812     
33813     getAutoCreate : function(){
33814         
33815         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33816         
33817         var cfg = {
33818             tag: this.tag,
33819             cls: 'blog-masonary-wrapper ' + this.cls,
33820             cn : {
33821                 cls : 'mas-boxes masonary'
33822             }
33823         };
33824         
33825         return cfg;
33826     },
33827     
33828     getChildContainer: function( )
33829     {
33830         if (this.boxesEl) {
33831             return this.boxesEl;
33832         }
33833         
33834         this.boxesEl = this.el.select('.mas-boxes').first();
33835         
33836         return this.boxesEl;
33837     },
33838     
33839     
33840     initEvents : function()
33841     {
33842         var _this = this;
33843         
33844         if(this.isAutoInitial){
33845             Roo.log('hook children rendered');
33846             this.on('childrenrendered', function() {
33847                 Roo.log('children rendered');
33848                 _this.initial();
33849             } ,this);
33850         }
33851     },
33852     
33853     initial : function()
33854     {
33855         this.selectedBrick = [];
33856         
33857         this.currentSize = this.el.getBox(true);
33858         
33859         Roo.EventManager.onWindowResize(this.resize, this); 
33860
33861         if(!this.isAutoInitial){
33862             this.layout();
33863             return;
33864         }
33865         
33866         this.layout();
33867         
33868         return;
33869         //this.layout.defer(500,this);
33870         
33871     },
33872     
33873     resize : function()
33874     {
33875         var cs = this.el.getBox(true);
33876         
33877         if (
33878                 this.currentSize.width == cs.width && 
33879                 this.currentSize.x == cs.x && 
33880                 this.currentSize.height == cs.height && 
33881                 this.currentSize.y == cs.y 
33882         ) {
33883             Roo.log("no change in with or X or Y");
33884             return;
33885         }
33886         
33887         this.currentSize = cs;
33888         
33889         this.layout();
33890         
33891     },
33892     
33893     layout : function()
33894     {   
33895         this._resetLayout();
33896         
33897         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33898         
33899         this.layoutItems( isInstant );
33900       
33901         this._isLayoutInited = true;
33902         
33903         this.fireEvent('layout', this);
33904         
33905     },
33906     
33907     _resetLayout : function()
33908     {
33909         if(this.isHorizontal){
33910             this.horizontalMeasureColumns();
33911             return;
33912         }
33913         
33914         this.verticalMeasureColumns();
33915         
33916     },
33917     
33918     verticalMeasureColumns : function()
33919     {
33920         this.getContainerWidth();
33921         
33922 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33923 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33924 //            return;
33925 //        }
33926         
33927         var boxWidth = this.boxWidth + this.padWidth;
33928         
33929         if(this.containerWidth < this.boxWidth){
33930             boxWidth = this.containerWidth
33931         }
33932         
33933         var containerWidth = this.containerWidth;
33934         
33935         var cols = Math.floor(containerWidth / boxWidth);
33936         
33937         this.cols = Math.max( cols, 1 );
33938         
33939         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33940         
33941         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33942         
33943         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33944         
33945         this.colWidth = boxWidth + avail - this.padWidth;
33946         
33947         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33948         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33949     },
33950     
33951     horizontalMeasureColumns : function()
33952     {
33953         this.getContainerWidth();
33954         
33955         var boxWidth = this.boxWidth;
33956         
33957         if(this.containerWidth < boxWidth){
33958             boxWidth = this.containerWidth;
33959         }
33960         
33961         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33962         
33963         this.el.setHeight(boxWidth);
33964         
33965     },
33966     
33967     getContainerWidth : function()
33968     {
33969         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33970     },
33971     
33972     layoutItems : function( isInstant )
33973     {
33974         Roo.log(this.bricks);
33975         
33976         var items = Roo.apply([], this.bricks);
33977         
33978         if(this.isHorizontal){
33979             this._horizontalLayoutItems( items , isInstant );
33980             return;
33981         }
33982         
33983 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33984 //            this._verticalAlternativeLayoutItems( items , isInstant );
33985 //            return;
33986 //        }
33987         
33988         this._verticalLayoutItems( items , isInstant );
33989         
33990     },
33991     
33992     _verticalLayoutItems : function ( items , isInstant)
33993     {
33994         if ( !items || !items.length ) {
33995             return;
33996         }
33997         
33998         var standard = [
33999             ['xs', 'xs', 'xs', 'tall'],
34000             ['xs', 'xs', 'tall'],
34001             ['xs', 'xs', 'sm'],
34002             ['xs', 'xs', 'xs'],
34003             ['xs', 'tall'],
34004             ['xs', 'sm'],
34005             ['xs', 'xs'],
34006             ['xs'],
34007             
34008             ['sm', 'xs', 'xs'],
34009             ['sm', 'xs'],
34010             ['sm'],
34011             
34012             ['tall', 'xs', 'xs', 'xs'],
34013             ['tall', 'xs', 'xs'],
34014             ['tall', 'xs'],
34015             ['tall']
34016             
34017         ];
34018         
34019         var queue = [];
34020         
34021         var boxes = [];
34022         
34023         var box = [];
34024         
34025         Roo.each(items, function(item, k){
34026             
34027             switch (item.size) {
34028                 // these layouts take up a full box,
34029                 case 'md' :
34030                 case 'md-left' :
34031                 case 'md-right' :
34032                 case 'wide' :
34033                     
34034                     if(box.length){
34035                         boxes.push(box);
34036                         box = [];
34037                     }
34038                     
34039                     boxes.push([item]);
34040                     
34041                     break;
34042                     
34043                 case 'xs' :
34044                 case 'sm' :
34045                 case 'tall' :
34046                     
34047                     box.push(item);
34048                     
34049                     break;
34050                 default :
34051                     break;
34052                     
34053             }
34054             
34055         }, this);
34056         
34057         if(box.length){
34058             boxes.push(box);
34059             box = [];
34060         }
34061         
34062         var filterPattern = function(box, length)
34063         {
34064             if(!box.length){
34065                 return;
34066             }
34067             
34068             var match = false;
34069             
34070             var pattern = box.slice(0, length);
34071             
34072             var format = [];
34073             
34074             Roo.each(pattern, function(i){
34075                 format.push(i.size);
34076             }, this);
34077             
34078             Roo.each(standard, function(s){
34079                 
34080                 if(String(s) != String(format)){
34081                     return;
34082                 }
34083                 
34084                 match = true;
34085                 return false;
34086                 
34087             }, this);
34088             
34089             if(!match && length == 1){
34090                 return;
34091             }
34092             
34093             if(!match){
34094                 filterPattern(box, length - 1);
34095                 return;
34096             }
34097                 
34098             queue.push(pattern);
34099
34100             box = box.slice(length, box.length);
34101
34102             filterPattern(box, 4);
34103
34104             return;
34105             
34106         }
34107         
34108         Roo.each(boxes, function(box, k){
34109             
34110             if(!box.length){
34111                 return;
34112             }
34113             
34114             if(box.length == 1){
34115                 queue.push(box);
34116                 return;
34117             }
34118             
34119             filterPattern(box, 4);
34120             
34121         }, this);
34122         
34123         this._processVerticalLayoutQueue( queue, isInstant );
34124         
34125     },
34126     
34127 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34128 //    {
34129 //        if ( !items || !items.length ) {
34130 //            return;
34131 //        }
34132 //
34133 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34134 //        
34135 //    },
34136     
34137     _horizontalLayoutItems : function ( items , isInstant)
34138     {
34139         if ( !items || !items.length || items.length < 3) {
34140             return;
34141         }
34142         
34143         items.reverse();
34144         
34145         var eItems = items.slice(0, 3);
34146         
34147         items = items.slice(3, items.length);
34148         
34149         var standard = [
34150             ['xs', 'xs', 'xs', 'wide'],
34151             ['xs', 'xs', 'wide'],
34152             ['xs', 'xs', 'sm'],
34153             ['xs', 'xs', 'xs'],
34154             ['xs', 'wide'],
34155             ['xs', 'sm'],
34156             ['xs', 'xs'],
34157             ['xs'],
34158             
34159             ['sm', 'xs', 'xs'],
34160             ['sm', 'xs'],
34161             ['sm'],
34162             
34163             ['wide', 'xs', 'xs', 'xs'],
34164             ['wide', 'xs', 'xs'],
34165             ['wide', 'xs'],
34166             ['wide'],
34167             
34168             ['wide-thin']
34169         ];
34170         
34171         var queue = [];
34172         
34173         var boxes = [];
34174         
34175         var box = [];
34176         
34177         Roo.each(items, function(item, k){
34178             
34179             switch (item.size) {
34180                 case 'md' :
34181                 case 'md-left' :
34182                 case 'md-right' :
34183                 case 'tall' :
34184                     
34185                     if(box.length){
34186                         boxes.push(box);
34187                         box = [];
34188                     }
34189                     
34190                     boxes.push([item]);
34191                     
34192                     break;
34193                     
34194                 case 'xs' :
34195                 case 'sm' :
34196                 case 'wide' :
34197                 case 'wide-thin' :
34198                     
34199                     box.push(item);
34200                     
34201                     break;
34202                 default :
34203                     break;
34204                     
34205             }
34206             
34207         }, this);
34208         
34209         if(box.length){
34210             boxes.push(box);
34211             box = [];
34212         }
34213         
34214         var filterPattern = function(box, length)
34215         {
34216             if(!box.length){
34217                 return;
34218             }
34219             
34220             var match = false;
34221             
34222             var pattern = box.slice(0, length);
34223             
34224             var format = [];
34225             
34226             Roo.each(pattern, function(i){
34227                 format.push(i.size);
34228             }, this);
34229             
34230             Roo.each(standard, function(s){
34231                 
34232                 if(String(s) != String(format)){
34233                     return;
34234                 }
34235                 
34236                 match = true;
34237                 return false;
34238                 
34239             }, this);
34240             
34241             if(!match && length == 1){
34242                 return;
34243             }
34244             
34245             if(!match){
34246                 filterPattern(box, length - 1);
34247                 return;
34248             }
34249                 
34250             queue.push(pattern);
34251
34252             box = box.slice(length, box.length);
34253
34254             filterPattern(box, 4);
34255
34256             return;
34257             
34258         }
34259         
34260         Roo.each(boxes, function(box, k){
34261             
34262             if(!box.length){
34263                 return;
34264             }
34265             
34266             if(box.length == 1){
34267                 queue.push(box);
34268                 return;
34269             }
34270             
34271             filterPattern(box, 4);
34272             
34273         }, this);
34274         
34275         
34276         var prune = [];
34277         
34278         var pos = this.el.getBox(true);
34279         
34280         var minX = pos.x;
34281         
34282         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34283         
34284         var hit_end = false;
34285         
34286         Roo.each(queue, function(box){
34287             
34288             if(hit_end){
34289                 
34290                 Roo.each(box, function(b){
34291                 
34292                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34293                     b.el.hide();
34294
34295                 }, this);
34296
34297                 return;
34298             }
34299             
34300             var mx = 0;
34301             
34302             Roo.each(box, function(b){
34303                 
34304                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34305                 b.el.show();
34306
34307                 mx = Math.max(mx, b.x);
34308                 
34309             }, this);
34310             
34311             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34312             
34313             if(maxX < minX){
34314                 
34315                 Roo.each(box, function(b){
34316                 
34317                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34318                     b.el.hide();
34319                     
34320                 }, this);
34321                 
34322                 hit_end = true;
34323                 
34324                 return;
34325             }
34326             
34327             prune.push(box);
34328             
34329         }, this);
34330         
34331         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34332     },
34333     
34334     /** Sets position of item in DOM
34335     * @param {Element} item
34336     * @param {Number} x - horizontal position
34337     * @param {Number} y - vertical position
34338     * @param {Boolean} isInstant - disables transitions
34339     */
34340     _processVerticalLayoutQueue : function( queue, isInstant )
34341     {
34342         var pos = this.el.getBox(true);
34343         var x = pos.x;
34344         var y = pos.y;
34345         var maxY = [];
34346         
34347         for (var i = 0; i < this.cols; i++){
34348             maxY[i] = pos.y;
34349         }
34350         
34351         Roo.each(queue, function(box, k){
34352             
34353             var col = k % this.cols;
34354             
34355             Roo.each(box, function(b,kk){
34356                 
34357                 b.el.position('absolute');
34358                 
34359                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34360                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34361                 
34362                 if(b.size == 'md-left' || b.size == 'md-right'){
34363                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34364                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34365                 }
34366                 
34367                 b.el.setWidth(width);
34368                 b.el.setHeight(height);
34369                 // iframe?
34370                 b.el.select('iframe',true).setSize(width,height);
34371                 
34372             }, this);
34373             
34374             for (var i = 0; i < this.cols; i++){
34375                 
34376                 if(maxY[i] < maxY[col]){
34377                     col = i;
34378                     continue;
34379                 }
34380                 
34381                 col = Math.min(col, i);
34382                 
34383             }
34384             
34385             x = pos.x + col * (this.colWidth + this.padWidth);
34386             
34387             y = maxY[col];
34388             
34389             var positions = [];
34390             
34391             switch (box.length){
34392                 case 1 :
34393                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34394                     break;
34395                 case 2 :
34396                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34397                     break;
34398                 case 3 :
34399                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34400                     break;
34401                 case 4 :
34402                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34403                     break;
34404                 default :
34405                     break;
34406             }
34407             
34408             Roo.each(box, function(b,kk){
34409                 
34410                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34411                 
34412                 var sz = b.el.getSize();
34413                 
34414                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34415                 
34416             }, this);
34417             
34418         }, this);
34419         
34420         var mY = 0;
34421         
34422         for (var i = 0; i < this.cols; i++){
34423             mY = Math.max(mY, maxY[i]);
34424         }
34425         
34426         this.el.setHeight(mY - pos.y);
34427         
34428     },
34429     
34430 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34431 //    {
34432 //        var pos = this.el.getBox(true);
34433 //        var x = pos.x;
34434 //        var y = pos.y;
34435 //        var maxX = pos.right;
34436 //        
34437 //        var maxHeight = 0;
34438 //        
34439 //        Roo.each(items, function(item, k){
34440 //            
34441 //            var c = k % 2;
34442 //            
34443 //            item.el.position('absolute');
34444 //                
34445 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34446 //
34447 //            item.el.setWidth(width);
34448 //
34449 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34450 //
34451 //            item.el.setHeight(height);
34452 //            
34453 //            if(c == 0){
34454 //                item.el.setXY([x, y], isInstant ? false : true);
34455 //            } else {
34456 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34457 //            }
34458 //            
34459 //            y = y + height + this.alternativePadWidth;
34460 //            
34461 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34462 //            
34463 //        }, this);
34464 //        
34465 //        this.el.setHeight(maxHeight);
34466 //        
34467 //    },
34468     
34469     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34470     {
34471         var pos = this.el.getBox(true);
34472         
34473         var minX = pos.x;
34474         var minY = pos.y;
34475         
34476         var maxX = pos.right;
34477         
34478         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34479         
34480         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34481         
34482         Roo.each(queue, function(box, k){
34483             
34484             Roo.each(box, function(b, kk){
34485                 
34486                 b.el.position('absolute');
34487                 
34488                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34489                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34490                 
34491                 if(b.size == 'md-left' || b.size == 'md-right'){
34492                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34493                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34494                 }
34495                 
34496                 b.el.setWidth(width);
34497                 b.el.setHeight(height);
34498                 
34499             }, this);
34500             
34501             if(!box.length){
34502                 return;
34503             }
34504             
34505             var positions = [];
34506             
34507             switch (box.length){
34508                 case 1 :
34509                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34510                     break;
34511                 case 2 :
34512                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34513                     break;
34514                 case 3 :
34515                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34516                     break;
34517                 case 4 :
34518                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34519                     break;
34520                 default :
34521                     break;
34522             }
34523             
34524             Roo.each(box, function(b,kk){
34525                 
34526                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34527                 
34528                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34529                 
34530             }, this);
34531             
34532         }, this);
34533         
34534     },
34535     
34536     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34537     {
34538         Roo.each(eItems, function(b,k){
34539             
34540             b.size = (k == 0) ? 'sm' : 'xs';
34541             b.x = (k == 0) ? 2 : 1;
34542             b.y = (k == 0) ? 2 : 1;
34543             
34544             b.el.position('absolute');
34545             
34546             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34547                 
34548             b.el.setWidth(width);
34549             
34550             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34551             
34552             b.el.setHeight(height);
34553             
34554         }, this);
34555
34556         var positions = [];
34557         
34558         positions.push({
34559             x : maxX - this.unitWidth * 2 - this.gutter,
34560             y : minY
34561         });
34562         
34563         positions.push({
34564             x : maxX - this.unitWidth,
34565             y : minY + (this.unitWidth + this.gutter) * 2
34566         });
34567         
34568         positions.push({
34569             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34570             y : minY
34571         });
34572         
34573         Roo.each(eItems, function(b,k){
34574             
34575             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34576
34577         }, this);
34578         
34579     },
34580     
34581     getVerticalOneBoxColPositions : function(x, y, box)
34582     {
34583         var pos = [];
34584         
34585         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34586         
34587         if(box[0].size == 'md-left'){
34588             rand = 0;
34589         }
34590         
34591         if(box[0].size == 'md-right'){
34592             rand = 1;
34593         }
34594         
34595         pos.push({
34596             x : x + (this.unitWidth + this.gutter) * rand,
34597             y : y
34598         });
34599         
34600         return pos;
34601     },
34602     
34603     getVerticalTwoBoxColPositions : function(x, y, box)
34604     {
34605         var pos = [];
34606         
34607         if(box[0].size == 'xs'){
34608             
34609             pos.push({
34610                 x : x,
34611                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34612             });
34613
34614             pos.push({
34615                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34616                 y : y
34617             });
34618             
34619             return pos;
34620             
34621         }
34622         
34623         pos.push({
34624             x : x,
34625             y : y
34626         });
34627
34628         pos.push({
34629             x : x + (this.unitWidth + this.gutter) * 2,
34630             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34631         });
34632         
34633         return pos;
34634         
34635     },
34636     
34637     getVerticalThreeBoxColPositions : function(x, y, box)
34638     {
34639         var pos = [];
34640         
34641         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34642             
34643             pos.push({
34644                 x : x,
34645                 y : y
34646             });
34647
34648             pos.push({
34649                 x : x + (this.unitWidth + this.gutter) * 1,
34650                 y : y
34651             });
34652             
34653             pos.push({
34654                 x : x + (this.unitWidth + this.gutter) * 2,
34655                 y : y
34656             });
34657             
34658             return pos;
34659             
34660         }
34661         
34662         if(box[0].size == 'xs' && box[1].size == 'xs'){
34663             
34664             pos.push({
34665                 x : x,
34666                 y : y
34667             });
34668
34669             pos.push({
34670                 x : x,
34671                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34672             });
34673             
34674             pos.push({
34675                 x : x + (this.unitWidth + this.gutter) * 1,
34676                 y : y
34677             });
34678             
34679             return pos;
34680             
34681         }
34682         
34683         pos.push({
34684             x : x,
34685             y : y
34686         });
34687
34688         pos.push({
34689             x : x + (this.unitWidth + this.gutter) * 2,
34690             y : y
34691         });
34692
34693         pos.push({
34694             x : x + (this.unitWidth + this.gutter) * 2,
34695             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34696         });
34697             
34698         return pos;
34699         
34700     },
34701     
34702     getVerticalFourBoxColPositions : function(x, y, box)
34703     {
34704         var pos = [];
34705         
34706         if(box[0].size == 'xs'){
34707             
34708             pos.push({
34709                 x : x,
34710                 y : y
34711             });
34712
34713             pos.push({
34714                 x : x,
34715                 y : y + (this.unitHeight + this.gutter) * 1
34716             });
34717             
34718             pos.push({
34719                 x : x,
34720                 y : y + (this.unitHeight + this.gutter) * 2
34721             });
34722             
34723             pos.push({
34724                 x : x + (this.unitWidth + this.gutter) * 1,
34725                 y : y
34726             });
34727             
34728             return pos;
34729             
34730         }
34731         
34732         pos.push({
34733             x : x,
34734             y : y
34735         });
34736
34737         pos.push({
34738             x : x + (this.unitWidth + this.gutter) * 2,
34739             y : y
34740         });
34741
34742         pos.push({
34743             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34744             y : y + (this.unitHeight + this.gutter) * 1
34745         });
34746
34747         pos.push({
34748             x : x + (this.unitWidth + this.gutter) * 2,
34749             y : y + (this.unitWidth + this.gutter) * 2
34750         });
34751
34752         return pos;
34753         
34754     },
34755     
34756     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34757     {
34758         var pos = [];
34759         
34760         if(box[0].size == 'md-left'){
34761             pos.push({
34762                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34763                 y : minY
34764             });
34765             
34766             return pos;
34767         }
34768         
34769         if(box[0].size == 'md-right'){
34770             pos.push({
34771                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34772                 y : minY + (this.unitWidth + this.gutter) * 1
34773             });
34774             
34775             return pos;
34776         }
34777         
34778         var rand = Math.floor(Math.random() * (4 - box[0].y));
34779         
34780         pos.push({
34781             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34782             y : minY + (this.unitWidth + this.gutter) * rand
34783         });
34784         
34785         return pos;
34786         
34787     },
34788     
34789     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34790     {
34791         var pos = [];
34792         
34793         if(box[0].size == 'xs'){
34794             
34795             pos.push({
34796                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34797                 y : minY
34798             });
34799
34800             pos.push({
34801                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34802                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34803             });
34804             
34805             return pos;
34806             
34807         }
34808         
34809         pos.push({
34810             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34811             y : minY
34812         });
34813
34814         pos.push({
34815             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34816             y : minY + (this.unitWidth + this.gutter) * 2
34817         });
34818         
34819         return pos;
34820         
34821     },
34822     
34823     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34824     {
34825         var pos = [];
34826         
34827         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34828             
34829             pos.push({
34830                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34831                 y : minY
34832             });
34833
34834             pos.push({
34835                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34836                 y : minY + (this.unitWidth + this.gutter) * 1
34837             });
34838             
34839             pos.push({
34840                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34841                 y : minY + (this.unitWidth + this.gutter) * 2
34842             });
34843             
34844             return pos;
34845             
34846         }
34847         
34848         if(box[0].size == 'xs' && box[1].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[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34857                 y : minY
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) * 1
34863             });
34864             
34865             return pos;
34866             
34867         }
34868         
34869         pos.push({
34870             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34871             y : minY
34872         });
34873
34874         pos.push({
34875             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34876             y : minY + (this.unitWidth + this.gutter) * 2
34877         });
34878
34879         pos.push({
34880             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34881             y : minY + (this.unitWidth + this.gutter) * 2
34882         });
34883             
34884         return pos;
34885         
34886     },
34887     
34888     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34889     {
34890         var pos = [];
34891         
34892         if(box[0].size == 'xs'){
34893             
34894             pos.push({
34895                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34896                 y : minY
34897             });
34898
34899             pos.push({
34900                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34901                 y : minY
34902             });
34903             
34904             pos.push({
34905                 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),
34906                 y : minY
34907             });
34908             
34909             pos.push({
34910                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34911                 y : minY + (this.unitWidth + this.gutter) * 1
34912             });
34913             
34914             return pos;
34915             
34916         }
34917         
34918         pos.push({
34919             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34920             y : minY
34921         });
34922         
34923         pos.push({
34924             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34925             y : minY + (this.unitWidth + this.gutter) * 2
34926         });
34927         
34928         pos.push({
34929             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34930             y : minY + (this.unitWidth + this.gutter) * 2
34931         });
34932         
34933         pos.push({
34934             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),
34935             y : minY + (this.unitWidth + this.gutter) * 2
34936         });
34937
34938         return pos;
34939         
34940     },
34941     
34942     /**
34943     * remove a Masonry Brick
34944     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34945     */
34946     removeBrick : function(brick_id)
34947     {
34948         if (!brick_id) {
34949             return;
34950         }
34951         
34952         for (var i = 0; i<this.bricks.length; i++) {
34953             if (this.bricks[i].id == brick_id) {
34954                 this.bricks.splice(i,1);
34955                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34956                 this.initial();
34957             }
34958         }
34959     },
34960     
34961     /**
34962     * adds a Masonry Brick
34963     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34964     */
34965     addBrick : function(cfg)
34966     {
34967         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34968         //this.register(cn);
34969         cn.parentId = this.id;
34970         cn.render(this.el);
34971         return cn;
34972     },
34973     
34974     /**
34975     * register a Masonry Brick
34976     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34977     */
34978     
34979     register : function(brick)
34980     {
34981         this.bricks.push(brick);
34982         brick.masonryId = this.id;
34983     },
34984     
34985     /**
34986     * clear all the Masonry Brick
34987     */
34988     clearAll : function()
34989     {
34990         this.bricks = [];
34991         //this.getChildContainer().dom.innerHTML = "";
34992         this.el.dom.innerHTML = '';
34993     },
34994     
34995     getSelected : function()
34996     {
34997         if (!this.selectedBrick) {
34998             return false;
34999         }
35000         
35001         return this.selectedBrick;
35002     }
35003 });
35004
35005 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35006     
35007     groups: {},
35008      /**
35009     * register a Masonry Layout
35010     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35011     */
35012     
35013     register : function(layout)
35014     {
35015         this.groups[layout.id] = layout;
35016     },
35017     /**
35018     * fetch a  Masonry Layout based on the masonry layout ID
35019     * @param {string} the masonry layout to add
35020     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35021     */
35022     
35023     get: function(layout_id) {
35024         if (typeof(this.groups[layout_id]) == 'undefined') {
35025             return false;
35026         }
35027         return this.groups[layout_id] ;
35028     }
35029     
35030     
35031     
35032 });
35033
35034  
35035
35036  /**
35037  *
35038  * This is based on 
35039  * http://masonry.desandro.com
35040  *
35041  * The idea is to render all the bricks based on vertical width...
35042  *
35043  * The original code extends 'outlayer' - we might need to use that....
35044  * 
35045  */
35046
35047
35048 /**
35049  * @class Roo.bootstrap.LayoutMasonryAuto
35050  * @extends Roo.bootstrap.Component
35051  * Bootstrap Layout Masonry class
35052  * 
35053  * @constructor
35054  * Create a new Element
35055  * @param {Object} config The config object
35056  */
35057
35058 Roo.bootstrap.LayoutMasonryAuto = function(config){
35059     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35060 };
35061
35062 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35063     
35064       /**
35065      * @cfg {Boolean} isFitWidth  - resize the width..
35066      */   
35067     isFitWidth : false,  // options..
35068     /**
35069      * @cfg {Boolean} isOriginLeft = left align?
35070      */   
35071     isOriginLeft : true,
35072     /**
35073      * @cfg {Boolean} isOriginTop = top align?
35074      */   
35075     isOriginTop : false,
35076     /**
35077      * @cfg {Boolean} isLayoutInstant = no animation?
35078      */   
35079     isLayoutInstant : false, // needed?
35080     /**
35081      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35082      */   
35083     isResizingContainer : true,
35084     /**
35085      * @cfg {Number} columnWidth  width of the columns 
35086      */   
35087     
35088     columnWidth : 0,
35089     
35090     /**
35091      * @cfg {Number} maxCols maximum number of columns
35092      */   
35093     
35094     maxCols: 0,
35095     /**
35096      * @cfg {Number} padHeight padding below box..
35097      */   
35098     
35099     padHeight : 10, 
35100     
35101     /**
35102      * @cfg {Boolean} isAutoInitial defalut true
35103      */   
35104     
35105     isAutoInitial : true, 
35106     
35107     // private?
35108     gutter : 0,
35109     
35110     containerWidth: 0,
35111     initialColumnWidth : 0,
35112     currentSize : null,
35113     
35114     colYs : null, // array.
35115     maxY : 0,
35116     padWidth: 10,
35117     
35118     
35119     tag: 'div',
35120     cls: '',
35121     bricks: null, //CompositeElement
35122     cols : 0, // array?
35123     // element : null, // wrapped now this.el
35124     _isLayoutInited : null, 
35125     
35126     
35127     getAutoCreate : function(){
35128         
35129         var cfg = {
35130             tag: this.tag,
35131             cls: 'blog-masonary-wrapper ' + this.cls,
35132             cn : {
35133                 cls : 'mas-boxes masonary'
35134             }
35135         };
35136         
35137         return cfg;
35138     },
35139     
35140     getChildContainer: function( )
35141     {
35142         if (this.boxesEl) {
35143             return this.boxesEl;
35144         }
35145         
35146         this.boxesEl = this.el.select('.mas-boxes').first();
35147         
35148         return this.boxesEl;
35149     },
35150     
35151     
35152     initEvents : function()
35153     {
35154         var _this = this;
35155         
35156         if(this.isAutoInitial){
35157             Roo.log('hook children rendered');
35158             this.on('childrenrendered', function() {
35159                 Roo.log('children rendered');
35160                 _this.initial();
35161             } ,this);
35162         }
35163         
35164     },
35165     
35166     initial : function()
35167     {
35168         this.reloadItems();
35169
35170         this.currentSize = this.el.getBox(true);
35171
35172         /// was window resize... - let's see if this works..
35173         Roo.EventManager.onWindowResize(this.resize, this); 
35174
35175         if(!this.isAutoInitial){
35176             this.layout();
35177             return;
35178         }
35179         
35180         this.layout.defer(500,this);
35181     },
35182     
35183     reloadItems: function()
35184     {
35185         this.bricks = this.el.select('.masonry-brick', true);
35186         
35187         this.bricks.each(function(b) {
35188             //Roo.log(b.getSize());
35189             if (!b.attr('originalwidth')) {
35190                 b.attr('originalwidth',  b.getSize().width);
35191             }
35192             
35193         });
35194         
35195         Roo.log(this.bricks.elements.length);
35196     },
35197     
35198     resize : function()
35199     {
35200         Roo.log('resize');
35201         var cs = this.el.getBox(true);
35202         
35203         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35204             Roo.log("no change in with or X");
35205             return;
35206         }
35207         this.currentSize = cs;
35208         this.layout();
35209     },
35210     
35211     layout : function()
35212     {
35213          Roo.log('layout');
35214         this._resetLayout();
35215         //this._manageStamps();
35216       
35217         // don't animate first layout
35218         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35219         this.layoutItems( isInstant );
35220       
35221         // flag for initalized
35222         this._isLayoutInited = true;
35223     },
35224     
35225     layoutItems : function( isInstant )
35226     {
35227         //var items = this._getItemsForLayout( this.items );
35228         // original code supports filtering layout items.. we just ignore it..
35229         
35230         this._layoutItems( this.bricks , isInstant );
35231       
35232         this._postLayout();
35233     },
35234     _layoutItems : function ( items , isInstant)
35235     {
35236        //this.fireEvent( 'layout', this, items );
35237     
35238
35239         if ( !items || !items.elements.length ) {
35240           // no items, emit event with empty array
35241             return;
35242         }
35243
35244         var queue = [];
35245         items.each(function(item) {
35246             Roo.log("layout item");
35247             Roo.log(item);
35248             // get x/y object from method
35249             var position = this._getItemLayoutPosition( item );
35250             // enqueue
35251             position.item = item;
35252             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35253             queue.push( position );
35254         }, this);
35255       
35256         this._processLayoutQueue( queue );
35257     },
35258     /** Sets position of item in DOM
35259     * @param {Element} item
35260     * @param {Number} x - horizontal position
35261     * @param {Number} y - vertical position
35262     * @param {Boolean} isInstant - disables transitions
35263     */
35264     _processLayoutQueue : function( queue )
35265     {
35266         for ( var i=0, len = queue.length; i < len; i++ ) {
35267             var obj = queue[i];
35268             obj.item.position('absolute');
35269             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35270         }
35271     },
35272       
35273     
35274     /**
35275     * Any logic you want to do after each layout,
35276     * i.e. size the container
35277     */
35278     _postLayout : function()
35279     {
35280         this.resizeContainer();
35281     },
35282     
35283     resizeContainer : function()
35284     {
35285         if ( !this.isResizingContainer ) {
35286             return;
35287         }
35288         var size = this._getContainerSize();
35289         if ( size ) {
35290             this.el.setSize(size.width,size.height);
35291             this.boxesEl.setSize(size.width,size.height);
35292         }
35293     },
35294     
35295     
35296     
35297     _resetLayout : function()
35298     {
35299         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35300         this.colWidth = this.el.getWidth();
35301         //this.gutter = this.el.getWidth(); 
35302         
35303         this.measureColumns();
35304
35305         // reset column Y
35306         var i = this.cols;
35307         this.colYs = [];
35308         while (i--) {
35309             this.colYs.push( 0 );
35310         }
35311     
35312         this.maxY = 0;
35313     },
35314
35315     measureColumns : function()
35316     {
35317         this.getContainerWidth();
35318       // if columnWidth is 0, default to outerWidth of first item
35319         if ( !this.columnWidth ) {
35320             var firstItem = this.bricks.first();
35321             Roo.log(firstItem);
35322             this.columnWidth  = this.containerWidth;
35323             if (firstItem && firstItem.attr('originalwidth') ) {
35324                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35325             }
35326             // columnWidth fall back to item of first element
35327             Roo.log("set column width?");
35328                         this.initialColumnWidth = this.columnWidth  ;
35329
35330             // if first elem has no width, default to size of container
35331             
35332         }
35333         
35334         
35335         if (this.initialColumnWidth) {
35336             this.columnWidth = this.initialColumnWidth;
35337         }
35338         
35339         
35340             
35341         // column width is fixed at the top - however if container width get's smaller we should
35342         // reduce it...
35343         
35344         // this bit calcs how man columns..
35345             
35346         var columnWidth = this.columnWidth += this.gutter;
35347       
35348         // calculate columns
35349         var containerWidth = this.containerWidth + this.gutter;
35350         
35351         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35352         // fix rounding errors, typically with gutters
35353         var excess = columnWidth - containerWidth % columnWidth;
35354         
35355         
35356         // if overshoot is less than a pixel, round up, otherwise floor it
35357         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35358         cols = Math[ mathMethod ]( cols );
35359         this.cols = Math.max( cols, 1 );
35360         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35361         
35362          // padding positioning..
35363         var totalColWidth = this.cols * this.columnWidth;
35364         var padavail = this.containerWidth - totalColWidth;
35365         // so for 2 columns - we need 3 'pads'
35366         
35367         var padNeeded = (1+this.cols) * this.padWidth;
35368         
35369         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35370         
35371         this.columnWidth += padExtra
35372         //this.padWidth = Math.floor(padavail /  ( this.cols));
35373         
35374         // adjust colum width so that padding is fixed??
35375         
35376         // we have 3 columns ... total = width * 3
35377         // we have X left over... that should be used by 
35378         
35379         //if (this.expandC) {
35380             
35381         //}
35382         
35383         
35384         
35385     },
35386     
35387     getContainerWidth : function()
35388     {
35389        /* // container is parent if fit width
35390         var container = this.isFitWidth ? this.element.parentNode : this.element;
35391         // check that this.size and size are there
35392         // IE8 triggers resize on body size change, so they might not be
35393         
35394         var size = getSize( container );  //FIXME
35395         this.containerWidth = size && size.innerWidth; //FIXME
35396         */
35397          
35398         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35399         
35400     },
35401     
35402     _getItemLayoutPosition : function( item )  // what is item?
35403     {
35404         // we resize the item to our columnWidth..
35405       
35406         item.setWidth(this.columnWidth);
35407         item.autoBoxAdjust  = false;
35408         
35409         var sz = item.getSize();
35410  
35411         // how many columns does this brick span
35412         var remainder = this.containerWidth % this.columnWidth;
35413         
35414         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35415         // round if off by 1 pixel, otherwise use ceil
35416         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35417         colSpan = Math.min( colSpan, this.cols );
35418         
35419         // normally this should be '1' as we dont' currently allow multi width columns..
35420         
35421         var colGroup = this._getColGroup( colSpan );
35422         // get the minimum Y value from the columns
35423         var minimumY = Math.min.apply( Math, colGroup );
35424         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35425         
35426         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35427          
35428         // position the brick
35429         var position = {
35430             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35431             y: this.currentSize.y + minimumY + this.padHeight
35432         };
35433         
35434         Roo.log(position);
35435         // apply setHeight to necessary columns
35436         var setHeight = minimumY + sz.height + this.padHeight;
35437         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35438         
35439         var setSpan = this.cols + 1 - colGroup.length;
35440         for ( var i = 0; i < setSpan; i++ ) {
35441           this.colYs[ shortColIndex + i ] = setHeight ;
35442         }
35443       
35444         return position;
35445     },
35446     
35447     /**
35448      * @param {Number} colSpan - number of columns the element spans
35449      * @returns {Array} colGroup
35450      */
35451     _getColGroup : function( colSpan )
35452     {
35453         if ( colSpan < 2 ) {
35454           // if brick spans only one column, use all the column Ys
35455           return this.colYs;
35456         }
35457       
35458         var colGroup = [];
35459         // how many different places could this brick fit horizontally
35460         var groupCount = this.cols + 1 - colSpan;
35461         // for each group potential horizontal position
35462         for ( var i = 0; i < groupCount; i++ ) {
35463           // make an array of colY values for that one group
35464           var groupColYs = this.colYs.slice( i, i + colSpan );
35465           // and get the max value of the array
35466           colGroup[i] = Math.max.apply( Math, groupColYs );
35467         }
35468         return colGroup;
35469     },
35470     /*
35471     _manageStamp : function( stamp )
35472     {
35473         var stampSize =  stamp.getSize();
35474         var offset = stamp.getBox();
35475         // get the columns that this stamp affects
35476         var firstX = this.isOriginLeft ? offset.x : offset.right;
35477         var lastX = firstX + stampSize.width;
35478         var firstCol = Math.floor( firstX / this.columnWidth );
35479         firstCol = Math.max( 0, firstCol );
35480         
35481         var lastCol = Math.floor( lastX / this.columnWidth );
35482         // lastCol should not go over if multiple of columnWidth #425
35483         lastCol -= lastX % this.columnWidth ? 0 : 1;
35484         lastCol = Math.min( this.cols - 1, lastCol );
35485         
35486         // set colYs to bottom of the stamp
35487         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35488             stampSize.height;
35489             
35490         for ( var i = firstCol; i <= lastCol; i++ ) {
35491           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35492         }
35493     },
35494     */
35495     
35496     _getContainerSize : function()
35497     {
35498         this.maxY = Math.max.apply( Math, this.colYs );
35499         var size = {
35500             height: this.maxY
35501         };
35502       
35503         if ( this.isFitWidth ) {
35504             size.width = this._getContainerFitWidth();
35505         }
35506       
35507         return size;
35508     },
35509     
35510     _getContainerFitWidth : function()
35511     {
35512         var unusedCols = 0;
35513         // count unused columns
35514         var i = this.cols;
35515         while ( --i ) {
35516           if ( this.colYs[i] !== 0 ) {
35517             break;
35518           }
35519           unusedCols++;
35520         }
35521         // fit container to columns that have been used
35522         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35523     },
35524     
35525     needsResizeLayout : function()
35526     {
35527         var previousWidth = this.containerWidth;
35528         this.getContainerWidth();
35529         return previousWidth !== this.containerWidth;
35530     }
35531  
35532 });
35533
35534  
35535
35536  /*
35537  * - LGPL
35538  *
35539  * element
35540  * 
35541  */
35542
35543 /**
35544  * @class Roo.bootstrap.MasonryBrick
35545  * @extends Roo.bootstrap.Component
35546  * Bootstrap MasonryBrick class
35547  * 
35548  * @constructor
35549  * Create a new MasonryBrick
35550  * @param {Object} config The config object
35551  */
35552
35553 Roo.bootstrap.MasonryBrick = function(config){
35554     
35555     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35556     
35557     Roo.bootstrap.MasonryBrick.register(this);
35558     
35559     this.addEvents({
35560         // raw events
35561         /**
35562          * @event click
35563          * When a MasonryBrick is clcik
35564          * @param {Roo.bootstrap.MasonryBrick} this
35565          * @param {Roo.EventObject} e
35566          */
35567         "click" : true
35568     });
35569 };
35570
35571 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35572     
35573     /**
35574      * @cfg {String} title
35575      */   
35576     title : '',
35577     /**
35578      * @cfg {String} html
35579      */   
35580     html : '',
35581     /**
35582      * @cfg {String} bgimage
35583      */   
35584     bgimage : '',
35585     /**
35586      * @cfg {String} videourl
35587      */   
35588     videourl : '',
35589     /**
35590      * @cfg {String} cls
35591      */   
35592     cls : '',
35593     /**
35594      * @cfg {String} href
35595      */   
35596     href : '',
35597     /**
35598      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35599      */   
35600     size : 'xs',
35601     
35602     /**
35603      * @cfg {String} placetitle (center|bottom)
35604      */   
35605     placetitle : '',
35606     
35607     /**
35608      * @cfg {Boolean} isFitContainer defalut true
35609      */   
35610     isFitContainer : true, 
35611     
35612     /**
35613      * @cfg {Boolean} preventDefault defalut false
35614      */   
35615     preventDefault : false, 
35616     
35617     /**
35618      * @cfg {Boolean} inverse defalut false
35619      */   
35620     maskInverse : false, 
35621     
35622     getAutoCreate : function()
35623     {
35624         if(!this.isFitContainer){
35625             return this.getSplitAutoCreate();
35626         }
35627         
35628         var cls = 'masonry-brick masonry-brick-full';
35629         
35630         if(this.href.length){
35631             cls += ' masonry-brick-link';
35632         }
35633         
35634         if(this.bgimage.length){
35635             cls += ' masonry-brick-image';
35636         }
35637         
35638         if(this.maskInverse){
35639             cls += ' mask-inverse';
35640         }
35641         
35642         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35643             cls += ' enable-mask';
35644         }
35645         
35646         if(this.size){
35647             cls += ' masonry-' + this.size + '-brick';
35648         }
35649         
35650         if(this.placetitle.length){
35651             
35652             switch (this.placetitle) {
35653                 case 'center' :
35654                     cls += ' masonry-center-title';
35655                     break;
35656                 case 'bottom' :
35657                     cls += ' masonry-bottom-title';
35658                     break;
35659                 default:
35660                     break;
35661             }
35662             
35663         } else {
35664             if(!this.html.length && !this.bgimage.length){
35665                 cls += ' masonry-center-title';
35666             }
35667
35668             if(!this.html.length && this.bgimage.length){
35669                 cls += ' masonry-bottom-title';
35670             }
35671         }
35672         
35673         if(this.cls){
35674             cls += ' ' + this.cls;
35675         }
35676         
35677         var cfg = {
35678             tag: (this.href.length) ? 'a' : 'div',
35679             cls: cls,
35680             cn: [
35681                 {
35682                     tag: 'div',
35683                     cls: 'masonry-brick-mask'
35684                 },
35685                 {
35686                     tag: 'div',
35687                     cls: 'masonry-brick-paragraph',
35688                     cn: []
35689                 }
35690             ]
35691         };
35692         
35693         if(this.href.length){
35694             cfg.href = this.href;
35695         }
35696         
35697         var cn = cfg.cn[1].cn;
35698         
35699         if(this.title.length){
35700             cn.push({
35701                 tag: 'h4',
35702                 cls: 'masonry-brick-title',
35703                 html: this.title
35704             });
35705         }
35706         
35707         if(this.html.length){
35708             cn.push({
35709                 tag: 'p',
35710                 cls: 'masonry-brick-text',
35711                 html: this.html
35712             });
35713         }
35714         
35715         if (!this.title.length && !this.html.length) {
35716             cfg.cn[1].cls += ' hide';
35717         }
35718         
35719         if(this.bgimage.length){
35720             cfg.cn.push({
35721                 tag: 'img',
35722                 cls: 'masonry-brick-image-view',
35723                 src: this.bgimage
35724             });
35725         }
35726         
35727         if(this.videourl.length){
35728             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35729             // youtube support only?
35730             cfg.cn.push({
35731                 tag: 'iframe',
35732                 cls: 'masonry-brick-image-view',
35733                 src: vurl,
35734                 frameborder : 0,
35735                 allowfullscreen : true
35736             });
35737         }
35738         
35739         return cfg;
35740         
35741     },
35742     
35743     getSplitAutoCreate : function()
35744     {
35745         var cls = 'masonry-brick masonry-brick-split';
35746         
35747         if(this.href.length){
35748             cls += ' masonry-brick-link';
35749         }
35750         
35751         if(this.bgimage.length){
35752             cls += ' masonry-brick-image';
35753         }
35754         
35755         if(this.size){
35756             cls += ' masonry-' + this.size + '-brick';
35757         }
35758         
35759         switch (this.placetitle) {
35760             case 'center' :
35761                 cls += ' masonry-center-title';
35762                 break;
35763             case 'bottom' :
35764                 cls += ' masonry-bottom-title';
35765                 break;
35766             default:
35767                 if(!this.bgimage.length){
35768                     cls += ' masonry-center-title';
35769                 }
35770
35771                 if(this.bgimage.length){
35772                     cls += ' masonry-bottom-title';
35773                 }
35774                 break;
35775         }
35776         
35777         if(this.cls){
35778             cls += ' ' + this.cls;
35779         }
35780         
35781         var cfg = {
35782             tag: (this.href.length) ? 'a' : 'div',
35783             cls: cls,
35784             cn: [
35785                 {
35786                     tag: 'div',
35787                     cls: 'masonry-brick-split-head',
35788                     cn: [
35789                         {
35790                             tag: 'div',
35791                             cls: 'masonry-brick-paragraph',
35792                             cn: []
35793                         }
35794                     ]
35795                 },
35796                 {
35797                     tag: 'div',
35798                     cls: 'masonry-brick-split-body',
35799                     cn: []
35800                 }
35801             ]
35802         };
35803         
35804         if(this.href.length){
35805             cfg.href = this.href;
35806         }
35807         
35808         if(this.title.length){
35809             cfg.cn[0].cn[0].cn.push({
35810                 tag: 'h4',
35811                 cls: 'masonry-brick-title',
35812                 html: this.title
35813             });
35814         }
35815         
35816         if(this.html.length){
35817             cfg.cn[1].cn.push({
35818                 tag: 'p',
35819                 cls: 'masonry-brick-text',
35820                 html: this.html
35821             });
35822         }
35823
35824         if(this.bgimage.length){
35825             cfg.cn[0].cn.push({
35826                 tag: 'img',
35827                 cls: 'masonry-brick-image-view',
35828                 src: this.bgimage
35829             });
35830         }
35831         
35832         if(this.videourl.length){
35833             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35834             // youtube support only?
35835             cfg.cn[0].cn.cn.push({
35836                 tag: 'iframe',
35837                 cls: 'masonry-brick-image-view',
35838                 src: vurl,
35839                 frameborder : 0,
35840                 allowfullscreen : true
35841             });
35842         }
35843         
35844         return cfg;
35845     },
35846     
35847     initEvents: function() 
35848     {
35849         switch (this.size) {
35850             case 'xs' :
35851                 this.x = 1;
35852                 this.y = 1;
35853                 break;
35854             case 'sm' :
35855                 this.x = 2;
35856                 this.y = 2;
35857                 break;
35858             case 'md' :
35859             case 'md-left' :
35860             case 'md-right' :
35861                 this.x = 3;
35862                 this.y = 3;
35863                 break;
35864             case 'tall' :
35865                 this.x = 2;
35866                 this.y = 3;
35867                 break;
35868             case 'wide' :
35869                 this.x = 3;
35870                 this.y = 2;
35871                 break;
35872             case 'wide-thin' :
35873                 this.x = 3;
35874                 this.y = 1;
35875                 break;
35876                         
35877             default :
35878                 break;
35879         }
35880         
35881         if(Roo.isTouch){
35882             this.el.on('touchstart', this.onTouchStart, this);
35883             this.el.on('touchmove', this.onTouchMove, this);
35884             this.el.on('touchend', this.onTouchEnd, this);
35885             this.el.on('contextmenu', this.onContextMenu, this);
35886         } else {
35887             this.el.on('mouseenter'  ,this.enter, this);
35888             this.el.on('mouseleave', this.leave, this);
35889             this.el.on('click', this.onClick, this);
35890         }
35891         
35892         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35893             this.parent().bricks.push(this);   
35894         }
35895         
35896     },
35897     
35898     onClick: function(e, el)
35899     {
35900         var time = this.endTimer - this.startTimer;
35901         // Roo.log(e.preventDefault());
35902         if(Roo.isTouch){
35903             if(time > 1000){
35904                 e.preventDefault();
35905                 return;
35906             }
35907         }
35908         
35909         if(!this.preventDefault){
35910             return;
35911         }
35912         
35913         e.preventDefault();
35914         
35915         if (this.activeClass != '') {
35916             this.selectBrick();
35917         }
35918         
35919         this.fireEvent('click', this, e);
35920     },
35921     
35922     enter: function(e, el)
35923     {
35924         e.preventDefault();
35925         
35926         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35927             return;
35928         }
35929         
35930         if(this.bgimage.length && this.html.length){
35931             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35932         }
35933     },
35934     
35935     leave: function(e, el)
35936     {
35937         e.preventDefault();
35938         
35939         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35940             return;
35941         }
35942         
35943         if(this.bgimage.length && this.html.length){
35944             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35945         }
35946     },
35947     
35948     onTouchStart: function(e, el)
35949     {
35950 //        e.preventDefault();
35951         
35952         this.touchmoved = false;
35953         
35954         if(!this.isFitContainer){
35955             return;
35956         }
35957         
35958         if(!this.bgimage.length || !this.html.length){
35959             return;
35960         }
35961         
35962         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35963         
35964         this.timer = new Date().getTime();
35965         
35966     },
35967     
35968     onTouchMove: function(e, el)
35969     {
35970         this.touchmoved = true;
35971     },
35972     
35973     onContextMenu : function(e,el)
35974     {
35975         e.preventDefault();
35976         e.stopPropagation();
35977         return false;
35978     },
35979     
35980     onTouchEnd: function(e, el)
35981     {
35982 //        e.preventDefault();
35983         
35984         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35985         
35986             this.leave(e,el);
35987             
35988             return;
35989         }
35990         
35991         if(!this.bgimage.length || !this.html.length){
35992             
35993             if(this.href.length){
35994                 window.location.href = this.href;
35995             }
35996             
35997             return;
35998         }
35999         
36000         if(!this.isFitContainer){
36001             return;
36002         }
36003         
36004         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36005         
36006         window.location.href = this.href;
36007     },
36008     
36009     //selection on single brick only
36010     selectBrick : function() {
36011         
36012         if (!this.parentId) {
36013             return;
36014         }
36015         
36016         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36017         var index = m.selectedBrick.indexOf(this.id);
36018         
36019         if ( index > -1) {
36020             m.selectedBrick.splice(index,1);
36021             this.el.removeClass(this.activeClass);
36022             return;
36023         }
36024         
36025         for(var i = 0; i < m.selectedBrick.length; i++) {
36026             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36027             b.el.removeClass(b.activeClass);
36028         }
36029         
36030         m.selectedBrick = [];
36031         
36032         m.selectedBrick.push(this.id);
36033         this.el.addClass(this.activeClass);
36034         return;
36035     },
36036     
36037     isSelected : function(){
36038         return this.el.hasClass(this.activeClass);
36039         
36040     }
36041 });
36042
36043 Roo.apply(Roo.bootstrap.MasonryBrick, {
36044     
36045     //groups: {},
36046     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36047      /**
36048     * register a Masonry Brick
36049     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36050     */
36051     
36052     register : function(brick)
36053     {
36054         //this.groups[brick.id] = brick;
36055         this.groups.add(brick.id, brick);
36056     },
36057     /**
36058     * fetch a  masonry brick based on the masonry brick ID
36059     * @param {string} the masonry brick to add
36060     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36061     */
36062     
36063     get: function(brick_id) 
36064     {
36065         // if (typeof(this.groups[brick_id]) == 'undefined') {
36066         //     return false;
36067         // }
36068         // return this.groups[brick_id] ;
36069         
36070         if(this.groups.key(brick_id)) {
36071             return this.groups.key(brick_id);
36072         }
36073         
36074         return false;
36075     }
36076     
36077     
36078     
36079 });
36080
36081  /*
36082  * - LGPL
36083  *
36084  * element
36085  * 
36086  */
36087
36088 /**
36089  * @class Roo.bootstrap.Brick
36090  * @extends Roo.bootstrap.Component
36091  * Bootstrap Brick class
36092  * 
36093  * @constructor
36094  * Create a new Brick
36095  * @param {Object} config The config object
36096  */
36097
36098 Roo.bootstrap.Brick = function(config){
36099     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36100     
36101     this.addEvents({
36102         // raw events
36103         /**
36104          * @event click
36105          * When a Brick is click
36106          * @param {Roo.bootstrap.Brick} this
36107          * @param {Roo.EventObject} e
36108          */
36109         "click" : true
36110     });
36111 };
36112
36113 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36114     
36115     /**
36116      * @cfg {String} title
36117      */   
36118     title : '',
36119     /**
36120      * @cfg {String} html
36121      */   
36122     html : '',
36123     /**
36124      * @cfg {String} bgimage
36125      */   
36126     bgimage : '',
36127     /**
36128      * @cfg {String} cls
36129      */   
36130     cls : '',
36131     /**
36132      * @cfg {String} href
36133      */   
36134     href : '',
36135     /**
36136      * @cfg {String} video
36137      */   
36138     video : '',
36139     /**
36140      * @cfg {Boolean} square
36141      */   
36142     square : true,
36143     
36144     getAutoCreate : function()
36145     {
36146         var cls = 'roo-brick';
36147         
36148         if(this.href.length){
36149             cls += ' roo-brick-link';
36150         }
36151         
36152         if(this.bgimage.length){
36153             cls += ' roo-brick-image';
36154         }
36155         
36156         if(!this.html.length && !this.bgimage.length){
36157             cls += ' roo-brick-center-title';
36158         }
36159         
36160         if(!this.html.length && this.bgimage.length){
36161             cls += ' roo-brick-bottom-title';
36162         }
36163         
36164         if(this.cls){
36165             cls += ' ' + this.cls;
36166         }
36167         
36168         var cfg = {
36169             tag: (this.href.length) ? 'a' : 'div',
36170             cls: cls,
36171             cn: [
36172                 {
36173                     tag: 'div',
36174                     cls: 'roo-brick-paragraph',
36175                     cn: []
36176                 }
36177             ]
36178         };
36179         
36180         if(this.href.length){
36181             cfg.href = this.href;
36182         }
36183         
36184         var cn = cfg.cn[0].cn;
36185         
36186         if(this.title.length){
36187             cn.push({
36188                 tag: 'h4',
36189                 cls: 'roo-brick-title',
36190                 html: this.title
36191             });
36192         }
36193         
36194         if(this.html.length){
36195             cn.push({
36196                 tag: 'p',
36197                 cls: 'roo-brick-text',
36198                 html: this.html
36199             });
36200         } else {
36201             cn.cls += ' hide';
36202         }
36203         
36204         if(this.bgimage.length){
36205             cfg.cn.push({
36206                 tag: 'img',
36207                 cls: 'roo-brick-image-view',
36208                 src: this.bgimage
36209             });
36210         }
36211         
36212         return cfg;
36213     },
36214     
36215     initEvents: function() 
36216     {
36217         if(this.title.length || this.html.length){
36218             this.el.on('mouseenter'  ,this.enter, this);
36219             this.el.on('mouseleave', this.leave, this);
36220         }
36221         
36222         Roo.EventManager.onWindowResize(this.resize, this); 
36223         
36224         if(this.bgimage.length){
36225             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36226             this.imageEl.on('load', this.onImageLoad, this);
36227             return;
36228         }
36229         
36230         this.resize();
36231     },
36232     
36233     onImageLoad : function()
36234     {
36235         this.resize();
36236     },
36237     
36238     resize : function()
36239     {
36240         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36241         
36242         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36243         
36244         if(this.bgimage.length){
36245             var image = this.el.select('.roo-brick-image-view', true).first();
36246             
36247             image.setWidth(paragraph.getWidth());
36248             
36249             if(this.square){
36250                 image.setHeight(paragraph.getWidth());
36251             }
36252             
36253             this.el.setHeight(image.getHeight());
36254             paragraph.setHeight(image.getHeight());
36255             
36256         }
36257         
36258     },
36259     
36260     enter: function(e, el)
36261     {
36262         e.preventDefault();
36263         
36264         if(this.bgimage.length){
36265             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36266             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36267         }
36268     },
36269     
36270     leave: function(e, el)
36271     {
36272         e.preventDefault();
36273         
36274         if(this.bgimage.length){
36275             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36276             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36277         }
36278     }
36279     
36280 });
36281
36282  
36283
36284  /*
36285  * - LGPL
36286  *
36287  * Number field 
36288  */
36289
36290 /**
36291  * @class Roo.bootstrap.NumberField
36292  * @extends Roo.bootstrap.Input
36293  * Bootstrap NumberField class
36294  * 
36295  * 
36296  * 
36297  * 
36298  * @constructor
36299  * Create a new NumberField
36300  * @param {Object} config The config object
36301  */
36302
36303 Roo.bootstrap.NumberField = function(config){
36304     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36305 };
36306
36307 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36308     
36309     /**
36310      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36311      */
36312     allowDecimals : true,
36313     /**
36314      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36315      */
36316     decimalSeparator : ".",
36317     /**
36318      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36319      */
36320     decimalPrecision : 2,
36321     /**
36322      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36323      */
36324     allowNegative : true,
36325     
36326     /**
36327      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36328      */
36329     allowZero: true,
36330     /**
36331      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36332      */
36333     minValue : Number.NEGATIVE_INFINITY,
36334     /**
36335      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36336      */
36337     maxValue : Number.MAX_VALUE,
36338     /**
36339      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36340      */
36341     minText : "The minimum value for this field is {0}",
36342     /**
36343      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36344      */
36345     maxText : "The maximum value for this field is {0}",
36346     /**
36347      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36348      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36349      */
36350     nanText : "{0} is not a valid number",
36351     /**
36352      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36353      */
36354     thousandsDelimiter : false,
36355     /**
36356      * @cfg {String} valueAlign alignment of value
36357      */
36358     valueAlign : "left",
36359
36360     getAutoCreate : function()
36361     {
36362         var hiddenInput = {
36363             tag: 'input',
36364             type: 'hidden',
36365             id: Roo.id(),
36366             cls: 'hidden-number-input'
36367         };
36368         
36369         if (this.name) {
36370             hiddenInput.name = this.name;
36371         }
36372         
36373         this.name = '';
36374         
36375         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36376         
36377         this.name = hiddenInput.name;
36378         
36379         if(cfg.cn.length > 0) {
36380             cfg.cn.push(hiddenInput);
36381         }
36382         
36383         return cfg;
36384     },
36385
36386     // private
36387     initEvents : function()
36388     {   
36389         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36390         
36391         var allowed = "0123456789";
36392         
36393         if(this.allowDecimals){
36394             allowed += this.decimalSeparator;
36395         }
36396         
36397         if(this.allowNegative){
36398             allowed += "-";
36399         }
36400         
36401         if(this.thousandsDelimiter) {
36402             allowed += ",";
36403         }
36404         
36405         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36406         
36407         var keyPress = function(e){
36408             
36409             var k = e.getKey();
36410             
36411             var c = e.getCharCode();
36412             
36413             if(
36414                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36415                     allowed.indexOf(String.fromCharCode(c)) === -1
36416             ){
36417                 e.stopEvent();
36418                 return;
36419             }
36420             
36421             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36422                 return;
36423             }
36424             
36425             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36426                 e.stopEvent();
36427             }
36428         };
36429         
36430         this.el.on("keypress", keyPress, this);
36431     },
36432     
36433     validateValue : function(value)
36434     {
36435         
36436         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36437             return false;
36438         }
36439         
36440         var num = this.parseValue(value);
36441         
36442         if(isNaN(num)){
36443             this.markInvalid(String.format(this.nanText, value));
36444             return false;
36445         }
36446         
36447         if(num < this.minValue){
36448             this.markInvalid(String.format(this.minText, this.minValue));
36449             return false;
36450         }
36451         
36452         if(num > this.maxValue){
36453             this.markInvalid(String.format(this.maxText, this.maxValue));
36454             return false;
36455         }
36456         
36457         return true;
36458     },
36459
36460     getValue : function()
36461     {
36462         var v = this.hiddenEl().getValue();
36463         
36464         return this.fixPrecision(this.parseValue(v));
36465     },
36466
36467     parseValue : function(value)
36468     {
36469         if(this.thousandsDelimiter) {
36470             value += "";
36471             r = new RegExp(",", "g");
36472             value = value.replace(r, "");
36473         }
36474         
36475         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36476         return isNaN(value) ? '' : value;
36477     },
36478
36479     fixPrecision : function(value)
36480     {
36481         if(this.thousandsDelimiter) {
36482             value += "";
36483             r = new RegExp(",", "g");
36484             value = value.replace(r, "");
36485         }
36486         
36487         var nan = isNaN(value);
36488         
36489         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36490             return nan ? '' : value;
36491         }
36492         return parseFloat(value).toFixed(this.decimalPrecision);
36493     },
36494
36495     setValue : function(v)
36496     {
36497         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36498         
36499         this.value = v;
36500         
36501         if(this.rendered){
36502             
36503             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36504             
36505             this.inputEl().dom.value = (v == '') ? '' :
36506                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36507             
36508             if(!this.allowZero && v === '0') {
36509                 this.hiddenEl().dom.value = '';
36510                 this.inputEl().dom.value = '';
36511             }
36512             
36513             this.validate();
36514         }
36515     },
36516
36517     decimalPrecisionFcn : function(v)
36518     {
36519         return Math.floor(v);
36520     },
36521
36522     beforeBlur : function()
36523     {
36524         var v = this.parseValue(this.getRawValue());
36525         
36526         if(v || v === 0 || v === ''){
36527             this.setValue(v);
36528         }
36529     },
36530     
36531     hiddenEl : function()
36532     {
36533         return this.el.select('input.hidden-number-input',true).first();
36534     }
36535     
36536 });
36537
36538  
36539
36540 /*
36541 * Licence: LGPL
36542 */
36543
36544 /**
36545  * @class Roo.bootstrap.DocumentSlider
36546  * @extends Roo.bootstrap.Component
36547  * Bootstrap DocumentSlider class
36548  * 
36549  * @constructor
36550  * Create a new DocumentViewer
36551  * @param {Object} config The config object
36552  */
36553
36554 Roo.bootstrap.DocumentSlider = function(config){
36555     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36556     
36557     this.files = [];
36558     
36559     this.addEvents({
36560         /**
36561          * @event initial
36562          * Fire after initEvent
36563          * @param {Roo.bootstrap.DocumentSlider} this
36564          */
36565         "initial" : true,
36566         /**
36567          * @event update
36568          * Fire after update
36569          * @param {Roo.bootstrap.DocumentSlider} this
36570          */
36571         "update" : true,
36572         /**
36573          * @event click
36574          * Fire after click
36575          * @param {Roo.bootstrap.DocumentSlider} this
36576          */
36577         "click" : true
36578     });
36579 };
36580
36581 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36582     
36583     files : false,
36584     
36585     indicator : 0,
36586     
36587     getAutoCreate : function()
36588     {
36589         var cfg = {
36590             tag : 'div',
36591             cls : 'roo-document-slider',
36592             cn : [
36593                 {
36594                     tag : 'div',
36595                     cls : 'roo-document-slider-header',
36596                     cn : [
36597                         {
36598                             tag : 'div',
36599                             cls : 'roo-document-slider-header-title'
36600                         }
36601                     ]
36602                 },
36603                 {
36604                     tag : 'div',
36605                     cls : 'roo-document-slider-body',
36606                     cn : [
36607                         {
36608                             tag : 'div',
36609                             cls : 'roo-document-slider-prev',
36610                             cn : [
36611                                 {
36612                                     tag : 'i',
36613                                     cls : 'fa fa-chevron-left'
36614                                 }
36615                             ]
36616                         },
36617                         {
36618                             tag : 'div',
36619                             cls : 'roo-document-slider-thumb',
36620                             cn : [
36621                                 {
36622                                     tag : 'img',
36623                                     cls : 'roo-document-slider-image'
36624                                 }
36625                             ]
36626                         },
36627                         {
36628                             tag : 'div',
36629                             cls : 'roo-document-slider-next',
36630                             cn : [
36631                                 {
36632                                     tag : 'i',
36633                                     cls : 'fa fa-chevron-right'
36634                                 }
36635                             ]
36636                         }
36637                     ]
36638                 }
36639             ]
36640         };
36641         
36642         return cfg;
36643     },
36644     
36645     initEvents : function()
36646     {
36647         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36648         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36649         
36650         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36651         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36652         
36653         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36654         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36655         
36656         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36657         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36658         
36659         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36660         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36661         
36662         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36663         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36664         
36665         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36666         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36667         
36668         this.thumbEl.on('click', this.onClick, this);
36669         
36670         this.prevIndicator.on('click', this.prev, this);
36671         
36672         this.nextIndicator.on('click', this.next, this);
36673         
36674     },
36675     
36676     initial : function()
36677     {
36678         if(this.files.length){
36679             this.indicator = 1;
36680             this.update()
36681         }
36682         
36683         this.fireEvent('initial', this);
36684     },
36685     
36686     update : function()
36687     {
36688         this.imageEl.attr('src', this.files[this.indicator - 1]);
36689         
36690         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36691         
36692         this.prevIndicator.show();
36693         
36694         if(this.indicator == 1){
36695             this.prevIndicator.hide();
36696         }
36697         
36698         this.nextIndicator.show();
36699         
36700         if(this.indicator == this.files.length){
36701             this.nextIndicator.hide();
36702         }
36703         
36704         this.thumbEl.scrollTo('top');
36705         
36706         this.fireEvent('update', this);
36707     },
36708     
36709     onClick : function(e)
36710     {
36711         e.preventDefault();
36712         
36713         this.fireEvent('click', this);
36714     },
36715     
36716     prev : function(e)
36717     {
36718         e.preventDefault();
36719         
36720         this.indicator = Math.max(1, this.indicator - 1);
36721         
36722         this.update();
36723     },
36724     
36725     next : function(e)
36726     {
36727         e.preventDefault();
36728         
36729         this.indicator = Math.min(this.files.length, this.indicator + 1);
36730         
36731         this.update();
36732     }
36733 });
36734 /*
36735  * - LGPL
36736  *
36737  * RadioSet
36738  *
36739  *
36740  */
36741
36742 /**
36743  * @class Roo.bootstrap.RadioSet
36744  * @extends Roo.bootstrap.Input
36745  * Bootstrap RadioSet class
36746  * @cfg {String} indicatorpos (left|right) default left
36747  * @cfg {Boolean} inline (true|false) inline the element (default true)
36748  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36749  * @constructor
36750  * Create a new RadioSet
36751  * @param {Object} config The config object
36752  */
36753
36754 Roo.bootstrap.RadioSet = function(config){
36755     
36756     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36757     
36758     this.radioes = [];
36759     
36760     Roo.bootstrap.RadioSet.register(this);
36761     
36762     this.addEvents({
36763         /**
36764         * @event check
36765         * Fires when the element is checked or unchecked.
36766         * @param {Roo.bootstrap.RadioSet} this This radio
36767         * @param {Roo.bootstrap.Radio} item The checked item
36768         */
36769        check : true,
36770        /**
36771         * @event click
36772         * Fires when the element is click.
36773         * @param {Roo.bootstrap.RadioSet} this This radio set
36774         * @param {Roo.bootstrap.Radio} item The checked item
36775         * @param {Roo.EventObject} e The event object
36776         */
36777        click : true
36778     });
36779     
36780 };
36781
36782 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36783
36784     radioes : false,
36785     
36786     inline : true,
36787     
36788     weight : '',
36789     
36790     indicatorpos : 'left',
36791     
36792     getAutoCreate : function()
36793     {
36794         var label = {
36795             tag : 'label',
36796             cls : 'roo-radio-set-label',
36797             cn : [
36798                 {
36799                     tag : 'span',
36800                     html : this.fieldLabel
36801                 }
36802             ]
36803         };
36804         if (Roo.bootstrap.version == 3) {
36805             
36806             
36807             if(this.indicatorpos == 'left'){
36808                 label.cn.unshift({
36809                     tag : 'i',
36810                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36811                     tooltip : 'This field is required'
36812                 });
36813             } else {
36814                 label.cn.push({
36815                     tag : 'i',
36816                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36817                     tooltip : 'This field is required'
36818                 });
36819             }
36820         }
36821         var items = {
36822             tag : 'div',
36823             cls : 'roo-radio-set-items'
36824         };
36825         
36826         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36827         
36828         if (align === 'left' && this.fieldLabel.length) {
36829             
36830             items = {
36831                 cls : "roo-radio-set-right", 
36832                 cn: [
36833                     items
36834                 ]
36835             };
36836             
36837             if(this.labelWidth > 12){
36838                 label.style = "width: " + this.labelWidth + 'px';
36839             }
36840             
36841             if(this.labelWidth < 13 && this.labelmd == 0){
36842                 this.labelmd = this.labelWidth;
36843             }
36844             
36845             if(this.labellg > 0){
36846                 label.cls += ' col-lg-' + this.labellg;
36847                 items.cls += ' col-lg-' + (12 - this.labellg);
36848             }
36849             
36850             if(this.labelmd > 0){
36851                 label.cls += ' col-md-' + this.labelmd;
36852                 items.cls += ' col-md-' + (12 - this.labelmd);
36853             }
36854             
36855             if(this.labelsm > 0){
36856                 label.cls += ' col-sm-' + this.labelsm;
36857                 items.cls += ' col-sm-' + (12 - this.labelsm);
36858             }
36859             
36860             if(this.labelxs > 0){
36861                 label.cls += ' col-xs-' + this.labelxs;
36862                 items.cls += ' col-xs-' + (12 - this.labelxs);
36863             }
36864         }
36865         
36866         var cfg = {
36867             tag : 'div',
36868             cls : 'roo-radio-set',
36869             cn : [
36870                 {
36871                     tag : 'input',
36872                     cls : 'roo-radio-set-input',
36873                     type : 'hidden',
36874                     name : this.name,
36875                     value : this.value ? this.value :  ''
36876                 },
36877                 label,
36878                 items
36879             ]
36880         };
36881         
36882         if(this.weight.length){
36883             cfg.cls += ' roo-radio-' + this.weight;
36884         }
36885         
36886         if(this.inline) {
36887             cfg.cls += ' roo-radio-set-inline';
36888         }
36889         
36890         var settings=this;
36891         ['xs','sm','md','lg'].map(function(size){
36892             if (settings[size]) {
36893                 cfg.cls += ' col-' + size + '-' + settings[size];
36894             }
36895         });
36896         
36897         return cfg;
36898         
36899     },
36900
36901     initEvents : function()
36902     {
36903         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36904         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36905         
36906         if(!this.fieldLabel.length){
36907             this.labelEl.hide();
36908         }
36909         
36910         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36911         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36912         
36913         this.indicator = this.indicatorEl();
36914         
36915         if(this.indicator){
36916             this.indicator.addClass('invisible');
36917         }
36918         
36919         this.originalValue = this.getValue();
36920         
36921     },
36922     
36923     inputEl: function ()
36924     {
36925         return this.el.select('.roo-radio-set-input', true).first();
36926     },
36927     
36928     getChildContainer : function()
36929     {
36930         return this.itemsEl;
36931     },
36932     
36933     register : function(item)
36934     {
36935         this.radioes.push(item);
36936         
36937     },
36938     
36939     validate : function()
36940     {   
36941         if(this.getVisibilityEl().hasClass('hidden')){
36942             return true;
36943         }
36944         
36945         var valid = false;
36946         
36947         Roo.each(this.radioes, function(i){
36948             if(!i.checked){
36949                 return;
36950             }
36951             
36952             valid = true;
36953             return false;
36954         });
36955         
36956         if(this.allowBlank) {
36957             return true;
36958         }
36959         
36960         if(this.disabled || valid){
36961             this.markValid();
36962             return true;
36963         }
36964         
36965         this.markInvalid();
36966         return false;
36967         
36968     },
36969     
36970     markValid : function()
36971     {
36972         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36973             this.indicatorEl().removeClass('visible');
36974             this.indicatorEl().addClass('invisible');
36975         }
36976         
36977         
36978         if (Roo.bootstrap.version == 3) {
36979             this.el.removeClass([this.invalidClass, this.validClass]);
36980             this.el.addClass(this.validClass);
36981         } else {
36982             this.el.removeClass(['is-invalid','is-valid']);
36983             this.el.addClass(['is-valid']);
36984         }
36985         this.fireEvent('valid', this);
36986     },
36987     
36988     markInvalid : function(msg)
36989     {
36990         if(this.allowBlank || this.disabled){
36991             return;
36992         }
36993         
36994         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36995             this.indicatorEl().removeClass('invisible');
36996             this.indicatorEl().addClass('visible');
36997         }
36998         if (Roo.bootstrap.version == 3) {
36999             this.el.removeClass([this.invalidClass, this.validClass]);
37000             this.el.addClass(this.invalidClass);
37001         } else {
37002             this.el.removeClass(['is-invalid','is-valid']);
37003             this.el.addClass(['is-invalid']);
37004         }
37005         
37006         this.fireEvent('invalid', this, msg);
37007         
37008     },
37009     
37010     setValue : function(v, suppressEvent)
37011     {   
37012         if(this.value === v){
37013             return;
37014         }
37015         
37016         this.value = v;
37017         
37018         if(this.rendered){
37019             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37020         }
37021         
37022         Roo.each(this.radioes, function(i){
37023             i.checked = false;
37024             i.el.removeClass('checked');
37025         });
37026         
37027         Roo.each(this.radioes, function(i){
37028             
37029             if(i.value === v || i.value.toString() === v.toString()){
37030                 i.checked = true;
37031                 i.el.addClass('checked');
37032                 
37033                 if(suppressEvent !== true){
37034                     this.fireEvent('check', this, i);
37035                 }
37036                 
37037                 return false;
37038             }
37039             
37040         }, this);
37041         
37042         this.validate();
37043     },
37044     
37045     clearInvalid : function(){
37046         
37047         if(!this.el || this.preventMark){
37048             return;
37049         }
37050         
37051         this.el.removeClass([this.invalidClass]);
37052         
37053         this.fireEvent('valid', this);
37054     }
37055     
37056 });
37057
37058 Roo.apply(Roo.bootstrap.RadioSet, {
37059     
37060     groups: {},
37061     
37062     register : function(set)
37063     {
37064         this.groups[set.name] = set;
37065     },
37066     
37067     get: function(name) 
37068     {
37069         if (typeof(this.groups[name]) == 'undefined') {
37070             return false;
37071         }
37072         
37073         return this.groups[name] ;
37074     }
37075     
37076 });
37077 /*
37078  * Based on:
37079  * Ext JS Library 1.1.1
37080  * Copyright(c) 2006-2007, Ext JS, LLC.
37081  *
37082  * Originally Released Under LGPL - original licence link has changed is not relivant.
37083  *
37084  * Fork - LGPL
37085  * <script type="text/javascript">
37086  */
37087
37088
37089 /**
37090  * @class Roo.bootstrap.SplitBar
37091  * @extends Roo.util.Observable
37092  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37093  * <br><br>
37094  * Usage:
37095  * <pre><code>
37096 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37097                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37098 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37099 split.minSize = 100;
37100 split.maxSize = 600;
37101 split.animate = true;
37102 split.on('moved', splitterMoved);
37103 </code></pre>
37104  * @constructor
37105  * Create a new SplitBar
37106  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37107  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37108  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37109  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37110                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37111                         position of the SplitBar).
37112  */
37113 Roo.bootstrap.SplitBar = function(cfg){
37114     
37115     /** @private */
37116     
37117     //{
37118     //  dragElement : elm
37119     //  resizingElement: el,
37120         // optional..
37121     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37122     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37123         // existingProxy ???
37124     //}
37125     
37126     this.el = Roo.get(cfg.dragElement, true);
37127     this.el.dom.unselectable = "on";
37128     /** @private */
37129     this.resizingEl = Roo.get(cfg.resizingElement, true);
37130
37131     /**
37132      * @private
37133      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37134      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37135      * @type Number
37136      */
37137     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37138     
37139     /**
37140      * The minimum size of the resizing element. (Defaults to 0)
37141      * @type Number
37142      */
37143     this.minSize = 0;
37144     
37145     /**
37146      * The maximum size of the resizing element. (Defaults to 2000)
37147      * @type Number
37148      */
37149     this.maxSize = 2000;
37150     
37151     /**
37152      * Whether to animate the transition to the new size
37153      * @type Boolean
37154      */
37155     this.animate = false;
37156     
37157     /**
37158      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37159      * @type Boolean
37160      */
37161     this.useShim = false;
37162     
37163     /** @private */
37164     this.shim = null;
37165     
37166     if(!cfg.existingProxy){
37167         /** @private */
37168         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37169     }else{
37170         this.proxy = Roo.get(cfg.existingProxy).dom;
37171     }
37172     /** @private */
37173     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37174     
37175     /** @private */
37176     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37177     
37178     /** @private */
37179     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37180     
37181     /** @private */
37182     this.dragSpecs = {};
37183     
37184     /**
37185      * @private The adapter to use to positon and resize elements
37186      */
37187     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37188     this.adapter.init(this);
37189     
37190     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37191         /** @private */
37192         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37193         this.el.addClass("roo-splitbar-h");
37194     }else{
37195         /** @private */
37196         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37197         this.el.addClass("roo-splitbar-v");
37198     }
37199     
37200     this.addEvents({
37201         /**
37202          * @event resize
37203          * Fires when the splitter is moved (alias for {@link #event-moved})
37204          * @param {Roo.bootstrap.SplitBar} this
37205          * @param {Number} newSize the new width or height
37206          */
37207         "resize" : true,
37208         /**
37209          * @event moved
37210          * Fires when the splitter is moved
37211          * @param {Roo.bootstrap.SplitBar} this
37212          * @param {Number} newSize the new width or height
37213          */
37214         "moved" : true,
37215         /**
37216          * @event beforeresize
37217          * Fires before the splitter is dragged
37218          * @param {Roo.bootstrap.SplitBar} this
37219          */
37220         "beforeresize" : true,
37221
37222         "beforeapply" : true
37223     });
37224
37225     Roo.util.Observable.call(this);
37226 };
37227
37228 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37229     onStartProxyDrag : function(x, y){
37230         this.fireEvent("beforeresize", this);
37231         if(!this.overlay){
37232             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37233             o.unselectable();
37234             o.enableDisplayMode("block");
37235             // all splitbars share the same overlay
37236             Roo.bootstrap.SplitBar.prototype.overlay = o;
37237         }
37238         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37239         this.overlay.show();
37240         Roo.get(this.proxy).setDisplayed("block");
37241         var size = this.adapter.getElementSize(this);
37242         this.activeMinSize = this.getMinimumSize();;
37243         this.activeMaxSize = this.getMaximumSize();;
37244         var c1 = size - this.activeMinSize;
37245         var c2 = Math.max(this.activeMaxSize - size, 0);
37246         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37247             this.dd.resetConstraints();
37248             this.dd.setXConstraint(
37249                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37250                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37251             );
37252             this.dd.setYConstraint(0, 0);
37253         }else{
37254             this.dd.resetConstraints();
37255             this.dd.setXConstraint(0, 0);
37256             this.dd.setYConstraint(
37257                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37258                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37259             );
37260          }
37261         this.dragSpecs.startSize = size;
37262         this.dragSpecs.startPoint = [x, y];
37263         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37264     },
37265     
37266     /** 
37267      * @private Called after the drag operation by the DDProxy
37268      */
37269     onEndProxyDrag : function(e){
37270         Roo.get(this.proxy).setDisplayed(false);
37271         var endPoint = Roo.lib.Event.getXY(e);
37272         if(this.overlay){
37273             this.overlay.hide();
37274         }
37275         var newSize;
37276         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37277             newSize = this.dragSpecs.startSize + 
37278                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37279                     endPoint[0] - this.dragSpecs.startPoint[0] :
37280                     this.dragSpecs.startPoint[0] - endPoint[0]
37281                 );
37282         }else{
37283             newSize = this.dragSpecs.startSize + 
37284                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37285                     endPoint[1] - this.dragSpecs.startPoint[1] :
37286                     this.dragSpecs.startPoint[1] - endPoint[1]
37287                 );
37288         }
37289         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37290         if(newSize != this.dragSpecs.startSize){
37291             if(this.fireEvent('beforeapply', this, newSize) !== false){
37292                 this.adapter.setElementSize(this, newSize);
37293                 this.fireEvent("moved", this, newSize);
37294                 this.fireEvent("resize", this, newSize);
37295             }
37296         }
37297     },
37298     
37299     /**
37300      * Get the adapter this SplitBar uses
37301      * @return The adapter object
37302      */
37303     getAdapter : function(){
37304         return this.adapter;
37305     },
37306     
37307     /**
37308      * Set the adapter this SplitBar uses
37309      * @param {Object} adapter A SplitBar adapter object
37310      */
37311     setAdapter : function(adapter){
37312         this.adapter = adapter;
37313         this.adapter.init(this);
37314     },
37315     
37316     /**
37317      * Gets the minimum size for the resizing element
37318      * @return {Number} The minimum size
37319      */
37320     getMinimumSize : function(){
37321         return this.minSize;
37322     },
37323     
37324     /**
37325      * Sets the minimum size for the resizing element
37326      * @param {Number} minSize The minimum size
37327      */
37328     setMinimumSize : function(minSize){
37329         this.minSize = minSize;
37330     },
37331     
37332     /**
37333      * Gets the maximum size for the resizing element
37334      * @return {Number} The maximum size
37335      */
37336     getMaximumSize : function(){
37337         return this.maxSize;
37338     },
37339     
37340     /**
37341      * Sets the maximum size for the resizing element
37342      * @param {Number} maxSize The maximum size
37343      */
37344     setMaximumSize : function(maxSize){
37345         this.maxSize = maxSize;
37346     },
37347     
37348     /**
37349      * Sets the initialize size for the resizing element
37350      * @param {Number} size The initial size
37351      */
37352     setCurrentSize : function(size){
37353         var oldAnimate = this.animate;
37354         this.animate = false;
37355         this.adapter.setElementSize(this, size);
37356         this.animate = oldAnimate;
37357     },
37358     
37359     /**
37360      * Destroy this splitbar. 
37361      * @param {Boolean} removeEl True to remove the element
37362      */
37363     destroy : function(removeEl){
37364         if(this.shim){
37365             this.shim.remove();
37366         }
37367         this.dd.unreg();
37368         this.proxy.parentNode.removeChild(this.proxy);
37369         if(removeEl){
37370             this.el.remove();
37371         }
37372     }
37373 });
37374
37375 /**
37376  * @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.
37377  */
37378 Roo.bootstrap.SplitBar.createProxy = function(dir){
37379     var proxy = new Roo.Element(document.createElement("div"));
37380     proxy.unselectable();
37381     var cls = 'roo-splitbar-proxy';
37382     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37383     document.body.appendChild(proxy.dom);
37384     return proxy.dom;
37385 };
37386
37387 /** 
37388  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37389  * Default Adapter. It assumes the splitter and resizing element are not positioned
37390  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37391  */
37392 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37393 };
37394
37395 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37396     // do nothing for now
37397     init : function(s){
37398     
37399     },
37400     /**
37401      * Called before drag operations to get the current size of the resizing element. 
37402      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37403      */
37404      getElementSize : function(s){
37405         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37406             return s.resizingEl.getWidth();
37407         }else{
37408             return s.resizingEl.getHeight();
37409         }
37410     },
37411     
37412     /**
37413      * Called after drag operations to set the size of the resizing element.
37414      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37415      * @param {Number} newSize The new size to set
37416      * @param {Function} onComplete A function to be invoked when resizing is complete
37417      */
37418     setElementSize : function(s, newSize, onComplete){
37419         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37420             if(!s.animate){
37421                 s.resizingEl.setWidth(newSize);
37422                 if(onComplete){
37423                     onComplete(s, newSize);
37424                 }
37425             }else{
37426                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37427             }
37428         }else{
37429             
37430             if(!s.animate){
37431                 s.resizingEl.setHeight(newSize);
37432                 if(onComplete){
37433                     onComplete(s, newSize);
37434                 }
37435             }else{
37436                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37437             }
37438         }
37439     }
37440 };
37441
37442 /** 
37443  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37444  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37445  * Adapter that  moves the splitter element to align with the resized sizing element. 
37446  * Used with an absolute positioned SplitBar.
37447  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37448  * document.body, make sure you assign an id to the body element.
37449  */
37450 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37451     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37452     this.container = Roo.get(container);
37453 };
37454
37455 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37456     init : function(s){
37457         this.basic.init(s);
37458     },
37459     
37460     getElementSize : function(s){
37461         return this.basic.getElementSize(s);
37462     },
37463     
37464     setElementSize : function(s, newSize, onComplete){
37465         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37466     },
37467     
37468     moveSplitter : function(s){
37469         var yes = Roo.bootstrap.SplitBar;
37470         switch(s.placement){
37471             case yes.LEFT:
37472                 s.el.setX(s.resizingEl.getRight());
37473                 break;
37474             case yes.RIGHT:
37475                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37476                 break;
37477             case yes.TOP:
37478                 s.el.setY(s.resizingEl.getBottom());
37479                 break;
37480             case yes.BOTTOM:
37481                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37482                 break;
37483         }
37484     }
37485 };
37486
37487 /**
37488  * Orientation constant - Create a vertical SplitBar
37489  * @static
37490  * @type Number
37491  */
37492 Roo.bootstrap.SplitBar.VERTICAL = 1;
37493
37494 /**
37495  * Orientation constant - Create a horizontal SplitBar
37496  * @static
37497  * @type Number
37498  */
37499 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37500
37501 /**
37502  * Placement constant - The resizing element is to the left of the splitter element
37503  * @static
37504  * @type Number
37505  */
37506 Roo.bootstrap.SplitBar.LEFT = 1;
37507
37508 /**
37509  * Placement constant - The resizing element is to the right of the splitter element
37510  * @static
37511  * @type Number
37512  */
37513 Roo.bootstrap.SplitBar.RIGHT = 2;
37514
37515 /**
37516  * Placement constant - The resizing element is positioned above the splitter element
37517  * @static
37518  * @type Number
37519  */
37520 Roo.bootstrap.SplitBar.TOP = 3;
37521
37522 /**
37523  * Placement constant - The resizing element is positioned under splitter element
37524  * @static
37525  * @type Number
37526  */
37527 Roo.bootstrap.SplitBar.BOTTOM = 4;
37528 Roo.namespace("Roo.bootstrap.layout");/*
37529  * Based on:
37530  * Ext JS Library 1.1.1
37531  * Copyright(c) 2006-2007, Ext JS, LLC.
37532  *
37533  * Originally Released Under LGPL - original licence link has changed is not relivant.
37534  *
37535  * Fork - LGPL
37536  * <script type="text/javascript">
37537  */
37538
37539 /**
37540  * @class Roo.bootstrap.layout.Manager
37541  * @extends Roo.bootstrap.Component
37542  * Base class for layout managers.
37543  */
37544 Roo.bootstrap.layout.Manager = function(config)
37545 {
37546     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37547
37548
37549
37550
37551
37552     /** false to disable window resize monitoring @type Boolean */
37553     this.monitorWindowResize = true;
37554     this.regions = {};
37555     this.addEvents({
37556         /**
37557          * @event layout
37558          * Fires when a layout is performed.
37559          * @param {Roo.LayoutManager} this
37560          */
37561         "layout" : true,
37562         /**
37563          * @event regionresized
37564          * Fires when the user resizes a region.
37565          * @param {Roo.LayoutRegion} region The resized region
37566          * @param {Number} newSize The new size (width for east/west, height for north/south)
37567          */
37568         "regionresized" : true,
37569         /**
37570          * @event regioncollapsed
37571          * Fires when a region is collapsed.
37572          * @param {Roo.LayoutRegion} region The collapsed region
37573          */
37574         "regioncollapsed" : true,
37575         /**
37576          * @event regionexpanded
37577          * Fires when a region is expanded.
37578          * @param {Roo.LayoutRegion} region The expanded region
37579          */
37580         "regionexpanded" : true
37581     });
37582     this.updating = false;
37583
37584     if (config.el) {
37585         this.el = Roo.get(config.el);
37586         this.initEvents();
37587     }
37588
37589 };
37590
37591 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37592
37593
37594     regions : null,
37595
37596     monitorWindowResize : true,
37597
37598
37599     updating : false,
37600
37601
37602     onRender : function(ct, position)
37603     {
37604         if(!this.el){
37605             this.el = Roo.get(ct);
37606             this.initEvents();
37607         }
37608         //this.fireEvent('render',this);
37609     },
37610
37611
37612     initEvents: function()
37613     {
37614
37615
37616         // ie scrollbar fix
37617         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37618             document.body.scroll = "no";
37619         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37620             this.el.position('relative');
37621         }
37622         this.id = this.el.id;
37623         this.el.addClass("roo-layout-container");
37624         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37625         if(this.el.dom != document.body ) {
37626             this.el.on('resize', this.layout,this);
37627             this.el.on('show', this.layout,this);
37628         }
37629
37630     },
37631
37632     /**
37633      * Returns true if this layout is currently being updated
37634      * @return {Boolean}
37635      */
37636     isUpdating : function(){
37637         return this.updating;
37638     },
37639
37640     /**
37641      * Suspend the LayoutManager from doing auto-layouts while
37642      * making multiple add or remove calls
37643      */
37644     beginUpdate : function(){
37645         this.updating = true;
37646     },
37647
37648     /**
37649      * Restore auto-layouts and optionally disable the manager from performing a layout
37650      * @param {Boolean} noLayout true to disable a layout update
37651      */
37652     endUpdate : function(noLayout){
37653         this.updating = false;
37654         if(!noLayout){
37655             this.layout();
37656         }
37657     },
37658
37659     layout: function(){
37660         // abstract...
37661     },
37662
37663     onRegionResized : function(region, newSize){
37664         this.fireEvent("regionresized", region, newSize);
37665         this.layout();
37666     },
37667
37668     onRegionCollapsed : function(region){
37669         this.fireEvent("regioncollapsed", region);
37670     },
37671
37672     onRegionExpanded : function(region){
37673         this.fireEvent("regionexpanded", region);
37674     },
37675
37676     /**
37677      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37678      * performs box-model adjustments.
37679      * @return {Object} The size as an object {width: (the width), height: (the height)}
37680      */
37681     getViewSize : function()
37682     {
37683         var size;
37684         if(this.el.dom != document.body){
37685             size = this.el.getSize();
37686         }else{
37687             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37688         }
37689         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37690         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37691         return size;
37692     },
37693
37694     /**
37695      * Returns the Element this layout is bound to.
37696      * @return {Roo.Element}
37697      */
37698     getEl : function(){
37699         return this.el;
37700     },
37701
37702     /**
37703      * Returns the specified region.
37704      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37705      * @return {Roo.LayoutRegion}
37706      */
37707     getRegion : function(target){
37708         return this.regions[target.toLowerCase()];
37709     },
37710
37711     onWindowResize : function(){
37712         if(this.monitorWindowResize){
37713             this.layout();
37714         }
37715     }
37716 });
37717 /*
37718  * Based on:
37719  * Ext JS Library 1.1.1
37720  * Copyright(c) 2006-2007, Ext JS, LLC.
37721  *
37722  * Originally Released Under LGPL - original licence link has changed is not relivant.
37723  *
37724  * Fork - LGPL
37725  * <script type="text/javascript">
37726  */
37727 /**
37728  * @class Roo.bootstrap.layout.Border
37729  * @extends Roo.bootstrap.layout.Manager
37730  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37731  * please see: examples/bootstrap/nested.html<br><br>
37732  
37733 <b>The container the layout is rendered into can be either the body element or any other element.
37734 If it is not the body element, the container needs to either be an absolute positioned element,
37735 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37736 the container size if it is not the body element.</b>
37737
37738 * @constructor
37739 * Create a new Border
37740 * @param {Object} config Configuration options
37741  */
37742 Roo.bootstrap.layout.Border = function(config){
37743     config = config || {};
37744     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37745     
37746     
37747     
37748     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37749         if(config[region]){
37750             config[region].region = region;
37751             this.addRegion(config[region]);
37752         }
37753     },this);
37754     
37755 };
37756
37757 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37758
37759 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37760     
37761     parent : false, // this might point to a 'nest' or a ???
37762     
37763     /**
37764      * Creates and adds a new region if it doesn't already exist.
37765      * @param {String} target The target region key (north, south, east, west or center).
37766      * @param {Object} config The regions config object
37767      * @return {BorderLayoutRegion} The new region
37768      */
37769     addRegion : function(config)
37770     {
37771         if(!this.regions[config.region]){
37772             var r = this.factory(config);
37773             this.bindRegion(r);
37774         }
37775         return this.regions[config.region];
37776     },
37777
37778     // private (kinda)
37779     bindRegion : function(r){
37780         this.regions[r.config.region] = r;
37781         
37782         r.on("visibilitychange",    this.layout, this);
37783         r.on("paneladded",          this.layout, this);
37784         r.on("panelremoved",        this.layout, this);
37785         r.on("invalidated",         this.layout, this);
37786         r.on("resized",             this.onRegionResized, this);
37787         r.on("collapsed",           this.onRegionCollapsed, this);
37788         r.on("expanded",            this.onRegionExpanded, this);
37789     },
37790
37791     /**
37792      * Performs a layout update.
37793      */
37794     layout : function()
37795     {
37796         if(this.updating) {
37797             return;
37798         }
37799         
37800         // render all the rebions if they have not been done alreayd?
37801         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37802             if(this.regions[region] && !this.regions[region].bodyEl){
37803                 this.regions[region].onRender(this.el)
37804             }
37805         },this);
37806         
37807         var size = this.getViewSize();
37808         var w = size.width;
37809         var h = size.height;
37810         var centerW = w;
37811         var centerH = h;
37812         var centerY = 0;
37813         var centerX = 0;
37814         //var x = 0, y = 0;
37815
37816         var rs = this.regions;
37817         var north = rs["north"];
37818         var south = rs["south"]; 
37819         var west = rs["west"];
37820         var east = rs["east"];
37821         var center = rs["center"];
37822         //if(this.hideOnLayout){ // not supported anymore
37823             //c.el.setStyle("display", "none");
37824         //}
37825         if(north && north.isVisible()){
37826             var b = north.getBox();
37827             var m = north.getMargins();
37828             b.width = w - (m.left+m.right);
37829             b.x = m.left;
37830             b.y = m.top;
37831             centerY = b.height + b.y + m.bottom;
37832             centerH -= centerY;
37833             north.updateBox(this.safeBox(b));
37834         }
37835         if(south && south.isVisible()){
37836             var b = south.getBox();
37837             var m = south.getMargins();
37838             b.width = w - (m.left+m.right);
37839             b.x = m.left;
37840             var totalHeight = (b.height + m.top + m.bottom);
37841             b.y = h - totalHeight + m.top;
37842             centerH -= totalHeight;
37843             south.updateBox(this.safeBox(b));
37844         }
37845         if(west && west.isVisible()){
37846             var b = west.getBox();
37847             var m = west.getMargins();
37848             b.height = centerH - (m.top+m.bottom);
37849             b.x = m.left;
37850             b.y = centerY + m.top;
37851             var totalWidth = (b.width + m.left + m.right);
37852             centerX += totalWidth;
37853             centerW -= totalWidth;
37854             west.updateBox(this.safeBox(b));
37855         }
37856         if(east && east.isVisible()){
37857             var b = east.getBox();
37858             var m = east.getMargins();
37859             b.height = centerH - (m.top+m.bottom);
37860             var totalWidth = (b.width + m.left + m.right);
37861             b.x = w - totalWidth + m.left;
37862             b.y = centerY + m.top;
37863             centerW -= totalWidth;
37864             east.updateBox(this.safeBox(b));
37865         }
37866         if(center){
37867             var m = center.getMargins();
37868             var centerBox = {
37869                 x: centerX + m.left,
37870                 y: centerY + m.top,
37871                 width: centerW - (m.left+m.right),
37872                 height: centerH - (m.top+m.bottom)
37873             };
37874             //if(this.hideOnLayout){
37875                 //center.el.setStyle("display", "block");
37876             //}
37877             center.updateBox(this.safeBox(centerBox));
37878         }
37879         this.el.repaint();
37880         this.fireEvent("layout", this);
37881     },
37882
37883     // private
37884     safeBox : function(box){
37885         box.width = Math.max(0, box.width);
37886         box.height = Math.max(0, box.height);
37887         return box;
37888     },
37889
37890     /**
37891      * Adds a ContentPanel (or subclass) to this layout.
37892      * @param {String} target The target region key (north, south, east, west or center).
37893      * @param {Roo.ContentPanel} panel The panel to add
37894      * @return {Roo.ContentPanel} The added panel
37895      */
37896     add : function(target, panel){
37897          
37898         target = target.toLowerCase();
37899         return this.regions[target].add(panel);
37900     },
37901
37902     /**
37903      * Remove a ContentPanel (or subclass) to this layout.
37904      * @param {String} target The target region key (north, south, east, west or center).
37905      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37906      * @return {Roo.ContentPanel} The removed panel
37907      */
37908     remove : function(target, panel){
37909         target = target.toLowerCase();
37910         return this.regions[target].remove(panel);
37911     },
37912
37913     /**
37914      * Searches all regions for a panel with the specified id
37915      * @param {String} panelId
37916      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37917      */
37918     findPanel : function(panelId){
37919         var rs = this.regions;
37920         for(var target in rs){
37921             if(typeof rs[target] != "function"){
37922                 var p = rs[target].getPanel(panelId);
37923                 if(p){
37924                     return p;
37925                 }
37926             }
37927         }
37928         return null;
37929     },
37930
37931     /**
37932      * Searches all regions for a panel with the specified id and activates (shows) it.
37933      * @param {String/ContentPanel} panelId The panels id or the panel itself
37934      * @return {Roo.ContentPanel} The shown panel or null
37935      */
37936     showPanel : function(panelId) {
37937       var rs = this.regions;
37938       for(var target in rs){
37939          var r = rs[target];
37940          if(typeof r != "function"){
37941             if(r.hasPanel(panelId)){
37942                return r.showPanel(panelId);
37943             }
37944          }
37945       }
37946       return null;
37947    },
37948
37949    /**
37950      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37951      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37952      */
37953    /*
37954     restoreState : function(provider){
37955         if(!provider){
37956             provider = Roo.state.Manager;
37957         }
37958         var sm = new Roo.LayoutStateManager();
37959         sm.init(this, provider);
37960     },
37961 */
37962  
37963  
37964     /**
37965      * Adds a xtype elements to the layout.
37966      * <pre><code>
37967
37968 layout.addxtype({
37969        xtype : 'ContentPanel',
37970        region: 'west',
37971        items: [ .... ]
37972    }
37973 );
37974
37975 layout.addxtype({
37976         xtype : 'NestedLayoutPanel',
37977         region: 'west',
37978         layout: {
37979            center: { },
37980            west: { }   
37981         },
37982         items : [ ... list of content panels or nested layout panels.. ]
37983    }
37984 );
37985 </code></pre>
37986      * @param {Object} cfg Xtype definition of item to add.
37987      */
37988     addxtype : function(cfg)
37989     {
37990         // basically accepts a pannel...
37991         // can accept a layout region..!?!?
37992         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37993         
37994         
37995         // theory?  children can only be panels??
37996         
37997         //if (!cfg.xtype.match(/Panel$/)) {
37998         //    return false;
37999         //}
38000         var ret = false;
38001         
38002         if (typeof(cfg.region) == 'undefined') {
38003             Roo.log("Failed to add Panel, region was not set");
38004             Roo.log(cfg);
38005             return false;
38006         }
38007         var region = cfg.region;
38008         delete cfg.region;
38009         
38010           
38011         var xitems = [];
38012         if (cfg.items) {
38013             xitems = cfg.items;
38014             delete cfg.items;
38015         }
38016         var nb = false;
38017         
38018         if ( region == 'center') {
38019             Roo.log("Center: " + cfg.title);
38020         }
38021         
38022         
38023         switch(cfg.xtype) 
38024         {
38025             case 'Content':  // ContentPanel (el, cfg)
38026             case 'Scroll':  // ContentPanel (el, cfg)
38027             case 'View': 
38028                 cfg.autoCreate = cfg.autoCreate || true;
38029                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38030                 //} else {
38031                 //    var el = this.el.createChild();
38032                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38033                 //}
38034                 
38035                 this.add(region, ret);
38036                 break;
38037             
38038             /*
38039             case 'TreePanel': // our new panel!
38040                 cfg.el = this.el.createChild();
38041                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38042                 this.add(region, ret);
38043                 break;
38044             */
38045             
38046             case 'Nest': 
38047                 // create a new Layout (which is  a Border Layout...
38048                 
38049                 var clayout = cfg.layout;
38050                 clayout.el  = this.el.createChild();
38051                 clayout.items   = clayout.items  || [];
38052                 
38053                 delete cfg.layout;
38054                 
38055                 // replace this exitems with the clayout ones..
38056                 xitems = clayout.items;
38057                  
38058                 // force background off if it's in center...
38059                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38060                     cfg.background = false;
38061                 }
38062                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38063                 
38064                 
38065                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38066                 //console.log('adding nested layout panel '  + cfg.toSource());
38067                 this.add(region, ret);
38068                 nb = {}; /// find first...
38069                 break;
38070             
38071             case 'Grid':
38072                 
38073                 // needs grid and region
38074                 
38075                 //var el = this.getRegion(region).el.createChild();
38076                 /*
38077                  *var el = this.el.createChild();
38078                 // create the grid first...
38079                 cfg.grid.container = el;
38080                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38081                 */
38082                 
38083                 if (region == 'center' && this.active ) {
38084                     cfg.background = false;
38085                 }
38086                 
38087                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38088                 
38089                 this.add(region, ret);
38090                 /*
38091                 if (cfg.background) {
38092                     // render grid on panel activation (if panel background)
38093                     ret.on('activate', function(gp) {
38094                         if (!gp.grid.rendered) {
38095                     //        gp.grid.render(el);
38096                         }
38097                     });
38098                 } else {
38099                   //  cfg.grid.render(el);
38100                 }
38101                 */
38102                 break;
38103            
38104            
38105             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38106                 // it was the old xcomponent building that caused this before.
38107                 // espeically if border is the top element in the tree.
38108                 ret = this;
38109                 break; 
38110                 
38111                     
38112                 
38113                 
38114                 
38115             default:
38116                 /*
38117                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38118                     
38119                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38120                     this.add(region, ret);
38121                 } else {
38122                 */
38123                     Roo.log(cfg);
38124                     throw "Can not add '" + cfg.xtype + "' to Border";
38125                     return null;
38126              
38127                                 
38128              
38129         }
38130         this.beginUpdate();
38131         // add children..
38132         var region = '';
38133         var abn = {};
38134         Roo.each(xitems, function(i)  {
38135             region = nb && i.region ? i.region : false;
38136             
38137             var add = ret.addxtype(i);
38138            
38139             if (region) {
38140                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38141                 if (!i.background) {
38142                     abn[region] = nb[region] ;
38143                 }
38144             }
38145             
38146         });
38147         this.endUpdate();
38148
38149         // make the last non-background panel active..
38150         //if (nb) { Roo.log(abn); }
38151         if (nb) {
38152             
38153             for(var r in abn) {
38154                 region = this.getRegion(r);
38155                 if (region) {
38156                     // tried using nb[r], but it does not work..
38157                      
38158                     region.showPanel(abn[r]);
38159                    
38160                 }
38161             }
38162         }
38163         return ret;
38164         
38165     },
38166     
38167     
38168 // private
38169     factory : function(cfg)
38170     {
38171         
38172         var validRegions = Roo.bootstrap.layout.Border.regions;
38173
38174         var target = cfg.region;
38175         cfg.mgr = this;
38176         
38177         var r = Roo.bootstrap.layout;
38178         Roo.log(target);
38179         switch(target){
38180             case "north":
38181                 return new r.North(cfg);
38182             case "south":
38183                 return new r.South(cfg);
38184             case "east":
38185                 return new r.East(cfg);
38186             case "west":
38187                 return new r.West(cfg);
38188             case "center":
38189                 return new r.Center(cfg);
38190         }
38191         throw 'Layout region "'+target+'" not supported.';
38192     }
38193     
38194     
38195 });
38196  /*
38197  * Based on:
38198  * Ext JS Library 1.1.1
38199  * Copyright(c) 2006-2007, Ext JS, LLC.
38200  *
38201  * Originally Released Under LGPL - original licence link has changed is not relivant.
38202  *
38203  * Fork - LGPL
38204  * <script type="text/javascript">
38205  */
38206  
38207 /**
38208  * @class Roo.bootstrap.layout.Basic
38209  * @extends Roo.util.Observable
38210  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38211  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38212  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38213  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38214  * @cfg {string}   region  the region that it inhabits..
38215  * @cfg {bool}   skipConfig skip config?
38216  * 
38217
38218  */
38219 Roo.bootstrap.layout.Basic = function(config){
38220     
38221     this.mgr = config.mgr;
38222     
38223     this.position = config.region;
38224     
38225     var skipConfig = config.skipConfig;
38226     
38227     this.events = {
38228         /**
38229          * @scope Roo.BasicLayoutRegion
38230          */
38231         
38232         /**
38233          * @event beforeremove
38234          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38235          * @param {Roo.LayoutRegion} this
38236          * @param {Roo.ContentPanel} panel The panel
38237          * @param {Object} e The cancel event object
38238          */
38239         "beforeremove" : true,
38240         /**
38241          * @event invalidated
38242          * Fires when the layout for this region is changed.
38243          * @param {Roo.LayoutRegion} this
38244          */
38245         "invalidated" : true,
38246         /**
38247          * @event visibilitychange
38248          * Fires when this region is shown or hidden 
38249          * @param {Roo.LayoutRegion} this
38250          * @param {Boolean} visibility true or false
38251          */
38252         "visibilitychange" : true,
38253         /**
38254          * @event paneladded
38255          * Fires when a panel is added. 
38256          * @param {Roo.LayoutRegion} this
38257          * @param {Roo.ContentPanel} panel The panel
38258          */
38259         "paneladded" : true,
38260         /**
38261          * @event panelremoved
38262          * Fires when a panel is removed. 
38263          * @param {Roo.LayoutRegion} this
38264          * @param {Roo.ContentPanel} panel The panel
38265          */
38266         "panelremoved" : true,
38267         /**
38268          * @event beforecollapse
38269          * Fires when this region before collapse.
38270          * @param {Roo.LayoutRegion} this
38271          */
38272         "beforecollapse" : true,
38273         /**
38274          * @event collapsed
38275          * Fires when this region is collapsed.
38276          * @param {Roo.LayoutRegion} this
38277          */
38278         "collapsed" : true,
38279         /**
38280          * @event expanded
38281          * Fires when this region is expanded.
38282          * @param {Roo.LayoutRegion} this
38283          */
38284         "expanded" : true,
38285         /**
38286          * @event slideshow
38287          * Fires when this region is slid into view.
38288          * @param {Roo.LayoutRegion} this
38289          */
38290         "slideshow" : true,
38291         /**
38292          * @event slidehide
38293          * Fires when this region slides out of view. 
38294          * @param {Roo.LayoutRegion} this
38295          */
38296         "slidehide" : true,
38297         /**
38298          * @event panelactivated
38299          * Fires when a panel is activated. 
38300          * @param {Roo.LayoutRegion} this
38301          * @param {Roo.ContentPanel} panel The activated panel
38302          */
38303         "panelactivated" : true,
38304         /**
38305          * @event resized
38306          * Fires when the user resizes this region. 
38307          * @param {Roo.LayoutRegion} this
38308          * @param {Number} newSize The new size (width for east/west, height for north/south)
38309          */
38310         "resized" : true
38311     };
38312     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38313     this.panels = new Roo.util.MixedCollection();
38314     this.panels.getKey = this.getPanelId.createDelegate(this);
38315     this.box = null;
38316     this.activePanel = null;
38317     // ensure listeners are added...
38318     
38319     if (config.listeners || config.events) {
38320         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38321             listeners : config.listeners || {},
38322             events : config.events || {}
38323         });
38324     }
38325     
38326     if(skipConfig !== true){
38327         this.applyConfig(config);
38328     }
38329 };
38330
38331 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38332 {
38333     getPanelId : function(p){
38334         return p.getId();
38335     },
38336     
38337     applyConfig : function(config){
38338         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38339         this.config = config;
38340         
38341     },
38342     
38343     /**
38344      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38345      * the width, for horizontal (north, south) the height.
38346      * @param {Number} newSize The new width or height
38347      */
38348     resizeTo : function(newSize){
38349         var el = this.el ? this.el :
38350                  (this.activePanel ? this.activePanel.getEl() : null);
38351         if(el){
38352             switch(this.position){
38353                 case "east":
38354                 case "west":
38355                     el.setWidth(newSize);
38356                     this.fireEvent("resized", this, newSize);
38357                 break;
38358                 case "north":
38359                 case "south":
38360                     el.setHeight(newSize);
38361                     this.fireEvent("resized", this, newSize);
38362                 break;                
38363             }
38364         }
38365     },
38366     
38367     getBox : function(){
38368         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38369     },
38370     
38371     getMargins : function(){
38372         return this.margins;
38373     },
38374     
38375     updateBox : function(box){
38376         this.box = box;
38377         var el = this.activePanel.getEl();
38378         el.dom.style.left = box.x + "px";
38379         el.dom.style.top = box.y + "px";
38380         this.activePanel.setSize(box.width, box.height);
38381     },
38382     
38383     /**
38384      * Returns the container element for this region.
38385      * @return {Roo.Element}
38386      */
38387     getEl : function(){
38388         return this.activePanel;
38389     },
38390     
38391     /**
38392      * Returns true if this region is currently visible.
38393      * @return {Boolean}
38394      */
38395     isVisible : function(){
38396         return this.activePanel ? true : false;
38397     },
38398     
38399     setActivePanel : function(panel){
38400         panel = this.getPanel(panel);
38401         if(this.activePanel && this.activePanel != panel){
38402             this.activePanel.setActiveState(false);
38403             this.activePanel.getEl().setLeftTop(-10000,-10000);
38404         }
38405         this.activePanel = panel;
38406         panel.setActiveState(true);
38407         if(this.box){
38408             panel.setSize(this.box.width, this.box.height);
38409         }
38410         this.fireEvent("panelactivated", this, panel);
38411         this.fireEvent("invalidated");
38412     },
38413     
38414     /**
38415      * Show the specified panel.
38416      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38417      * @return {Roo.ContentPanel} The shown panel or null
38418      */
38419     showPanel : function(panel){
38420         panel = this.getPanel(panel);
38421         if(panel){
38422             this.setActivePanel(panel);
38423         }
38424         return panel;
38425     },
38426     
38427     /**
38428      * Get the active panel for this region.
38429      * @return {Roo.ContentPanel} The active panel or null
38430      */
38431     getActivePanel : function(){
38432         return this.activePanel;
38433     },
38434     
38435     /**
38436      * Add the passed ContentPanel(s)
38437      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38438      * @return {Roo.ContentPanel} The panel added (if only one was added)
38439      */
38440     add : function(panel){
38441         if(arguments.length > 1){
38442             for(var i = 0, len = arguments.length; i < len; i++) {
38443                 this.add(arguments[i]);
38444             }
38445             return null;
38446         }
38447         if(this.hasPanel(panel)){
38448             this.showPanel(panel);
38449             return panel;
38450         }
38451         var el = panel.getEl();
38452         if(el.dom.parentNode != this.mgr.el.dom){
38453             this.mgr.el.dom.appendChild(el.dom);
38454         }
38455         if(panel.setRegion){
38456             panel.setRegion(this);
38457         }
38458         this.panels.add(panel);
38459         el.setStyle("position", "absolute");
38460         if(!panel.background){
38461             this.setActivePanel(panel);
38462             if(this.config.initialSize && this.panels.getCount()==1){
38463                 this.resizeTo(this.config.initialSize);
38464             }
38465         }
38466         this.fireEvent("paneladded", this, panel);
38467         return panel;
38468     },
38469     
38470     /**
38471      * Returns true if the panel is in this region.
38472      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38473      * @return {Boolean}
38474      */
38475     hasPanel : function(panel){
38476         if(typeof panel == "object"){ // must be panel obj
38477             panel = panel.getId();
38478         }
38479         return this.getPanel(panel) ? true : false;
38480     },
38481     
38482     /**
38483      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38484      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38485      * @param {Boolean} preservePanel Overrides the config preservePanel option
38486      * @return {Roo.ContentPanel} The panel that was removed
38487      */
38488     remove : function(panel, preservePanel){
38489         panel = this.getPanel(panel);
38490         if(!panel){
38491             return null;
38492         }
38493         var e = {};
38494         this.fireEvent("beforeremove", this, panel, e);
38495         if(e.cancel === true){
38496             return null;
38497         }
38498         var panelId = panel.getId();
38499         this.panels.removeKey(panelId);
38500         return panel;
38501     },
38502     
38503     /**
38504      * Returns the panel specified or null if it's not in this region.
38505      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38506      * @return {Roo.ContentPanel}
38507      */
38508     getPanel : function(id){
38509         if(typeof id == "object"){ // must be panel obj
38510             return id;
38511         }
38512         return this.panels.get(id);
38513     },
38514     
38515     /**
38516      * Returns this regions position (north/south/east/west/center).
38517      * @return {String} 
38518      */
38519     getPosition: function(){
38520         return this.position;    
38521     }
38522 });/*
38523  * Based on:
38524  * Ext JS Library 1.1.1
38525  * Copyright(c) 2006-2007, Ext JS, LLC.
38526  *
38527  * Originally Released Under LGPL - original licence link has changed is not relivant.
38528  *
38529  * Fork - LGPL
38530  * <script type="text/javascript">
38531  */
38532  
38533 /**
38534  * @class Roo.bootstrap.layout.Region
38535  * @extends Roo.bootstrap.layout.Basic
38536  * This class represents a region in a layout manager.
38537  
38538  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38539  * @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})
38540  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38541  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38542  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38543  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38544  * @cfg {String}    title           The title for the region (overrides panel titles)
38545  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38546  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38547  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38548  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38549  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38550  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38551  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38552  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38553  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38554  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38555
38556  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38557  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38558  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38559  * @cfg {Number}    width           For East/West panels
38560  * @cfg {Number}    height          For North/South panels
38561  * @cfg {Boolean}   split           To show the splitter
38562  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38563  * 
38564  * @cfg {string}   cls             Extra CSS classes to add to region
38565  * 
38566  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38567  * @cfg {string}   region  the region that it inhabits..
38568  *
38569
38570  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38571  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38572
38573  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38574  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38575  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38576  */
38577 Roo.bootstrap.layout.Region = function(config)
38578 {
38579     this.applyConfig(config);
38580
38581     var mgr = config.mgr;
38582     var pos = config.region;
38583     config.skipConfig = true;
38584     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38585     
38586     if (mgr.el) {
38587         this.onRender(mgr.el);   
38588     }
38589      
38590     this.visible = true;
38591     this.collapsed = false;
38592     this.unrendered_panels = [];
38593 };
38594
38595 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38596
38597     position: '', // set by wrapper (eg. north/south etc..)
38598     unrendered_panels : null,  // unrendered panels.
38599     
38600     tabPosition : false,
38601     
38602     mgr: false, // points to 'Border'
38603     
38604     
38605     createBody : function(){
38606         /** This region's body element 
38607         * @type Roo.Element */
38608         this.bodyEl = this.el.createChild({
38609                 tag: "div",
38610                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38611         });
38612     },
38613
38614     onRender: function(ctr, pos)
38615     {
38616         var dh = Roo.DomHelper;
38617         /** This region's container element 
38618         * @type Roo.Element */
38619         this.el = dh.append(ctr.dom, {
38620                 tag: "div",
38621                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38622             }, true);
38623         /** This region's title element 
38624         * @type Roo.Element */
38625     
38626         this.titleEl = dh.append(this.el.dom,  {
38627                 tag: "div",
38628                 unselectable: "on",
38629                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38630                 children:[
38631                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38632                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38633                 ]
38634             }, true);
38635         
38636         this.titleEl.enableDisplayMode();
38637         /** This region's title text element 
38638         * @type HTMLElement */
38639         this.titleTextEl = this.titleEl.dom.firstChild;
38640         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38641         /*
38642         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38643         this.closeBtn.enableDisplayMode();
38644         this.closeBtn.on("click", this.closeClicked, this);
38645         this.closeBtn.hide();
38646     */
38647         this.createBody(this.config);
38648         if(this.config.hideWhenEmpty){
38649             this.hide();
38650             this.on("paneladded", this.validateVisibility, this);
38651             this.on("panelremoved", this.validateVisibility, this);
38652         }
38653         if(this.autoScroll){
38654             this.bodyEl.setStyle("overflow", "auto");
38655         }else{
38656             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38657         }
38658         //if(c.titlebar !== false){
38659             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38660                 this.titleEl.hide();
38661             }else{
38662                 this.titleEl.show();
38663                 if(this.config.title){
38664                     this.titleTextEl.innerHTML = this.config.title;
38665                 }
38666             }
38667         //}
38668         if(this.config.collapsed){
38669             this.collapse(true);
38670         }
38671         if(this.config.hidden){
38672             this.hide();
38673         }
38674         
38675         if (this.unrendered_panels && this.unrendered_panels.length) {
38676             for (var i =0;i< this.unrendered_panels.length; i++) {
38677                 this.add(this.unrendered_panels[i]);
38678             }
38679             this.unrendered_panels = null;
38680             
38681         }
38682         
38683     },
38684     
38685     applyConfig : function(c)
38686     {
38687         /*
38688          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38689             var dh = Roo.DomHelper;
38690             if(c.titlebar !== false){
38691                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38692                 this.collapseBtn.on("click", this.collapse, this);
38693                 this.collapseBtn.enableDisplayMode();
38694                 /*
38695                 if(c.showPin === true || this.showPin){
38696                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38697                     this.stickBtn.enableDisplayMode();
38698                     this.stickBtn.on("click", this.expand, this);
38699                     this.stickBtn.hide();
38700                 }
38701                 
38702             }
38703             */
38704             /** This region's collapsed element
38705             * @type Roo.Element */
38706             /*
38707              *
38708             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38709                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38710             ]}, true);
38711             
38712             if(c.floatable !== false){
38713                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38714                this.collapsedEl.on("click", this.collapseClick, this);
38715             }
38716
38717             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38718                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38719                    id: "message", unselectable: "on", style:{"float":"left"}});
38720                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38721              }
38722             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38723             this.expandBtn.on("click", this.expand, this);
38724             
38725         }
38726         
38727         if(this.collapseBtn){
38728             this.collapseBtn.setVisible(c.collapsible == true);
38729         }
38730         
38731         this.cmargins = c.cmargins || this.cmargins ||
38732                          (this.position == "west" || this.position == "east" ?
38733                              {top: 0, left: 2, right:2, bottom: 0} :
38734                              {top: 2, left: 0, right:0, bottom: 2});
38735         */
38736         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38737         
38738         
38739         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38740         
38741         this.autoScroll = c.autoScroll || false;
38742         
38743         
38744        
38745         
38746         this.duration = c.duration || .30;
38747         this.slideDuration = c.slideDuration || .45;
38748         this.config = c;
38749        
38750     },
38751     /**
38752      * Returns true if this region is currently visible.
38753      * @return {Boolean}
38754      */
38755     isVisible : function(){
38756         return this.visible;
38757     },
38758
38759     /**
38760      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38761      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38762      */
38763     //setCollapsedTitle : function(title){
38764     //    title = title || "&#160;";
38765      //   if(this.collapsedTitleTextEl){
38766       //      this.collapsedTitleTextEl.innerHTML = title;
38767        // }
38768     //},
38769
38770     getBox : function(){
38771         var b;
38772       //  if(!this.collapsed){
38773             b = this.el.getBox(false, true);
38774        // }else{
38775           //  b = this.collapsedEl.getBox(false, true);
38776         //}
38777         return b;
38778     },
38779
38780     getMargins : function(){
38781         return this.margins;
38782         //return this.collapsed ? this.cmargins : this.margins;
38783     },
38784 /*
38785     highlight : function(){
38786         this.el.addClass("x-layout-panel-dragover");
38787     },
38788
38789     unhighlight : function(){
38790         this.el.removeClass("x-layout-panel-dragover");
38791     },
38792 */
38793     updateBox : function(box)
38794     {
38795         if (!this.bodyEl) {
38796             return; // not rendered yet..
38797         }
38798         
38799         this.box = box;
38800         if(!this.collapsed){
38801             this.el.dom.style.left = box.x + "px";
38802             this.el.dom.style.top = box.y + "px";
38803             this.updateBody(box.width, box.height);
38804         }else{
38805             this.collapsedEl.dom.style.left = box.x + "px";
38806             this.collapsedEl.dom.style.top = box.y + "px";
38807             this.collapsedEl.setSize(box.width, box.height);
38808         }
38809         if(this.tabs){
38810             this.tabs.autoSizeTabs();
38811         }
38812     },
38813
38814     updateBody : function(w, h)
38815     {
38816         if(w !== null){
38817             this.el.setWidth(w);
38818             w -= this.el.getBorderWidth("rl");
38819             if(this.config.adjustments){
38820                 w += this.config.adjustments[0];
38821             }
38822         }
38823         if(h !== null && h > 0){
38824             this.el.setHeight(h);
38825             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38826             h -= this.el.getBorderWidth("tb");
38827             if(this.config.adjustments){
38828                 h += this.config.adjustments[1];
38829             }
38830             this.bodyEl.setHeight(h);
38831             if(this.tabs){
38832                 h = this.tabs.syncHeight(h);
38833             }
38834         }
38835         if(this.panelSize){
38836             w = w !== null ? w : this.panelSize.width;
38837             h = h !== null ? h : this.panelSize.height;
38838         }
38839         if(this.activePanel){
38840             var el = this.activePanel.getEl();
38841             w = w !== null ? w : el.getWidth();
38842             h = h !== null ? h : el.getHeight();
38843             this.panelSize = {width: w, height: h};
38844             this.activePanel.setSize(w, h);
38845         }
38846         if(Roo.isIE && this.tabs){
38847             this.tabs.el.repaint();
38848         }
38849     },
38850
38851     /**
38852      * Returns the container element for this region.
38853      * @return {Roo.Element}
38854      */
38855     getEl : function(){
38856         return this.el;
38857     },
38858
38859     /**
38860      * Hides this region.
38861      */
38862     hide : function(){
38863         //if(!this.collapsed){
38864             this.el.dom.style.left = "-2000px";
38865             this.el.hide();
38866         //}else{
38867          //   this.collapsedEl.dom.style.left = "-2000px";
38868          //   this.collapsedEl.hide();
38869        // }
38870         this.visible = false;
38871         this.fireEvent("visibilitychange", this, false);
38872     },
38873
38874     /**
38875      * Shows this region if it was previously hidden.
38876      */
38877     show : function(){
38878         //if(!this.collapsed){
38879             this.el.show();
38880         //}else{
38881         //    this.collapsedEl.show();
38882        // }
38883         this.visible = true;
38884         this.fireEvent("visibilitychange", this, true);
38885     },
38886 /*
38887     closeClicked : function(){
38888         if(this.activePanel){
38889             this.remove(this.activePanel);
38890         }
38891     },
38892
38893     collapseClick : function(e){
38894         if(this.isSlid){
38895            e.stopPropagation();
38896            this.slideIn();
38897         }else{
38898            e.stopPropagation();
38899            this.slideOut();
38900         }
38901     },
38902 */
38903     /**
38904      * Collapses this region.
38905      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38906      */
38907     /*
38908     collapse : function(skipAnim, skipCheck = false){
38909         if(this.collapsed) {
38910             return;
38911         }
38912         
38913         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38914             
38915             this.collapsed = true;
38916             if(this.split){
38917                 this.split.el.hide();
38918             }
38919             if(this.config.animate && skipAnim !== true){
38920                 this.fireEvent("invalidated", this);
38921                 this.animateCollapse();
38922             }else{
38923                 this.el.setLocation(-20000,-20000);
38924                 this.el.hide();
38925                 this.collapsedEl.show();
38926                 this.fireEvent("collapsed", this);
38927                 this.fireEvent("invalidated", this);
38928             }
38929         }
38930         
38931     },
38932 */
38933     animateCollapse : function(){
38934         // overridden
38935     },
38936
38937     /**
38938      * Expands this region if it was previously collapsed.
38939      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38940      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38941      */
38942     /*
38943     expand : function(e, skipAnim){
38944         if(e) {
38945             e.stopPropagation();
38946         }
38947         if(!this.collapsed || this.el.hasActiveFx()) {
38948             return;
38949         }
38950         if(this.isSlid){
38951             this.afterSlideIn();
38952             skipAnim = true;
38953         }
38954         this.collapsed = false;
38955         if(this.config.animate && skipAnim !== true){
38956             this.animateExpand();
38957         }else{
38958             this.el.show();
38959             if(this.split){
38960                 this.split.el.show();
38961             }
38962             this.collapsedEl.setLocation(-2000,-2000);
38963             this.collapsedEl.hide();
38964             this.fireEvent("invalidated", this);
38965             this.fireEvent("expanded", this);
38966         }
38967     },
38968 */
38969     animateExpand : function(){
38970         // overridden
38971     },
38972
38973     initTabs : function()
38974     {
38975         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38976         
38977         var ts = new Roo.bootstrap.panel.Tabs({
38978             el: this.bodyEl.dom,
38979             region : this,
38980             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38981             disableTooltips: this.config.disableTabTips,
38982             toolbar : this.config.toolbar
38983         });
38984         
38985         if(this.config.hideTabs){
38986             ts.stripWrap.setDisplayed(false);
38987         }
38988         this.tabs = ts;
38989         ts.resizeTabs = this.config.resizeTabs === true;
38990         ts.minTabWidth = this.config.minTabWidth || 40;
38991         ts.maxTabWidth = this.config.maxTabWidth || 250;
38992         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38993         ts.monitorResize = false;
38994         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38995         ts.bodyEl.addClass('roo-layout-tabs-body');
38996         this.panels.each(this.initPanelAsTab, this);
38997     },
38998
38999     initPanelAsTab : function(panel){
39000         var ti = this.tabs.addTab(
39001             panel.getEl().id,
39002             panel.getTitle(),
39003             null,
39004             this.config.closeOnTab && panel.isClosable(),
39005             panel.tpl
39006         );
39007         if(panel.tabTip !== undefined){
39008             ti.setTooltip(panel.tabTip);
39009         }
39010         ti.on("activate", function(){
39011               this.setActivePanel(panel);
39012         }, this);
39013         
39014         if(this.config.closeOnTab){
39015             ti.on("beforeclose", function(t, e){
39016                 e.cancel = true;
39017                 this.remove(panel);
39018             }, this);
39019         }
39020         
39021         panel.tabItem = ti;
39022         
39023         return ti;
39024     },
39025
39026     updatePanelTitle : function(panel, title)
39027     {
39028         if(this.activePanel == panel){
39029             this.updateTitle(title);
39030         }
39031         if(this.tabs){
39032             var ti = this.tabs.getTab(panel.getEl().id);
39033             ti.setText(title);
39034             if(panel.tabTip !== undefined){
39035                 ti.setTooltip(panel.tabTip);
39036             }
39037         }
39038     },
39039
39040     updateTitle : function(title){
39041         if(this.titleTextEl && !this.config.title){
39042             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39043         }
39044     },
39045
39046     setActivePanel : function(panel)
39047     {
39048         panel = this.getPanel(panel);
39049         if(this.activePanel && this.activePanel != panel){
39050             if(this.activePanel.setActiveState(false) === false){
39051                 return;
39052             }
39053         }
39054         this.activePanel = panel;
39055         panel.setActiveState(true);
39056         if(this.panelSize){
39057             panel.setSize(this.panelSize.width, this.panelSize.height);
39058         }
39059         if(this.closeBtn){
39060             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39061         }
39062         this.updateTitle(panel.getTitle());
39063         if(this.tabs){
39064             this.fireEvent("invalidated", this);
39065         }
39066         this.fireEvent("panelactivated", this, panel);
39067     },
39068
39069     /**
39070      * Shows the specified panel.
39071      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39072      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39073      */
39074     showPanel : function(panel)
39075     {
39076         panel = this.getPanel(panel);
39077         if(panel){
39078             if(this.tabs){
39079                 var tab = this.tabs.getTab(panel.getEl().id);
39080                 if(tab.isHidden()){
39081                     this.tabs.unhideTab(tab.id);
39082                 }
39083                 tab.activate();
39084             }else{
39085                 this.setActivePanel(panel);
39086             }
39087         }
39088         return panel;
39089     },
39090
39091     /**
39092      * Get the active panel for this region.
39093      * @return {Roo.ContentPanel} The active panel or null
39094      */
39095     getActivePanel : function(){
39096         return this.activePanel;
39097     },
39098
39099     validateVisibility : function(){
39100         if(this.panels.getCount() < 1){
39101             this.updateTitle("&#160;");
39102             this.closeBtn.hide();
39103             this.hide();
39104         }else{
39105             if(!this.isVisible()){
39106                 this.show();
39107             }
39108         }
39109     },
39110
39111     /**
39112      * Adds the passed ContentPanel(s) to this region.
39113      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39114      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39115      */
39116     add : function(panel)
39117     {
39118         if(arguments.length > 1){
39119             for(var i = 0, len = arguments.length; i < len; i++) {
39120                 this.add(arguments[i]);
39121             }
39122             return null;
39123         }
39124         
39125         // if we have not been rendered yet, then we can not really do much of this..
39126         if (!this.bodyEl) {
39127             this.unrendered_panels.push(panel);
39128             return panel;
39129         }
39130         
39131         
39132         
39133         
39134         if(this.hasPanel(panel)){
39135             this.showPanel(panel);
39136             return panel;
39137         }
39138         panel.setRegion(this);
39139         this.panels.add(panel);
39140        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39141             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39142             // and hide them... ???
39143             this.bodyEl.dom.appendChild(panel.getEl().dom);
39144             if(panel.background !== true){
39145                 this.setActivePanel(panel);
39146             }
39147             this.fireEvent("paneladded", this, panel);
39148             return panel;
39149         }
39150         */
39151         if(!this.tabs){
39152             this.initTabs();
39153         }else{
39154             this.initPanelAsTab(panel);
39155         }
39156         
39157         
39158         if(panel.background !== true){
39159             this.tabs.activate(panel.getEl().id);
39160         }
39161         this.fireEvent("paneladded", this, panel);
39162         return panel;
39163     },
39164
39165     /**
39166      * Hides the tab for the specified panel.
39167      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39168      */
39169     hidePanel : function(panel){
39170         if(this.tabs && (panel = this.getPanel(panel))){
39171             this.tabs.hideTab(panel.getEl().id);
39172         }
39173     },
39174
39175     /**
39176      * Unhides the tab for a previously hidden panel.
39177      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39178      */
39179     unhidePanel : function(panel){
39180         if(this.tabs && (panel = this.getPanel(panel))){
39181             this.tabs.unhideTab(panel.getEl().id);
39182         }
39183     },
39184
39185     clearPanels : function(){
39186         while(this.panels.getCount() > 0){
39187              this.remove(this.panels.first());
39188         }
39189     },
39190
39191     /**
39192      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39193      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39194      * @param {Boolean} preservePanel Overrides the config preservePanel option
39195      * @return {Roo.ContentPanel} The panel that was removed
39196      */
39197     remove : function(panel, preservePanel)
39198     {
39199         panel = this.getPanel(panel);
39200         if(!panel){
39201             return null;
39202         }
39203         var e = {};
39204         this.fireEvent("beforeremove", this, panel, e);
39205         if(e.cancel === true){
39206             return null;
39207         }
39208         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39209         var panelId = panel.getId();
39210         this.panels.removeKey(panelId);
39211         if(preservePanel){
39212             document.body.appendChild(panel.getEl().dom);
39213         }
39214         if(this.tabs){
39215             this.tabs.removeTab(panel.getEl().id);
39216         }else if (!preservePanel){
39217             this.bodyEl.dom.removeChild(panel.getEl().dom);
39218         }
39219         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39220             var p = this.panels.first();
39221             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39222             tempEl.appendChild(p.getEl().dom);
39223             this.bodyEl.update("");
39224             this.bodyEl.dom.appendChild(p.getEl().dom);
39225             tempEl = null;
39226             this.updateTitle(p.getTitle());
39227             this.tabs = null;
39228             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39229             this.setActivePanel(p);
39230         }
39231         panel.setRegion(null);
39232         if(this.activePanel == panel){
39233             this.activePanel = null;
39234         }
39235         if(this.config.autoDestroy !== false && preservePanel !== true){
39236             try{panel.destroy();}catch(e){}
39237         }
39238         this.fireEvent("panelremoved", this, panel);
39239         return panel;
39240     },
39241
39242     /**
39243      * Returns the TabPanel component used by this region
39244      * @return {Roo.TabPanel}
39245      */
39246     getTabs : function(){
39247         return this.tabs;
39248     },
39249
39250     createTool : function(parentEl, className){
39251         var btn = Roo.DomHelper.append(parentEl, {
39252             tag: "div",
39253             cls: "x-layout-tools-button",
39254             children: [ {
39255                 tag: "div",
39256                 cls: "roo-layout-tools-button-inner " + className,
39257                 html: "&#160;"
39258             }]
39259         }, true);
39260         btn.addClassOnOver("roo-layout-tools-button-over");
39261         return btn;
39262     }
39263 });/*
39264  * Based on:
39265  * Ext JS Library 1.1.1
39266  * Copyright(c) 2006-2007, Ext JS, LLC.
39267  *
39268  * Originally Released Under LGPL - original licence link has changed is not relivant.
39269  *
39270  * Fork - LGPL
39271  * <script type="text/javascript">
39272  */
39273  
39274
39275
39276 /**
39277  * @class Roo.SplitLayoutRegion
39278  * @extends Roo.LayoutRegion
39279  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39280  */
39281 Roo.bootstrap.layout.Split = function(config){
39282     this.cursor = config.cursor;
39283     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39284 };
39285
39286 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39287 {
39288     splitTip : "Drag to resize.",
39289     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39290     useSplitTips : false,
39291
39292     applyConfig : function(config){
39293         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39294     },
39295     
39296     onRender : function(ctr,pos) {
39297         
39298         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39299         if(!this.config.split){
39300             return;
39301         }
39302         if(!this.split){
39303             
39304             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39305                             tag: "div",
39306                             id: this.el.id + "-split",
39307                             cls: "roo-layout-split roo-layout-split-"+this.position,
39308                             html: "&#160;"
39309             });
39310             /** The SplitBar for this region 
39311             * @type Roo.SplitBar */
39312             // does not exist yet...
39313             Roo.log([this.position, this.orientation]);
39314             
39315             this.split = new Roo.bootstrap.SplitBar({
39316                 dragElement : splitEl,
39317                 resizingElement: this.el,
39318                 orientation : this.orientation
39319             });
39320             
39321             this.split.on("moved", this.onSplitMove, this);
39322             this.split.useShim = this.config.useShim === true;
39323             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39324             if(this.useSplitTips){
39325                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39326             }
39327             //if(config.collapsible){
39328             //    this.split.el.on("dblclick", this.collapse,  this);
39329             //}
39330         }
39331         if(typeof this.config.minSize != "undefined"){
39332             this.split.minSize = this.config.minSize;
39333         }
39334         if(typeof this.config.maxSize != "undefined"){
39335             this.split.maxSize = this.config.maxSize;
39336         }
39337         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39338             this.hideSplitter();
39339         }
39340         
39341     },
39342
39343     getHMaxSize : function(){
39344          var cmax = this.config.maxSize || 10000;
39345          var center = this.mgr.getRegion("center");
39346          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39347     },
39348
39349     getVMaxSize : function(){
39350          var cmax = this.config.maxSize || 10000;
39351          var center = this.mgr.getRegion("center");
39352          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39353     },
39354
39355     onSplitMove : function(split, newSize){
39356         this.fireEvent("resized", this, newSize);
39357     },
39358     
39359     /** 
39360      * Returns the {@link Roo.SplitBar} for this region.
39361      * @return {Roo.SplitBar}
39362      */
39363     getSplitBar : function(){
39364         return this.split;
39365     },
39366     
39367     hide : function(){
39368         this.hideSplitter();
39369         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39370     },
39371
39372     hideSplitter : function(){
39373         if(this.split){
39374             this.split.el.setLocation(-2000,-2000);
39375             this.split.el.hide();
39376         }
39377     },
39378
39379     show : function(){
39380         if(this.split){
39381             this.split.el.show();
39382         }
39383         Roo.bootstrap.layout.Split.superclass.show.call(this);
39384     },
39385     
39386     beforeSlide: function(){
39387         if(Roo.isGecko){// firefox overflow auto bug workaround
39388             this.bodyEl.clip();
39389             if(this.tabs) {
39390                 this.tabs.bodyEl.clip();
39391             }
39392             if(this.activePanel){
39393                 this.activePanel.getEl().clip();
39394                 
39395                 if(this.activePanel.beforeSlide){
39396                     this.activePanel.beforeSlide();
39397                 }
39398             }
39399         }
39400     },
39401     
39402     afterSlide : function(){
39403         if(Roo.isGecko){// firefox overflow auto bug workaround
39404             this.bodyEl.unclip();
39405             if(this.tabs) {
39406                 this.tabs.bodyEl.unclip();
39407             }
39408             if(this.activePanel){
39409                 this.activePanel.getEl().unclip();
39410                 if(this.activePanel.afterSlide){
39411                     this.activePanel.afterSlide();
39412                 }
39413             }
39414         }
39415     },
39416
39417     initAutoHide : function(){
39418         if(this.autoHide !== false){
39419             if(!this.autoHideHd){
39420                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39421                 this.autoHideHd = {
39422                     "mouseout": function(e){
39423                         if(!e.within(this.el, true)){
39424                             st.delay(500);
39425                         }
39426                     },
39427                     "mouseover" : function(e){
39428                         st.cancel();
39429                     },
39430                     scope : this
39431                 };
39432             }
39433             this.el.on(this.autoHideHd);
39434         }
39435     },
39436
39437     clearAutoHide : function(){
39438         if(this.autoHide !== false){
39439             this.el.un("mouseout", this.autoHideHd.mouseout);
39440             this.el.un("mouseover", this.autoHideHd.mouseover);
39441         }
39442     },
39443
39444     clearMonitor : function(){
39445         Roo.get(document).un("click", this.slideInIf, this);
39446     },
39447
39448     // these names are backwards but not changed for compat
39449     slideOut : function(){
39450         if(this.isSlid || this.el.hasActiveFx()){
39451             return;
39452         }
39453         this.isSlid = true;
39454         if(this.collapseBtn){
39455             this.collapseBtn.hide();
39456         }
39457         this.closeBtnState = this.closeBtn.getStyle('display');
39458         this.closeBtn.hide();
39459         if(this.stickBtn){
39460             this.stickBtn.show();
39461         }
39462         this.el.show();
39463         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39464         this.beforeSlide();
39465         this.el.setStyle("z-index", 10001);
39466         this.el.slideIn(this.getSlideAnchor(), {
39467             callback: function(){
39468                 this.afterSlide();
39469                 this.initAutoHide();
39470                 Roo.get(document).on("click", this.slideInIf, this);
39471                 this.fireEvent("slideshow", this);
39472             },
39473             scope: this,
39474             block: true
39475         });
39476     },
39477
39478     afterSlideIn : function(){
39479         this.clearAutoHide();
39480         this.isSlid = false;
39481         this.clearMonitor();
39482         this.el.setStyle("z-index", "");
39483         if(this.collapseBtn){
39484             this.collapseBtn.show();
39485         }
39486         this.closeBtn.setStyle('display', this.closeBtnState);
39487         if(this.stickBtn){
39488             this.stickBtn.hide();
39489         }
39490         this.fireEvent("slidehide", this);
39491     },
39492
39493     slideIn : function(cb){
39494         if(!this.isSlid || this.el.hasActiveFx()){
39495             Roo.callback(cb);
39496             return;
39497         }
39498         this.isSlid = false;
39499         this.beforeSlide();
39500         this.el.slideOut(this.getSlideAnchor(), {
39501             callback: function(){
39502                 this.el.setLeftTop(-10000, -10000);
39503                 this.afterSlide();
39504                 this.afterSlideIn();
39505                 Roo.callback(cb);
39506             },
39507             scope: this,
39508             block: true
39509         });
39510     },
39511     
39512     slideInIf : function(e){
39513         if(!e.within(this.el)){
39514             this.slideIn();
39515         }
39516     },
39517
39518     animateCollapse : function(){
39519         this.beforeSlide();
39520         this.el.setStyle("z-index", 20000);
39521         var anchor = this.getSlideAnchor();
39522         this.el.slideOut(anchor, {
39523             callback : function(){
39524                 this.el.setStyle("z-index", "");
39525                 this.collapsedEl.slideIn(anchor, {duration:.3});
39526                 this.afterSlide();
39527                 this.el.setLocation(-10000,-10000);
39528                 this.el.hide();
39529                 this.fireEvent("collapsed", this);
39530             },
39531             scope: this,
39532             block: true
39533         });
39534     },
39535
39536     animateExpand : function(){
39537         this.beforeSlide();
39538         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39539         this.el.setStyle("z-index", 20000);
39540         this.collapsedEl.hide({
39541             duration:.1
39542         });
39543         this.el.slideIn(this.getSlideAnchor(), {
39544             callback : function(){
39545                 this.el.setStyle("z-index", "");
39546                 this.afterSlide();
39547                 if(this.split){
39548                     this.split.el.show();
39549                 }
39550                 this.fireEvent("invalidated", this);
39551                 this.fireEvent("expanded", this);
39552             },
39553             scope: this,
39554             block: true
39555         });
39556     },
39557
39558     anchors : {
39559         "west" : "left",
39560         "east" : "right",
39561         "north" : "top",
39562         "south" : "bottom"
39563     },
39564
39565     sanchors : {
39566         "west" : "l",
39567         "east" : "r",
39568         "north" : "t",
39569         "south" : "b"
39570     },
39571
39572     canchors : {
39573         "west" : "tl-tr",
39574         "east" : "tr-tl",
39575         "north" : "tl-bl",
39576         "south" : "bl-tl"
39577     },
39578
39579     getAnchor : function(){
39580         return this.anchors[this.position];
39581     },
39582
39583     getCollapseAnchor : function(){
39584         return this.canchors[this.position];
39585     },
39586
39587     getSlideAnchor : function(){
39588         return this.sanchors[this.position];
39589     },
39590
39591     getAlignAdj : function(){
39592         var cm = this.cmargins;
39593         switch(this.position){
39594             case "west":
39595                 return [0, 0];
39596             break;
39597             case "east":
39598                 return [0, 0];
39599             break;
39600             case "north":
39601                 return [0, 0];
39602             break;
39603             case "south":
39604                 return [0, 0];
39605             break;
39606         }
39607     },
39608
39609     getExpandAdj : function(){
39610         var c = this.collapsedEl, cm = this.cmargins;
39611         switch(this.position){
39612             case "west":
39613                 return [-(cm.right+c.getWidth()+cm.left), 0];
39614             break;
39615             case "east":
39616                 return [cm.right+c.getWidth()+cm.left, 0];
39617             break;
39618             case "north":
39619                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39620             break;
39621             case "south":
39622                 return [0, cm.top+cm.bottom+c.getHeight()];
39623             break;
39624         }
39625     }
39626 });/*
39627  * Based on:
39628  * Ext JS Library 1.1.1
39629  * Copyright(c) 2006-2007, Ext JS, LLC.
39630  *
39631  * Originally Released Under LGPL - original licence link has changed is not relivant.
39632  *
39633  * Fork - LGPL
39634  * <script type="text/javascript">
39635  */
39636 /*
39637  * These classes are private internal classes
39638  */
39639 Roo.bootstrap.layout.Center = function(config){
39640     config.region = "center";
39641     Roo.bootstrap.layout.Region.call(this, config);
39642     this.visible = true;
39643     this.minWidth = config.minWidth || 20;
39644     this.minHeight = config.minHeight || 20;
39645 };
39646
39647 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39648     hide : function(){
39649         // center panel can't be hidden
39650     },
39651     
39652     show : function(){
39653         // center panel can't be hidden
39654     },
39655     
39656     getMinWidth: function(){
39657         return this.minWidth;
39658     },
39659     
39660     getMinHeight: function(){
39661         return this.minHeight;
39662     }
39663 });
39664
39665
39666
39667
39668  
39669
39670
39671
39672
39673
39674
39675 Roo.bootstrap.layout.North = function(config)
39676 {
39677     config.region = 'north';
39678     config.cursor = 'n-resize';
39679     
39680     Roo.bootstrap.layout.Split.call(this, config);
39681     
39682     
39683     if(this.split){
39684         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39685         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39686         this.split.el.addClass("roo-layout-split-v");
39687     }
39688     //var size = config.initialSize || config.height;
39689     //if(this.el && typeof size != "undefined"){
39690     //    this.el.setHeight(size);
39691     //}
39692 };
39693 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39694 {
39695     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39696      
39697      
39698     onRender : function(ctr, pos)
39699     {
39700         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39701         var size = this.config.initialSize || this.config.height;
39702         if(this.el && typeof size != "undefined"){
39703             this.el.setHeight(size);
39704         }
39705     
39706     },
39707     
39708     getBox : function(){
39709         if(this.collapsed){
39710             return this.collapsedEl.getBox();
39711         }
39712         var box = this.el.getBox();
39713         if(this.split){
39714             box.height += this.split.el.getHeight();
39715         }
39716         return box;
39717     },
39718     
39719     updateBox : function(box){
39720         if(this.split && !this.collapsed){
39721             box.height -= this.split.el.getHeight();
39722             this.split.el.setLeft(box.x);
39723             this.split.el.setTop(box.y+box.height);
39724             this.split.el.setWidth(box.width);
39725         }
39726         if(this.collapsed){
39727             this.updateBody(box.width, null);
39728         }
39729         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39730     }
39731 });
39732
39733
39734
39735
39736
39737 Roo.bootstrap.layout.South = function(config){
39738     config.region = 'south';
39739     config.cursor = 's-resize';
39740     Roo.bootstrap.layout.Split.call(this, config);
39741     if(this.split){
39742         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39743         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39744         this.split.el.addClass("roo-layout-split-v");
39745     }
39746     
39747 };
39748
39749 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39750     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39751     
39752     onRender : function(ctr, pos)
39753     {
39754         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39755         var size = this.config.initialSize || this.config.height;
39756         if(this.el && typeof size != "undefined"){
39757             this.el.setHeight(size);
39758         }
39759     
39760     },
39761     
39762     getBox : function(){
39763         if(this.collapsed){
39764             return this.collapsedEl.getBox();
39765         }
39766         var box = this.el.getBox();
39767         if(this.split){
39768             var sh = this.split.el.getHeight();
39769             box.height += sh;
39770             box.y -= sh;
39771         }
39772         return box;
39773     },
39774     
39775     updateBox : function(box){
39776         if(this.split && !this.collapsed){
39777             var sh = this.split.el.getHeight();
39778             box.height -= sh;
39779             box.y += sh;
39780             this.split.el.setLeft(box.x);
39781             this.split.el.setTop(box.y-sh);
39782             this.split.el.setWidth(box.width);
39783         }
39784         if(this.collapsed){
39785             this.updateBody(box.width, null);
39786         }
39787         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39788     }
39789 });
39790
39791 Roo.bootstrap.layout.East = function(config){
39792     config.region = "east";
39793     config.cursor = "e-resize";
39794     Roo.bootstrap.layout.Split.call(this, config);
39795     if(this.split){
39796         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39797         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39798         this.split.el.addClass("roo-layout-split-h");
39799     }
39800     
39801 };
39802 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39803     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39804     
39805     onRender : function(ctr, pos)
39806     {
39807         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39808         var size = this.config.initialSize || this.config.width;
39809         if(this.el && typeof size != "undefined"){
39810             this.el.setWidth(size);
39811         }
39812     
39813     },
39814     
39815     getBox : function(){
39816         if(this.collapsed){
39817             return this.collapsedEl.getBox();
39818         }
39819         var box = this.el.getBox();
39820         if(this.split){
39821             var sw = this.split.el.getWidth();
39822             box.width += sw;
39823             box.x -= sw;
39824         }
39825         return box;
39826     },
39827
39828     updateBox : function(box){
39829         if(this.split && !this.collapsed){
39830             var sw = this.split.el.getWidth();
39831             box.width -= sw;
39832             this.split.el.setLeft(box.x);
39833             this.split.el.setTop(box.y);
39834             this.split.el.setHeight(box.height);
39835             box.x += sw;
39836         }
39837         if(this.collapsed){
39838             this.updateBody(null, box.height);
39839         }
39840         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39841     }
39842 });
39843
39844 Roo.bootstrap.layout.West = function(config){
39845     config.region = "west";
39846     config.cursor = "w-resize";
39847     
39848     Roo.bootstrap.layout.Split.call(this, config);
39849     if(this.split){
39850         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39851         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39852         this.split.el.addClass("roo-layout-split-h");
39853     }
39854     
39855 };
39856 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39857     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39858     
39859     onRender: function(ctr, pos)
39860     {
39861         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39862         var size = this.config.initialSize || this.config.width;
39863         if(typeof size != "undefined"){
39864             this.el.setWidth(size);
39865         }
39866     },
39867     
39868     getBox : function(){
39869         if(this.collapsed){
39870             return this.collapsedEl.getBox();
39871         }
39872         var box = this.el.getBox();
39873         if (box.width == 0) {
39874             box.width = this.config.width; // kludge?
39875         }
39876         if(this.split){
39877             box.width += this.split.el.getWidth();
39878         }
39879         return box;
39880     },
39881     
39882     updateBox : function(box){
39883         if(this.split && !this.collapsed){
39884             var sw = this.split.el.getWidth();
39885             box.width -= sw;
39886             this.split.el.setLeft(box.x+box.width);
39887             this.split.el.setTop(box.y);
39888             this.split.el.setHeight(box.height);
39889         }
39890         if(this.collapsed){
39891             this.updateBody(null, box.height);
39892         }
39893         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39894     }
39895 });Roo.namespace("Roo.bootstrap.panel");/*
39896  * Based on:
39897  * Ext JS Library 1.1.1
39898  * Copyright(c) 2006-2007, Ext JS, LLC.
39899  *
39900  * Originally Released Under LGPL - original licence link has changed is not relivant.
39901  *
39902  * Fork - LGPL
39903  * <script type="text/javascript">
39904  */
39905 /**
39906  * @class Roo.ContentPanel
39907  * @extends Roo.util.Observable
39908  * A basic ContentPanel element.
39909  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39910  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39911  * @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
39912  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39913  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39914  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39915  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39916  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39917  * @cfg {String} title          The title for this panel
39918  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39919  * @cfg {String} url            Calls {@link #setUrl} with this value
39920  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39921  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39922  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39923  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39924  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39925  * @cfg {Boolean} badges render the badges
39926  * @cfg {String} cls  extra classes to use  
39927  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39928
39929  * @constructor
39930  * Create a new ContentPanel.
39931  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39932  * @param {String/Object} config A string to set only the title or a config object
39933  * @param {String} content (optional) Set the HTML content for this panel
39934  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39935  */
39936 Roo.bootstrap.panel.Content = function( config){
39937     
39938     this.tpl = config.tpl || false;
39939     
39940     var el = config.el;
39941     var content = config.content;
39942
39943     if(config.autoCreate){ // xtype is available if this is called from factory
39944         el = Roo.id();
39945     }
39946     this.el = Roo.get(el);
39947     if(!this.el && config && config.autoCreate){
39948         if(typeof config.autoCreate == "object"){
39949             if(!config.autoCreate.id){
39950                 config.autoCreate.id = config.id||el;
39951             }
39952             this.el = Roo.DomHelper.append(document.body,
39953                         config.autoCreate, true);
39954         }else{
39955             var elcfg =  {
39956                 tag: "div",
39957                 cls: (config.cls || '') +
39958                     (config.background ? ' bg-' + config.background : '') +
39959                     " roo-layout-inactive-content",
39960                 id: config.id||el
39961             };
39962             if (config.iframe) {
39963                 elcfg.cn = [
39964                     {
39965                         tag : 'iframe',
39966                         style : 'border: 0px',
39967                         src : 'about:blank'
39968                     }
39969                 ];
39970             }
39971               
39972             if (config.html) {
39973                 elcfg.html = config.html;
39974                 
39975             }
39976                         
39977             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39978             if (config.iframe) {
39979                 this.iframeEl = this.el.select('iframe',true).first();
39980             }
39981             
39982         }
39983     } 
39984     this.closable = false;
39985     this.loaded = false;
39986     this.active = false;
39987    
39988       
39989     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39990         
39991         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39992         
39993         this.wrapEl = this.el; //this.el.wrap();
39994         var ti = [];
39995         if (config.toolbar.items) {
39996             ti = config.toolbar.items ;
39997             delete config.toolbar.items ;
39998         }
39999         
40000         var nitems = [];
40001         this.toolbar.render(this.wrapEl, 'before');
40002         for(var i =0;i < ti.length;i++) {
40003           //  Roo.log(['add child', items[i]]);
40004             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40005         }
40006         this.toolbar.items = nitems;
40007         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40008         delete config.toolbar;
40009         
40010     }
40011     /*
40012     // xtype created footer. - not sure if will work as we normally have to render first..
40013     if (this.footer && !this.footer.el && this.footer.xtype) {
40014         if (!this.wrapEl) {
40015             this.wrapEl = this.el.wrap();
40016         }
40017     
40018         this.footer.container = this.wrapEl.createChild();
40019          
40020         this.footer = Roo.factory(this.footer, Roo);
40021         
40022     }
40023     */
40024     
40025      if(typeof config == "string"){
40026         this.title = config;
40027     }else{
40028         Roo.apply(this, config);
40029     }
40030     
40031     if(this.resizeEl){
40032         this.resizeEl = Roo.get(this.resizeEl, true);
40033     }else{
40034         this.resizeEl = this.el;
40035     }
40036     // handle view.xtype
40037     
40038  
40039     
40040     
40041     this.addEvents({
40042         /**
40043          * @event activate
40044          * Fires when this panel is activated. 
40045          * @param {Roo.ContentPanel} this
40046          */
40047         "activate" : true,
40048         /**
40049          * @event deactivate
40050          * Fires when this panel is activated. 
40051          * @param {Roo.ContentPanel} this
40052          */
40053         "deactivate" : true,
40054
40055         /**
40056          * @event resize
40057          * Fires when this panel is resized if fitToFrame is true.
40058          * @param {Roo.ContentPanel} this
40059          * @param {Number} width The width after any component adjustments
40060          * @param {Number} height The height after any component adjustments
40061          */
40062         "resize" : true,
40063         
40064          /**
40065          * @event render
40066          * Fires when this tab is created
40067          * @param {Roo.ContentPanel} this
40068          */
40069         "render" : true
40070         
40071         
40072         
40073     });
40074     
40075
40076     
40077     
40078     if(this.autoScroll && !this.iframe){
40079         this.resizeEl.setStyle("overflow", "auto");
40080     } else {
40081         // fix randome scrolling
40082         //this.el.on('scroll', function() {
40083         //    Roo.log('fix random scolling');
40084         //    this.scrollTo('top',0); 
40085         //});
40086     }
40087     content = content || this.content;
40088     if(content){
40089         this.setContent(content);
40090     }
40091     if(config && config.url){
40092         this.setUrl(this.url, this.params, this.loadOnce);
40093     }
40094     
40095     
40096     
40097     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40098     
40099     if (this.view && typeof(this.view.xtype) != 'undefined') {
40100         this.view.el = this.el.appendChild(document.createElement("div"));
40101         this.view = Roo.factory(this.view); 
40102         this.view.render  &&  this.view.render(false, '');  
40103     }
40104     
40105     
40106     this.fireEvent('render', this);
40107 };
40108
40109 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40110     
40111     cls : '',
40112     background : '',
40113     
40114     tabTip : '',
40115     
40116     iframe : false,
40117     iframeEl : false,
40118     
40119     setRegion : function(region){
40120         this.region = region;
40121         this.setActiveClass(region && !this.background);
40122     },
40123     
40124     
40125     setActiveClass: function(state)
40126     {
40127         if(state){
40128            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40129            this.el.setStyle('position','relative');
40130         }else{
40131            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40132            this.el.setStyle('position', 'absolute');
40133         } 
40134     },
40135     
40136     /**
40137      * Returns the toolbar for this Panel if one was configured. 
40138      * @return {Roo.Toolbar} 
40139      */
40140     getToolbar : function(){
40141         return this.toolbar;
40142     },
40143     
40144     setActiveState : function(active)
40145     {
40146         this.active = active;
40147         this.setActiveClass(active);
40148         if(!active){
40149             if(this.fireEvent("deactivate", this) === false){
40150                 return false;
40151             }
40152             return true;
40153         }
40154         this.fireEvent("activate", this);
40155         return true;
40156     },
40157     /**
40158      * Updates this panel's element (not for iframe)
40159      * @param {String} content The new content
40160      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40161     */
40162     setContent : function(content, loadScripts){
40163         if (this.iframe) {
40164             return;
40165         }
40166         
40167         this.el.update(content, loadScripts);
40168     },
40169
40170     ignoreResize : function(w, h){
40171         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40172             return true;
40173         }else{
40174             this.lastSize = {width: w, height: h};
40175             return false;
40176         }
40177     },
40178     /**
40179      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40180      * @return {Roo.UpdateManager} The UpdateManager
40181      */
40182     getUpdateManager : function(){
40183         if (this.iframe) {
40184             return false;
40185         }
40186         return this.el.getUpdateManager();
40187     },
40188      /**
40189      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40190      * Does not work with IFRAME contents
40191      * @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:
40192 <pre><code>
40193 panel.load({
40194     url: "your-url.php",
40195     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40196     callback: yourFunction,
40197     scope: yourObject, //(optional scope)
40198     discardUrl: false,
40199     nocache: false,
40200     text: "Loading...",
40201     timeout: 30,
40202     scripts: false
40203 });
40204 </code></pre>
40205      
40206      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40207      * 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.
40208      * @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}
40209      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40210      * @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.
40211      * @return {Roo.ContentPanel} this
40212      */
40213     load : function(){
40214         
40215         if (this.iframe) {
40216             return this;
40217         }
40218         
40219         var um = this.el.getUpdateManager();
40220         um.update.apply(um, arguments);
40221         return this;
40222     },
40223
40224
40225     /**
40226      * 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.
40227      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40228      * @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)
40229      * @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)
40230      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40231      */
40232     setUrl : function(url, params, loadOnce){
40233         if (this.iframe) {
40234             this.iframeEl.dom.src = url;
40235             return false;
40236         }
40237         
40238         if(this.refreshDelegate){
40239             this.removeListener("activate", this.refreshDelegate);
40240         }
40241         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40242         this.on("activate", this.refreshDelegate);
40243         return this.el.getUpdateManager();
40244     },
40245     
40246     _handleRefresh : function(url, params, loadOnce){
40247         if(!loadOnce || !this.loaded){
40248             var updater = this.el.getUpdateManager();
40249             updater.update(url, params, this._setLoaded.createDelegate(this));
40250         }
40251     },
40252     
40253     _setLoaded : function(){
40254         this.loaded = true;
40255     }, 
40256     
40257     /**
40258      * Returns this panel's id
40259      * @return {String} 
40260      */
40261     getId : function(){
40262         return this.el.id;
40263     },
40264     
40265     /** 
40266      * Returns this panel's element - used by regiosn to add.
40267      * @return {Roo.Element} 
40268      */
40269     getEl : function(){
40270         return this.wrapEl || this.el;
40271     },
40272     
40273    
40274     
40275     adjustForComponents : function(width, height)
40276     {
40277         //Roo.log('adjustForComponents ');
40278         if(this.resizeEl != this.el){
40279             width -= this.el.getFrameWidth('lr');
40280             height -= this.el.getFrameWidth('tb');
40281         }
40282         if(this.toolbar){
40283             var te = this.toolbar.getEl();
40284             te.setWidth(width);
40285             height -= te.getHeight();
40286         }
40287         if(this.footer){
40288             var te = this.footer.getEl();
40289             te.setWidth(width);
40290             height -= te.getHeight();
40291         }
40292         
40293         
40294         if(this.adjustments){
40295             width += this.adjustments[0];
40296             height += this.adjustments[1];
40297         }
40298         return {"width": width, "height": height};
40299     },
40300     
40301     setSize : function(width, height){
40302         if(this.fitToFrame && !this.ignoreResize(width, height)){
40303             if(this.fitContainer && this.resizeEl != this.el){
40304                 this.el.setSize(width, height);
40305             }
40306             var size = this.adjustForComponents(width, height);
40307             if (this.iframe) {
40308                 this.iframeEl.setSize(width,height);
40309             }
40310             
40311             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40312             this.fireEvent('resize', this, size.width, size.height);
40313             
40314             
40315         }
40316     },
40317     
40318     /**
40319      * Returns this panel's title
40320      * @return {String} 
40321      */
40322     getTitle : function(){
40323         
40324         if (typeof(this.title) != 'object') {
40325             return this.title;
40326         }
40327         
40328         var t = '';
40329         for (var k in this.title) {
40330             if (!this.title.hasOwnProperty(k)) {
40331                 continue;
40332             }
40333             
40334             if (k.indexOf('-') >= 0) {
40335                 var s = k.split('-');
40336                 for (var i = 0; i<s.length; i++) {
40337                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40338                 }
40339             } else {
40340                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40341             }
40342         }
40343         return t;
40344     },
40345     
40346     /**
40347      * Set this panel's title
40348      * @param {String} title
40349      */
40350     setTitle : function(title){
40351         this.title = title;
40352         if(this.region){
40353             this.region.updatePanelTitle(this, title);
40354         }
40355     },
40356     
40357     /**
40358      * Returns true is this panel was configured to be closable
40359      * @return {Boolean} 
40360      */
40361     isClosable : function(){
40362         return this.closable;
40363     },
40364     
40365     beforeSlide : function(){
40366         this.el.clip();
40367         this.resizeEl.clip();
40368     },
40369     
40370     afterSlide : function(){
40371         this.el.unclip();
40372         this.resizeEl.unclip();
40373     },
40374     
40375     /**
40376      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40377      *   Will fail silently if the {@link #setUrl} method has not been called.
40378      *   This does not activate the panel, just updates its content.
40379      */
40380     refresh : function(){
40381         if(this.refreshDelegate){
40382            this.loaded = false;
40383            this.refreshDelegate();
40384         }
40385     },
40386     
40387     /**
40388      * Destroys this panel
40389      */
40390     destroy : function(){
40391         this.el.removeAllListeners();
40392         var tempEl = document.createElement("span");
40393         tempEl.appendChild(this.el.dom);
40394         tempEl.innerHTML = "";
40395         this.el.remove();
40396         this.el = null;
40397     },
40398     
40399     /**
40400      * form - if the content panel contains a form - this is a reference to it.
40401      * @type {Roo.form.Form}
40402      */
40403     form : false,
40404     /**
40405      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40406      *    This contains a reference to it.
40407      * @type {Roo.View}
40408      */
40409     view : false,
40410     
40411       /**
40412      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40413      * <pre><code>
40414
40415 layout.addxtype({
40416        xtype : 'Form',
40417        items: [ .... ]
40418    }
40419 );
40420
40421 </code></pre>
40422      * @param {Object} cfg Xtype definition of item to add.
40423      */
40424     
40425     
40426     getChildContainer: function () {
40427         return this.getEl();
40428     }
40429     
40430     
40431     /*
40432         var  ret = new Roo.factory(cfg);
40433         return ret;
40434         
40435         
40436         // add form..
40437         if (cfg.xtype.match(/^Form$/)) {
40438             
40439             var el;
40440             //if (this.footer) {
40441             //    el = this.footer.container.insertSibling(false, 'before');
40442             //} else {
40443                 el = this.el.createChild();
40444             //}
40445
40446             this.form = new  Roo.form.Form(cfg);
40447             
40448             
40449             if ( this.form.allItems.length) {
40450                 this.form.render(el.dom);
40451             }
40452             return this.form;
40453         }
40454         // should only have one of theses..
40455         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40456             // views.. should not be just added - used named prop 'view''
40457             
40458             cfg.el = this.el.appendChild(document.createElement("div"));
40459             // factory?
40460             
40461             var ret = new Roo.factory(cfg);
40462              
40463              ret.render && ret.render(false, ''); // render blank..
40464             this.view = ret;
40465             return ret;
40466         }
40467         return false;
40468     }
40469     \*/
40470 });
40471  
40472 /**
40473  * @class Roo.bootstrap.panel.Grid
40474  * @extends Roo.bootstrap.panel.Content
40475  * @constructor
40476  * Create a new GridPanel.
40477  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40478  * @param {Object} config A the config object
40479   
40480  */
40481
40482
40483
40484 Roo.bootstrap.panel.Grid = function(config)
40485 {
40486     
40487       
40488     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40489         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40490
40491     config.el = this.wrapper;
40492     //this.el = this.wrapper;
40493     
40494       if (config.container) {
40495         // ctor'ed from a Border/panel.grid
40496         
40497         
40498         this.wrapper.setStyle("overflow", "hidden");
40499         this.wrapper.addClass('roo-grid-container');
40500
40501     }
40502     
40503     
40504     if(config.toolbar){
40505         var tool_el = this.wrapper.createChild();    
40506         this.toolbar = Roo.factory(config.toolbar);
40507         var ti = [];
40508         if (config.toolbar.items) {
40509             ti = config.toolbar.items ;
40510             delete config.toolbar.items ;
40511         }
40512         
40513         var nitems = [];
40514         this.toolbar.render(tool_el);
40515         for(var i =0;i < ti.length;i++) {
40516           //  Roo.log(['add child', items[i]]);
40517             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40518         }
40519         this.toolbar.items = nitems;
40520         
40521         delete config.toolbar;
40522     }
40523     
40524     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40525     config.grid.scrollBody = true;;
40526     config.grid.monitorWindowResize = false; // turn off autosizing
40527     config.grid.autoHeight = false;
40528     config.grid.autoWidth = false;
40529     
40530     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40531     
40532     if (config.background) {
40533         // render grid on panel activation (if panel background)
40534         this.on('activate', function(gp) {
40535             if (!gp.grid.rendered) {
40536                 gp.grid.render(this.wrapper);
40537                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40538             }
40539         });
40540             
40541     } else {
40542         this.grid.render(this.wrapper);
40543         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40544
40545     }
40546     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40547     // ??? needed ??? config.el = this.wrapper;
40548     
40549     
40550     
40551   
40552     // xtype created footer. - not sure if will work as we normally have to render first..
40553     if (this.footer && !this.footer.el && this.footer.xtype) {
40554         
40555         var ctr = this.grid.getView().getFooterPanel(true);
40556         this.footer.dataSource = this.grid.dataSource;
40557         this.footer = Roo.factory(this.footer, Roo);
40558         this.footer.render(ctr);
40559         
40560     }
40561     
40562     
40563     
40564     
40565      
40566 };
40567
40568 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40569     getId : function(){
40570         return this.grid.id;
40571     },
40572     
40573     /**
40574      * Returns the grid for this panel
40575      * @return {Roo.bootstrap.Table} 
40576      */
40577     getGrid : function(){
40578         return this.grid;    
40579     },
40580     
40581     setSize : function(width, height){
40582         if(!this.ignoreResize(width, height)){
40583             var grid = this.grid;
40584             var size = this.adjustForComponents(width, height);
40585             // tfoot is not a footer?
40586           
40587             
40588             var gridel = grid.getGridEl();
40589             gridel.setSize(size.width, size.height);
40590             
40591             var tbd = grid.getGridEl().select('tbody', true).first();
40592             var thd = grid.getGridEl().select('thead',true).first();
40593             var tbf= grid.getGridEl().select('tfoot', true).first();
40594
40595             if (tbf) {
40596                 size.height -= tbf.getHeight();
40597             }
40598             if (thd) {
40599                 size.height -= thd.getHeight();
40600             }
40601             
40602             tbd.setSize(size.width, size.height );
40603             // this is for the account management tab -seems to work there.
40604             var thd = grid.getGridEl().select('thead',true).first();
40605             //if (tbd) {
40606             //    tbd.setSize(size.width, size.height - thd.getHeight());
40607             //}
40608              
40609             grid.autoSize();
40610         }
40611     },
40612      
40613     
40614     
40615     beforeSlide : function(){
40616         this.grid.getView().scroller.clip();
40617     },
40618     
40619     afterSlide : function(){
40620         this.grid.getView().scroller.unclip();
40621     },
40622     
40623     destroy : function(){
40624         this.grid.destroy();
40625         delete this.grid;
40626         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40627     }
40628 });
40629
40630 /**
40631  * @class Roo.bootstrap.panel.Nest
40632  * @extends Roo.bootstrap.panel.Content
40633  * @constructor
40634  * Create a new Panel, that can contain a layout.Border.
40635  * 
40636  * 
40637  * @param {Roo.BorderLayout} layout The layout for this panel
40638  * @param {String/Object} config A string to set only the title or a config object
40639  */
40640 Roo.bootstrap.panel.Nest = function(config)
40641 {
40642     // construct with only one argument..
40643     /* FIXME - implement nicer consturctors
40644     if (layout.layout) {
40645         config = layout;
40646         layout = config.layout;
40647         delete config.layout;
40648     }
40649     if (layout.xtype && !layout.getEl) {
40650         // then layout needs constructing..
40651         layout = Roo.factory(layout, Roo);
40652     }
40653     */
40654     
40655     config.el =  config.layout.getEl();
40656     
40657     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40658     
40659     config.layout.monitorWindowResize = false; // turn off autosizing
40660     this.layout = config.layout;
40661     this.layout.getEl().addClass("roo-layout-nested-layout");
40662     this.layout.parent = this;
40663     
40664     
40665     
40666     
40667 };
40668
40669 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40670
40671     setSize : function(width, height){
40672         if(!this.ignoreResize(width, height)){
40673             var size = this.adjustForComponents(width, height);
40674             var el = this.layout.getEl();
40675             if (size.height < 1) {
40676                 el.setWidth(size.width);   
40677             } else {
40678                 el.setSize(size.width, size.height);
40679             }
40680             var touch = el.dom.offsetWidth;
40681             this.layout.layout();
40682             // ie requires a double layout on the first pass
40683             if(Roo.isIE && !this.initialized){
40684                 this.initialized = true;
40685                 this.layout.layout();
40686             }
40687         }
40688     },
40689     
40690     // activate all subpanels if not currently active..
40691     
40692     setActiveState : function(active){
40693         this.active = active;
40694         this.setActiveClass(active);
40695         
40696         if(!active){
40697             this.fireEvent("deactivate", this);
40698             return;
40699         }
40700         
40701         this.fireEvent("activate", this);
40702         // not sure if this should happen before or after..
40703         if (!this.layout) {
40704             return; // should not happen..
40705         }
40706         var reg = false;
40707         for (var r in this.layout.regions) {
40708             reg = this.layout.getRegion(r);
40709             if (reg.getActivePanel()) {
40710                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40711                 reg.setActivePanel(reg.getActivePanel());
40712                 continue;
40713             }
40714             if (!reg.panels.length) {
40715                 continue;
40716             }
40717             reg.showPanel(reg.getPanel(0));
40718         }
40719         
40720         
40721         
40722         
40723     },
40724     
40725     /**
40726      * Returns the nested BorderLayout for this panel
40727      * @return {Roo.BorderLayout} 
40728      */
40729     getLayout : function(){
40730         return this.layout;
40731     },
40732     
40733      /**
40734      * Adds a xtype elements to the layout of the nested panel
40735      * <pre><code>
40736
40737 panel.addxtype({
40738        xtype : 'ContentPanel',
40739        region: 'west',
40740        items: [ .... ]
40741    }
40742 );
40743
40744 panel.addxtype({
40745         xtype : 'NestedLayoutPanel',
40746         region: 'west',
40747         layout: {
40748            center: { },
40749            west: { }   
40750         },
40751         items : [ ... list of content panels or nested layout panels.. ]
40752    }
40753 );
40754 </code></pre>
40755      * @param {Object} cfg Xtype definition of item to add.
40756      */
40757     addxtype : function(cfg) {
40758         return this.layout.addxtype(cfg);
40759     
40760     }
40761 });/*
40762  * Based on:
40763  * Ext JS Library 1.1.1
40764  * Copyright(c) 2006-2007, Ext JS, LLC.
40765  *
40766  * Originally Released Under LGPL - original licence link has changed is not relivant.
40767  *
40768  * Fork - LGPL
40769  * <script type="text/javascript">
40770  */
40771 /**
40772  * @class Roo.TabPanel
40773  * @extends Roo.util.Observable
40774  * A lightweight tab container.
40775  * <br><br>
40776  * Usage:
40777  * <pre><code>
40778 // basic tabs 1, built from existing content
40779 var tabs = new Roo.TabPanel("tabs1");
40780 tabs.addTab("script", "View Script");
40781 tabs.addTab("markup", "View Markup");
40782 tabs.activate("script");
40783
40784 // more advanced tabs, built from javascript
40785 var jtabs = new Roo.TabPanel("jtabs");
40786 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40787
40788 // set up the UpdateManager
40789 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40790 var updater = tab2.getUpdateManager();
40791 updater.setDefaultUrl("ajax1.htm");
40792 tab2.on('activate', updater.refresh, updater, true);
40793
40794 // Use setUrl for Ajax loading
40795 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40796 tab3.setUrl("ajax2.htm", null, true);
40797
40798 // Disabled tab
40799 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40800 tab4.disable();
40801
40802 jtabs.activate("jtabs-1");
40803  * </code></pre>
40804  * @constructor
40805  * Create a new TabPanel.
40806  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40807  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40808  */
40809 Roo.bootstrap.panel.Tabs = function(config){
40810     /**
40811     * The container element for this TabPanel.
40812     * @type Roo.Element
40813     */
40814     this.el = Roo.get(config.el);
40815     delete config.el;
40816     if(config){
40817         if(typeof config == "boolean"){
40818             this.tabPosition = config ? "bottom" : "top";
40819         }else{
40820             Roo.apply(this, config);
40821         }
40822     }
40823     
40824     if(this.tabPosition == "bottom"){
40825         // if tabs are at the bottom = create the body first.
40826         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40827         this.el.addClass("roo-tabs-bottom");
40828     }
40829     // next create the tabs holders
40830     
40831     if (this.tabPosition == "west"){
40832         
40833         var reg = this.region; // fake it..
40834         while (reg) {
40835             if (!reg.mgr.parent) {
40836                 break;
40837             }
40838             reg = reg.mgr.parent.region;
40839         }
40840         Roo.log("got nest?");
40841         Roo.log(reg);
40842         if (reg.mgr.getRegion('west')) {
40843             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40844             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40845             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40846             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40847             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40848         
40849             
40850         }
40851         
40852         
40853     } else {
40854      
40855         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40856         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40857         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40858         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40859     }
40860     
40861     
40862     if(Roo.isIE){
40863         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40864     }
40865     
40866     // finally - if tabs are at the top, then create the body last..
40867     if(this.tabPosition != "bottom"){
40868         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40869          * @type Roo.Element
40870          */
40871         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40872         this.el.addClass("roo-tabs-top");
40873     }
40874     this.items = [];
40875
40876     this.bodyEl.setStyle("position", "relative");
40877
40878     this.active = null;
40879     this.activateDelegate = this.activate.createDelegate(this);
40880
40881     this.addEvents({
40882         /**
40883          * @event tabchange
40884          * Fires when the active tab changes
40885          * @param {Roo.TabPanel} this
40886          * @param {Roo.TabPanelItem} activePanel The new active tab
40887          */
40888         "tabchange": true,
40889         /**
40890          * @event beforetabchange
40891          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40892          * @param {Roo.TabPanel} this
40893          * @param {Object} e Set cancel to true on this object to cancel the tab change
40894          * @param {Roo.TabPanelItem} tab The tab being changed to
40895          */
40896         "beforetabchange" : true
40897     });
40898
40899     Roo.EventManager.onWindowResize(this.onResize, this);
40900     this.cpad = this.el.getPadding("lr");
40901     this.hiddenCount = 0;
40902
40903
40904     // toolbar on the tabbar support...
40905     if (this.toolbar) {
40906         alert("no toolbar support yet");
40907         this.toolbar  = false;
40908         /*
40909         var tcfg = this.toolbar;
40910         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40911         this.toolbar = new Roo.Toolbar(tcfg);
40912         if (Roo.isSafari) {
40913             var tbl = tcfg.container.child('table', true);
40914             tbl.setAttribute('width', '100%');
40915         }
40916         */
40917         
40918     }
40919    
40920
40921
40922     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40923 };
40924
40925 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40926     /*
40927      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40928      */
40929     tabPosition : "top",
40930     /*
40931      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40932      */
40933     currentTabWidth : 0,
40934     /*
40935      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40936      */
40937     minTabWidth : 40,
40938     /*
40939      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40940      */
40941     maxTabWidth : 250,
40942     /*
40943      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40944      */
40945     preferredTabWidth : 175,
40946     /*
40947      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40948      */
40949     resizeTabs : false,
40950     /*
40951      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40952      */
40953     monitorResize : true,
40954     /*
40955      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40956      */
40957     toolbar : false,  // set by caller..
40958     
40959     region : false, /// set by caller
40960     
40961     disableTooltips : true, // not used yet...
40962
40963     /**
40964      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40965      * @param {String} id The id of the div to use <b>or create</b>
40966      * @param {String} text The text for the tab
40967      * @param {String} content (optional) Content to put in the TabPanelItem body
40968      * @param {Boolean} closable (optional) True to create a close icon on the tab
40969      * @return {Roo.TabPanelItem} The created TabPanelItem
40970      */
40971     addTab : function(id, text, content, closable, tpl)
40972     {
40973         var item = new Roo.bootstrap.panel.TabItem({
40974             panel: this,
40975             id : id,
40976             text : text,
40977             closable : closable,
40978             tpl : tpl
40979         });
40980         this.addTabItem(item);
40981         if(content){
40982             item.setContent(content);
40983         }
40984         return item;
40985     },
40986
40987     /**
40988      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40989      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40990      * @return {Roo.TabPanelItem}
40991      */
40992     getTab : function(id){
40993         return this.items[id];
40994     },
40995
40996     /**
40997      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40998      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40999      */
41000     hideTab : function(id){
41001         var t = this.items[id];
41002         if(!t.isHidden()){
41003            t.setHidden(true);
41004            this.hiddenCount++;
41005            this.autoSizeTabs();
41006         }
41007     },
41008
41009     /**
41010      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41011      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41012      */
41013     unhideTab : function(id){
41014         var t = this.items[id];
41015         if(t.isHidden()){
41016            t.setHidden(false);
41017            this.hiddenCount--;
41018            this.autoSizeTabs();
41019         }
41020     },
41021
41022     /**
41023      * Adds an existing {@link Roo.TabPanelItem}.
41024      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41025      */
41026     addTabItem : function(item)
41027     {
41028         this.items[item.id] = item;
41029         this.items.push(item);
41030         this.autoSizeTabs();
41031       //  if(this.resizeTabs){
41032     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41033   //         this.autoSizeTabs();
41034 //        }else{
41035 //            item.autoSize();
41036        // }
41037     },
41038
41039     /**
41040      * Removes a {@link Roo.TabPanelItem}.
41041      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41042      */
41043     removeTab : function(id){
41044         var items = this.items;
41045         var tab = items[id];
41046         if(!tab) { return; }
41047         var index = items.indexOf(tab);
41048         if(this.active == tab && items.length > 1){
41049             var newTab = this.getNextAvailable(index);
41050             if(newTab) {
41051                 newTab.activate();
41052             }
41053         }
41054         this.stripEl.dom.removeChild(tab.pnode.dom);
41055         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41056             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41057         }
41058         items.splice(index, 1);
41059         delete this.items[tab.id];
41060         tab.fireEvent("close", tab);
41061         tab.purgeListeners();
41062         this.autoSizeTabs();
41063     },
41064
41065     getNextAvailable : function(start){
41066         var items = this.items;
41067         var index = start;
41068         // look for a next tab that will slide over to
41069         // replace the one being removed
41070         while(index < items.length){
41071             var item = items[++index];
41072             if(item && !item.isHidden()){
41073                 return item;
41074             }
41075         }
41076         // if one isn't found select the previous tab (on the left)
41077         index = start;
41078         while(index >= 0){
41079             var item = items[--index];
41080             if(item && !item.isHidden()){
41081                 return item;
41082             }
41083         }
41084         return null;
41085     },
41086
41087     /**
41088      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41089      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41090      */
41091     disableTab : function(id){
41092         var tab = this.items[id];
41093         if(tab && this.active != tab){
41094             tab.disable();
41095         }
41096     },
41097
41098     /**
41099      * Enables a {@link Roo.TabPanelItem} that is disabled.
41100      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41101      */
41102     enableTab : function(id){
41103         var tab = this.items[id];
41104         tab.enable();
41105     },
41106
41107     /**
41108      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41109      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41110      * @return {Roo.TabPanelItem} The TabPanelItem.
41111      */
41112     activate : function(id)
41113     {
41114         //Roo.log('activite:'  + id);
41115         
41116         var tab = this.items[id];
41117         if(!tab){
41118             return null;
41119         }
41120         if(tab == this.active || tab.disabled){
41121             return tab;
41122         }
41123         var e = {};
41124         this.fireEvent("beforetabchange", this, e, tab);
41125         if(e.cancel !== true && !tab.disabled){
41126             if(this.active){
41127                 this.active.hide();
41128             }
41129             this.active = this.items[id];
41130             this.active.show();
41131             this.fireEvent("tabchange", this, this.active);
41132         }
41133         return tab;
41134     },
41135
41136     /**
41137      * Gets the active {@link Roo.TabPanelItem}.
41138      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41139      */
41140     getActiveTab : function(){
41141         return this.active;
41142     },
41143
41144     /**
41145      * Updates the tab body element to fit the height of the container element
41146      * for overflow scrolling
41147      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41148      */
41149     syncHeight : function(targetHeight){
41150         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41151         var bm = this.bodyEl.getMargins();
41152         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41153         this.bodyEl.setHeight(newHeight);
41154         return newHeight;
41155     },
41156
41157     onResize : function(){
41158         if(this.monitorResize){
41159             this.autoSizeTabs();
41160         }
41161     },
41162
41163     /**
41164      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41165      */
41166     beginUpdate : function(){
41167         this.updating = true;
41168     },
41169
41170     /**
41171      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41172      */
41173     endUpdate : function(){
41174         this.updating = false;
41175         this.autoSizeTabs();
41176     },
41177
41178     /**
41179      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41180      */
41181     autoSizeTabs : function()
41182     {
41183         var count = this.items.length;
41184         var vcount = count - this.hiddenCount;
41185         
41186         if (vcount < 2) {
41187             this.stripEl.hide();
41188         } else {
41189             this.stripEl.show();
41190         }
41191         
41192         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41193             return;
41194         }
41195         
41196         
41197         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41198         var availWidth = Math.floor(w / vcount);
41199         var b = this.stripBody;
41200         if(b.getWidth() > w){
41201             var tabs = this.items;
41202             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41203             if(availWidth < this.minTabWidth){
41204                 /*if(!this.sleft){    // incomplete scrolling code
41205                     this.createScrollButtons();
41206                 }
41207                 this.showScroll();
41208                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41209             }
41210         }else{
41211             if(this.currentTabWidth < this.preferredTabWidth){
41212                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41213             }
41214         }
41215     },
41216
41217     /**
41218      * Returns the number of tabs in this TabPanel.
41219      * @return {Number}
41220      */
41221      getCount : function(){
41222          return this.items.length;
41223      },
41224
41225     /**
41226      * Resizes all the tabs to the passed width
41227      * @param {Number} The new width
41228      */
41229     setTabWidth : function(width){
41230         this.currentTabWidth = width;
41231         for(var i = 0, len = this.items.length; i < len; i++) {
41232                 if(!this.items[i].isHidden()) {
41233                 this.items[i].setWidth(width);
41234             }
41235         }
41236     },
41237
41238     /**
41239      * Destroys this TabPanel
41240      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41241      */
41242     destroy : function(removeEl){
41243         Roo.EventManager.removeResizeListener(this.onResize, this);
41244         for(var i = 0, len = this.items.length; i < len; i++){
41245             this.items[i].purgeListeners();
41246         }
41247         if(removeEl === true){
41248             this.el.update("");
41249             this.el.remove();
41250         }
41251     },
41252     
41253     createStrip : function(container)
41254     {
41255         var strip = document.createElement("nav");
41256         strip.className = Roo.bootstrap.version == 4 ?
41257             "navbar-light bg-light" : 
41258             "navbar navbar-default"; //"x-tabs-wrap";
41259         container.appendChild(strip);
41260         return strip;
41261     },
41262     
41263     createStripList : function(strip)
41264     {
41265         // div wrapper for retard IE
41266         // returns the "tr" element.
41267         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41268         //'<div class="x-tabs-strip-wrap">'+
41269           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41270           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41271         return strip.firstChild; //.firstChild.firstChild.firstChild;
41272     },
41273     createBody : function(container)
41274     {
41275         var body = document.createElement("div");
41276         Roo.id(body, "tab-body");
41277         //Roo.fly(body).addClass("x-tabs-body");
41278         Roo.fly(body).addClass("tab-content");
41279         container.appendChild(body);
41280         return body;
41281     },
41282     createItemBody :function(bodyEl, id){
41283         var body = Roo.getDom(id);
41284         if(!body){
41285             body = document.createElement("div");
41286             body.id = id;
41287         }
41288         //Roo.fly(body).addClass("x-tabs-item-body");
41289         Roo.fly(body).addClass("tab-pane");
41290          bodyEl.insertBefore(body, bodyEl.firstChild);
41291         return body;
41292     },
41293     /** @private */
41294     createStripElements :  function(stripEl, text, closable, tpl)
41295     {
41296         var td = document.createElement("li"); // was td..
41297         td.className = 'nav-item';
41298         
41299         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41300         
41301         
41302         stripEl.appendChild(td);
41303         /*if(closable){
41304             td.className = "x-tabs-closable";
41305             if(!this.closeTpl){
41306                 this.closeTpl = new Roo.Template(
41307                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41308                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41309                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41310                 );
41311             }
41312             var el = this.closeTpl.overwrite(td, {"text": text});
41313             var close = el.getElementsByTagName("div")[0];
41314             var inner = el.getElementsByTagName("em")[0];
41315             return {"el": el, "close": close, "inner": inner};
41316         } else {
41317         */
41318         // not sure what this is..
41319 //            if(!this.tabTpl){
41320                 //this.tabTpl = new Roo.Template(
41321                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41322                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41323                 //);
41324 //                this.tabTpl = new Roo.Template(
41325 //                   '<a href="#">' +
41326 //                   '<span unselectable="on"' +
41327 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41328 //                            ' >{text}</span></a>'
41329 //                );
41330 //                
41331 //            }
41332
41333
41334             var template = tpl || this.tabTpl || false;
41335             
41336             if(!template){
41337                 template =  new Roo.Template(
41338                         Roo.bootstrap.version == 4 ? 
41339                             (
41340                                 '<a class="nav-link" href="#" unselectable="on"' +
41341                                      (this.disableTooltips ? '' : ' title="{text}"') +
41342                                      ' >{text}</a>'
41343                             ) : (
41344                                 '<a class="nav-link" href="#">' +
41345                                 '<span unselectable="on"' +
41346                                          (this.disableTooltips ? '' : ' title="{text}"') +
41347                                     ' >{text}</span></a>'
41348                             )
41349                 );
41350             }
41351             
41352             switch (typeof(template)) {
41353                 case 'object' :
41354                     break;
41355                 case 'string' :
41356                     template = new Roo.Template(template);
41357                     break;
41358                 default :
41359                     break;
41360             }
41361             
41362             var el = template.overwrite(td, {"text": text});
41363             
41364             var inner = el.getElementsByTagName("span")[0];
41365             
41366             return {"el": el, "inner": inner};
41367             
41368     }
41369         
41370     
41371 });
41372
41373 /**
41374  * @class Roo.TabPanelItem
41375  * @extends Roo.util.Observable
41376  * Represents an individual item (tab plus body) in a TabPanel.
41377  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41378  * @param {String} id The id of this TabPanelItem
41379  * @param {String} text The text for the tab of this TabPanelItem
41380  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41381  */
41382 Roo.bootstrap.panel.TabItem = function(config){
41383     /**
41384      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41385      * @type Roo.TabPanel
41386      */
41387     this.tabPanel = config.panel;
41388     /**
41389      * The id for this TabPanelItem
41390      * @type String
41391      */
41392     this.id = config.id;
41393     /** @private */
41394     this.disabled = false;
41395     /** @private */
41396     this.text = config.text;
41397     /** @private */
41398     this.loaded = false;
41399     this.closable = config.closable;
41400
41401     /**
41402      * The body element for this TabPanelItem.
41403      * @type Roo.Element
41404      */
41405     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41406     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41407     this.bodyEl.setStyle("display", "block");
41408     this.bodyEl.setStyle("zoom", "1");
41409     //this.hideAction();
41410
41411     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41412     /** @private */
41413     this.el = Roo.get(els.el);
41414     this.inner = Roo.get(els.inner, true);
41415      this.textEl = Roo.bootstrap.version == 4 ?
41416         this.el : Roo.get(this.el.dom.firstChild, true);
41417
41418     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41419     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41420
41421     
41422 //    this.el.on("mousedown", this.onTabMouseDown, this);
41423     this.el.on("click", this.onTabClick, this);
41424     /** @private */
41425     if(config.closable){
41426         var c = Roo.get(els.close, true);
41427         c.dom.title = this.closeText;
41428         c.addClassOnOver("close-over");
41429         c.on("click", this.closeClick, this);
41430      }
41431
41432     this.addEvents({
41433          /**
41434          * @event activate
41435          * Fires when this tab becomes the active tab.
41436          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41437          * @param {Roo.TabPanelItem} this
41438          */
41439         "activate": true,
41440         /**
41441          * @event beforeclose
41442          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41443          * @param {Roo.TabPanelItem} this
41444          * @param {Object} e Set cancel to true on this object to cancel the close.
41445          */
41446         "beforeclose": true,
41447         /**
41448          * @event close
41449          * Fires when this tab is closed.
41450          * @param {Roo.TabPanelItem} this
41451          */
41452          "close": true,
41453         /**
41454          * @event deactivate
41455          * Fires when this tab is no longer the active tab.
41456          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41457          * @param {Roo.TabPanelItem} this
41458          */
41459          "deactivate" : true
41460     });
41461     this.hidden = false;
41462
41463     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41464 };
41465
41466 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41467            {
41468     purgeListeners : function(){
41469        Roo.util.Observable.prototype.purgeListeners.call(this);
41470        this.el.removeAllListeners();
41471     },
41472     /**
41473      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41474      */
41475     show : function(){
41476         this.status_node.addClass("active");
41477         this.showAction();
41478         if(Roo.isOpera){
41479             this.tabPanel.stripWrap.repaint();
41480         }
41481         this.fireEvent("activate", this.tabPanel, this);
41482     },
41483
41484     /**
41485      * Returns true if this tab is the active tab.
41486      * @return {Boolean}
41487      */
41488     isActive : function(){
41489         return this.tabPanel.getActiveTab() == this;
41490     },
41491
41492     /**
41493      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41494      */
41495     hide : function(){
41496         this.status_node.removeClass("active");
41497         this.hideAction();
41498         this.fireEvent("deactivate", this.tabPanel, this);
41499     },
41500
41501     hideAction : function(){
41502         this.bodyEl.hide();
41503         this.bodyEl.setStyle("position", "absolute");
41504         this.bodyEl.setLeft("-20000px");
41505         this.bodyEl.setTop("-20000px");
41506     },
41507
41508     showAction : function(){
41509         this.bodyEl.setStyle("position", "relative");
41510         this.bodyEl.setTop("");
41511         this.bodyEl.setLeft("");
41512         this.bodyEl.show();
41513     },
41514
41515     /**
41516      * Set the tooltip for the tab.
41517      * @param {String} tooltip The tab's tooltip
41518      */
41519     setTooltip : function(text){
41520         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41521             this.textEl.dom.qtip = text;
41522             this.textEl.dom.removeAttribute('title');
41523         }else{
41524             this.textEl.dom.title = text;
41525         }
41526     },
41527
41528     onTabClick : function(e){
41529         e.preventDefault();
41530         this.tabPanel.activate(this.id);
41531     },
41532
41533     onTabMouseDown : function(e){
41534         e.preventDefault();
41535         this.tabPanel.activate(this.id);
41536     },
41537 /*
41538     getWidth : function(){
41539         return this.inner.getWidth();
41540     },
41541
41542     setWidth : function(width){
41543         var iwidth = width - this.linode.getPadding("lr");
41544         this.inner.setWidth(iwidth);
41545         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41546         this.linode.setWidth(width);
41547     },
41548 */
41549     /**
41550      * Show or hide the tab
41551      * @param {Boolean} hidden True to hide or false to show.
41552      */
41553     setHidden : function(hidden){
41554         this.hidden = hidden;
41555         this.linode.setStyle("display", hidden ? "none" : "");
41556     },
41557
41558     /**
41559      * Returns true if this tab is "hidden"
41560      * @return {Boolean}
41561      */
41562     isHidden : function(){
41563         return this.hidden;
41564     },
41565
41566     /**
41567      * Returns the text for this tab
41568      * @return {String}
41569      */
41570     getText : function(){
41571         return this.text;
41572     },
41573     /*
41574     autoSize : function(){
41575         //this.el.beginMeasure();
41576         this.textEl.setWidth(1);
41577         /*
41578          *  #2804 [new] Tabs in Roojs
41579          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41580          */
41581         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41582         //this.el.endMeasure();
41583     //},
41584
41585     /**
41586      * Sets the text for the tab (Note: this also sets the tooltip text)
41587      * @param {String} text The tab's text and tooltip
41588      */
41589     setText : function(text){
41590         this.text = text;
41591         this.textEl.update(text);
41592         this.setTooltip(text);
41593         //if(!this.tabPanel.resizeTabs){
41594         //    this.autoSize();
41595         //}
41596     },
41597     /**
41598      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41599      */
41600     activate : function(){
41601         this.tabPanel.activate(this.id);
41602     },
41603
41604     /**
41605      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41606      */
41607     disable : function(){
41608         if(this.tabPanel.active != this){
41609             this.disabled = true;
41610             this.status_node.addClass("disabled");
41611         }
41612     },
41613
41614     /**
41615      * Enables this TabPanelItem if it was previously disabled.
41616      */
41617     enable : function(){
41618         this.disabled = false;
41619         this.status_node.removeClass("disabled");
41620     },
41621
41622     /**
41623      * Sets the content for this TabPanelItem.
41624      * @param {String} content The content
41625      * @param {Boolean} loadScripts true to look for and load scripts
41626      */
41627     setContent : function(content, loadScripts){
41628         this.bodyEl.update(content, loadScripts);
41629     },
41630
41631     /**
41632      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41633      * @return {Roo.UpdateManager} The UpdateManager
41634      */
41635     getUpdateManager : function(){
41636         return this.bodyEl.getUpdateManager();
41637     },
41638
41639     /**
41640      * Set a URL to be used to load the content for this TabPanelItem.
41641      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41642      * @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)
41643      * @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)
41644      * @return {Roo.UpdateManager} The UpdateManager
41645      */
41646     setUrl : function(url, params, loadOnce){
41647         if(this.refreshDelegate){
41648             this.un('activate', this.refreshDelegate);
41649         }
41650         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41651         this.on("activate", this.refreshDelegate);
41652         return this.bodyEl.getUpdateManager();
41653     },
41654
41655     /** @private */
41656     _handleRefresh : function(url, params, loadOnce){
41657         if(!loadOnce || !this.loaded){
41658             var updater = this.bodyEl.getUpdateManager();
41659             updater.update(url, params, this._setLoaded.createDelegate(this));
41660         }
41661     },
41662
41663     /**
41664      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41665      *   Will fail silently if the setUrl method has not been called.
41666      *   This does not activate the panel, just updates its content.
41667      */
41668     refresh : function(){
41669         if(this.refreshDelegate){
41670            this.loaded = false;
41671            this.refreshDelegate();
41672         }
41673     },
41674
41675     /** @private */
41676     _setLoaded : function(){
41677         this.loaded = true;
41678     },
41679
41680     /** @private */
41681     closeClick : function(e){
41682         var o = {};
41683         e.stopEvent();
41684         this.fireEvent("beforeclose", this, o);
41685         if(o.cancel !== true){
41686             this.tabPanel.removeTab(this.id);
41687         }
41688     },
41689     /**
41690      * The text displayed in the tooltip for the close icon.
41691      * @type String
41692      */
41693     closeText : "Close this tab"
41694 });
41695 /**
41696 *    This script refer to:
41697 *    Title: International Telephone Input
41698 *    Author: Jack O'Connor
41699 *    Code version:  v12.1.12
41700 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41701 **/
41702
41703 Roo.bootstrap.PhoneInputData = function() {
41704     var d = [
41705       [
41706         "Afghanistan (‫افغانستان‬‎)",
41707         "af",
41708         "93"
41709       ],
41710       [
41711         "Albania (Shqipëri)",
41712         "al",
41713         "355"
41714       ],
41715       [
41716         "Algeria (‫الجزائر‬‎)",
41717         "dz",
41718         "213"
41719       ],
41720       [
41721         "American Samoa",
41722         "as",
41723         "1684"
41724       ],
41725       [
41726         "Andorra",
41727         "ad",
41728         "376"
41729       ],
41730       [
41731         "Angola",
41732         "ao",
41733         "244"
41734       ],
41735       [
41736         "Anguilla",
41737         "ai",
41738         "1264"
41739       ],
41740       [
41741         "Antigua and Barbuda",
41742         "ag",
41743         "1268"
41744       ],
41745       [
41746         "Argentina",
41747         "ar",
41748         "54"
41749       ],
41750       [
41751         "Armenia (Հայաստան)",
41752         "am",
41753         "374"
41754       ],
41755       [
41756         "Aruba",
41757         "aw",
41758         "297"
41759       ],
41760       [
41761         "Australia",
41762         "au",
41763         "61",
41764         0
41765       ],
41766       [
41767         "Austria (Österreich)",
41768         "at",
41769         "43"
41770       ],
41771       [
41772         "Azerbaijan (Azərbaycan)",
41773         "az",
41774         "994"
41775       ],
41776       [
41777         "Bahamas",
41778         "bs",
41779         "1242"
41780       ],
41781       [
41782         "Bahrain (‫البحرين‬‎)",
41783         "bh",
41784         "973"
41785       ],
41786       [
41787         "Bangladesh (বাংলাদেশ)",
41788         "bd",
41789         "880"
41790       ],
41791       [
41792         "Barbados",
41793         "bb",
41794         "1246"
41795       ],
41796       [
41797         "Belarus (Беларусь)",
41798         "by",
41799         "375"
41800       ],
41801       [
41802         "Belgium (België)",
41803         "be",
41804         "32"
41805       ],
41806       [
41807         "Belize",
41808         "bz",
41809         "501"
41810       ],
41811       [
41812         "Benin (Bénin)",
41813         "bj",
41814         "229"
41815       ],
41816       [
41817         "Bermuda",
41818         "bm",
41819         "1441"
41820       ],
41821       [
41822         "Bhutan (འབྲུག)",
41823         "bt",
41824         "975"
41825       ],
41826       [
41827         "Bolivia",
41828         "bo",
41829         "591"
41830       ],
41831       [
41832         "Bosnia and Herzegovina (Босна и Херцеговина)",
41833         "ba",
41834         "387"
41835       ],
41836       [
41837         "Botswana",
41838         "bw",
41839         "267"
41840       ],
41841       [
41842         "Brazil (Brasil)",
41843         "br",
41844         "55"
41845       ],
41846       [
41847         "British Indian Ocean Territory",
41848         "io",
41849         "246"
41850       ],
41851       [
41852         "British Virgin Islands",
41853         "vg",
41854         "1284"
41855       ],
41856       [
41857         "Brunei",
41858         "bn",
41859         "673"
41860       ],
41861       [
41862         "Bulgaria (България)",
41863         "bg",
41864         "359"
41865       ],
41866       [
41867         "Burkina Faso",
41868         "bf",
41869         "226"
41870       ],
41871       [
41872         "Burundi (Uburundi)",
41873         "bi",
41874         "257"
41875       ],
41876       [
41877         "Cambodia (កម្ពុជា)",
41878         "kh",
41879         "855"
41880       ],
41881       [
41882         "Cameroon (Cameroun)",
41883         "cm",
41884         "237"
41885       ],
41886       [
41887         "Canada",
41888         "ca",
41889         "1",
41890         1,
41891         ["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"]
41892       ],
41893       [
41894         "Cape Verde (Kabu Verdi)",
41895         "cv",
41896         "238"
41897       ],
41898       [
41899         "Caribbean Netherlands",
41900         "bq",
41901         "599",
41902         1
41903       ],
41904       [
41905         "Cayman Islands",
41906         "ky",
41907         "1345"
41908       ],
41909       [
41910         "Central African Republic (République centrafricaine)",
41911         "cf",
41912         "236"
41913       ],
41914       [
41915         "Chad (Tchad)",
41916         "td",
41917         "235"
41918       ],
41919       [
41920         "Chile",
41921         "cl",
41922         "56"
41923       ],
41924       [
41925         "China (中国)",
41926         "cn",
41927         "86"
41928       ],
41929       [
41930         "Christmas Island",
41931         "cx",
41932         "61",
41933         2
41934       ],
41935       [
41936         "Cocos (Keeling) Islands",
41937         "cc",
41938         "61",
41939         1
41940       ],
41941       [
41942         "Colombia",
41943         "co",
41944         "57"
41945       ],
41946       [
41947         "Comoros (‫جزر القمر‬‎)",
41948         "km",
41949         "269"
41950       ],
41951       [
41952         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41953         "cd",
41954         "243"
41955       ],
41956       [
41957         "Congo (Republic) (Congo-Brazzaville)",
41958         "cg",
41959         "242"
41960       ],
41961       [
41962         "Cook Islands",
41963         "ck",
41964         "682"
41965       ],
41966       [
41967         "Costa Rica",
41968         "cr",
41969         "506"
41970       ],
41971       [
41972         "Côte d’Ivoire",
41973         "ci",
41974         "225"
41975       ],
41976       [
41977         "Croatia (Hrvatska)",
41978         "hr",
41979         "385"
41980       ],
41981       [
41982         "Cuba",
41983         "cu",
41984         "53"
41985       ],
41986       [
41987         "Curaçao",
41988         "cw",
41989         "599",
41990         0
41991       ],
41992       [
41993         "Cyprus (Κύπρος)",
41994         "cy",
41995         "357"
41996       ],
41997       [
41998         "Czech Republic (Česká republika)",
41999         "cz",
42000         "420"
42001       ],
42002       [
42003         "Denmark (Danmark)",
42004         "dk",
42005         "45"
42006       ],
42007       [
42008         "Djibouti",
42009         "dj",
42010         "253"
42011       ],
42012       [
42013         "Dominica",
42014         "dm",
42015         "1767"
42016       ],
42017       [
42018         "Dominican Republic (República Dominicana)",
42019         "do",
42020         "1",
42021         2,
42022         ["809", "829", "849"]
42023       ],
42024       [
42025         "Ecuador",
42026         "ec",
42027         "593"
42028       ],
42029       [
42030         "Egypt (‫مصر‬‎)",
42031         "eg",
42032         "20"
42033       ],
42034       [
42035         "El Salvador",
42036         "sv",
42037         "503"
42038       ],
42039       [
42040         "Equatorial Guinea (Guinea Ecuatorial)",
42041         "gq",
42042         "240"
42043       ],
42044       [
42045         "Eritrea",
42046         "er",
42047         "291"
42048       ],
42049       [
42050         "Estonia (Eesti)",
42051         "ee",
42052         "372"
42053       ],
42054       [
42055         "Ethiopia",
42056         "et",
42057         "251"
42058       ],
42059       [
42060         "Falkland Islands (Islas Malvinas)",
42061         "fk",
42062         "500"
42063       ],
42064       [
42065         "Faroe Islands (Føroyar)",
42066         "fo",
42067         "298"
42068       ],
42069       [
42070         "Fiji",
42071         "fj",
42072         "679"
42073       ],
42074       [
42075         "Finland (Suomi)",
42076         "fi",
42077         "358",
42078         0
42079       ],
42080       [
42081         "France",
42082         "fr",
42083         "33"
42084       ],
42085       [
42086         "French Guiana (Guyane française)",
42087         "gf",
42088         "594"
42089       ],
42090       [
42091         "French Polynesia (Polynésie française)",
42092         "pf",
42093         "689"
42094       ],
42095       [
42096         "Gabon",
42097         "ga",
42098         "241"
42099       ],
42100       [
42101         "Gambia",
42102         "gm",
42103         "220"
42104       ],
42105       [
42106         "Georgia (საქართველო)",
42107         "ge",
42108         "995"
42109       ],
42110       [
42111         "Germany (Deutschland)",
42112         "de",
42113         "49"
42114       ],
42115       [
42116         "Ghana (Gaana)",
42117         "gh",
42118         "233"
42119       ],
42120       [
42121         "Gibraltar",
42122         "gi",
42123         "350"
42124       ],
42125       [
42126         "Greece (Ελλάδα)",
42127         "gr",
42128         "30"
42129       ],
42130       [
42131         "Greenland (Kalaallit Nunaat)",
42132         "gl",
42133         "299"
42134       ],
42135       [
42136         "Grenada",
42137         "gd",
42138         "1473"
42139       ],
42140       [
42141         "Guadeloupe",
42142         "gp",
42143         "590",
42144         0
42145       ],
42146       [
42147         "Guam",
42148         "gu",
42149         "1671"
42150       ],
42151       [
42152         "Guatemala",
42153         "gt",
42154         "502"
42155       ],
42156       [
42157         "Guernsey",
42158         "gg",
42159         "44",
42160         1
42161       ],
42162       [
42163         "Guinea (Guinée)",
42164         "gn",
42165         "224"
42166       ],
42167       [
42168         "Guinea-Bissau (Guiné Bissau)",
42169         "gw",
42170         "245"
42171       ],
42172       [
42173         "Guyana",
42174         "gy",
42175         "592"
42176       ],
42177       [
42178         "Haiti",
42179         "ht",
42180         "509"
42181       ],
42182       [
42183         "Honduras",
42184         "hn",
42185         "504"
42186       ],
42187       [
42188         "Hong Kong (香港)",
42189         "hk",
42190         "852"
42191       ],
42192       [
42193         "Hungary (Magyarország)",
42194         "hu",
42195         "36"
42196       ],
42197       [
42198         "Iceland (Ísland)",
42199         "is",
42200         "354"
42201       ],
42202       [
42203         "India (भारत)",
42204         "in",
42205         "91"
42206       ],
42207       [
42208         "Indonesia",
42209         "id",
42210         "62"
42211       ],
42212       [
42213         "Iran (‫ایران‬‎)",
42214         "ir",
42215         "98"
42216       ],
42217       [
42218         "Iraq (‫العراق‬‎)",
42219         "iq",
42220         "964"
42221       ],
42222       [
42223         "Ireland",
42224         "ie",
42225         "353"
42226       ],
42227       [
42228         "Isle of Man",
42229         "im",
42230         "44",
42231         2
42232       ],
42233       [
42234         "Israel (‫ישראל‬‎)",
42235         "il",
42236         "972"
42237       ],
42238       [
42239         "Italy (Italia)",
42240         "it",
42241         "39",
42242         0
42243       ],
42244       [
42245         "Jamaica",
42246         "jm",
42247         "1876"
42248       ],
42249       [
42250         "Japan (日本)",
42251         "jp",
42252         "81"
42253       ],
42254       [
42255         "Jersey",
42256         "je",
42257         "44",
42258         3
42259       ],
42260       [
42261         "Jordan (‫الأردن‬‎)",
42262         "jo",
42263         "962"
42264       ],
42265       [
42266         "Kazakhstan (Казахстан)",
42267         "kz",
42268         "7",
42269         1
42270       ],
42271       [
42272         "Kenya",
42273         "ke",
42274         "254"
42275       ],
42276       [
42277         "Kiribati",
42278         "ki",
42279         "686"
42280       ],
42281       [
42282         "Kosovo",
42283         "xk",
42284         "383"
42285       ],
42286       [
42287         "Kuwait (‫الكويت‬‎)",
42288         "kw",
42289         "965"
42290       ],
42291       [
42292         "Kyrgyzstan (Кыргызстан)",
42293         "kg",
42294         "996"
42295       ],
42296       [
42297         "Laos (ລາວ)",
42298         "la",
42299         "856"
42300       ],
42301       [
42302         "Latvia (Latvija)",
42303         "lv",
42304         "371"
42305       ],
42306       [
42307         "Lebanon (‫لبنان‬‎)",
42308         "lb",
42309         "961"
42310       ],
42311       [
42312         "Lesotho",
42313         "ls",
42314         "266"
42315       ],
42316       [
42317         "Liberia",
42318         "lr",
42319         "231"
42320       ],
42321       [
42322         "Libya (‫ليبيا‬‎)",
42323         "ly",
42324         "218"
42325       ],
42326       [
42327         "Liechtenstein",
42328         "li",
42329         "423"
42330       ],
42331       [
42332         "Lithuania (Lietuva)",
42333         "lt",
42334         "370"
42335       ],
42336       [
42337         "Luxembourg",
42338         "lu",
42339         "352"
42340       ],
42341       [
42342         "Macau (澳門)",
42343         "mo",
42344         "853"
42345       ],
42346       [
42347         "Macedonia (FYROM) (Македонија)",
42348         "mk",
42349         "389"
42350       ],
42351       [
42352         "Madagascar (Madagasikara)",
42353         "mg",
42354         "261"
42355       ],
42356       [
42357         "Malawi",
42358         "mw",
42359         "265"
42360       ],
42361       [
42362         "Malaysia",
42363         "my",
42364         "60"
42365       ],
42366       [
42367         "Maldives",
42368         "mv",
42369         "960"
42370       ],
42371       [
42372         "Mali",
42373         "ml",
42374         "223"
42375       ],
42376       [
42377         "Malta",
42378         "mt",
42379         "356"
42380       ],
42381       [
42382         "Marshall Islands",
42383         "mh",
42384         "692"
42385       ],
42386       [
42387         "Martinique",
42388         "mq",
42389         "596"
42390       ],
42391       [
42392         "Mauritania (‫موريتانيا‬‎)",
42393         "mr",
42394         "222"
42395       ],
42396       [
42397         "Mauritius (Moris)",
42398         "mu",
42399         "230"
42400       ],
42401       [
42402         "Mayotte",
42403         "yt",
42404         "262",
42405         1
42406       ],
42407       [
42408         "Mexico (México)",
42409         "mx",
42410         "52"
42411       ],
42412       [
42413         "Micronesia",
42414         "fm",
42415         "691"
42416       ],
42417       [
42418         "Moldova (Republica Moldova)",
42419         "md",
42420         "373"
42421       ],
42422       [
42423         "Monaco",
42424         "mc",
42425         "377"
42426       ],
42427       [
42428         "Mongolia (Монгол)",
42429         "mn",
42430         "976"
42431       ],
42432       [
42433         "Montenegro (Crna Gora)",
42434         "me",
42435         "382"
42436       ],
42437       [
42438         "Montserrat",
42439         "ms",
42440         "1664"
42441       ],
42442       [
42443         "Morocco (‫المغرب‬‎)",
42444         "ma",
42445         "212",
42446         0
42447       ],
42448       [
42449         "Mozambique (Moçambique)",
42450         "mz",
42451         "258"
42452       ],
42453       [
42454         "Myanmar (Burma) (မြန်မာ)",
42455         "mm",
42456         "95"
42457       ],
42458       [
42459         "Namibia (Namibië)",
42460         "na",
42461         "264"
42462       ],
42463       [
42464         "Nauru",
42465         "nr",
42466         "674"
42467       ],
42468       [
42469         "Nepal (नेपाल)",
42470         "np",
42471         "977"
42472       ],
42473       [
42474         "Netherlands (Nederland)",
42475         "nl",
42476         "31"
42477       ],
42478       [
42479         "New Caledonia (Nouvelle-Calédonie)",
42480         "nc",
42481         "687"
42482       ],
42483       [
42484         "New Zealand",
42485         "nz",
42486         "64"
42487       ],
42488       [
42489         "Nicaragua",
42490         "ni",
42491         "505"
42492       ],
42493       [
42494         "Niger (Nijar)",
42495         "ne",
42496         "227"
42497       ],
42498       [
42499         "Nigeria",
42500         "ng",
42501         "234"
42502       ],
42503       [
42504         "Niue",
42505         "nu",
42506         "683"
42507       ],
42508       [
42509         "Norfolk Island",
42510         "nf",
42511         "672"
42512       ],
42513       [
42514         "North Korea (조선 민주주의 인민 공화국)",
42515         "kp",
42516         "850"
42517       ],
42518       [
42519         "Northern Mariana Islands",
42520         "mp",
42521         "1670"
42522       ],
42523       [
42524         "Norway (Norge)",
42525         "no",
42526         "47",
42527         0
42528       ],
42529       [
42530         "Oman (‫عُمان‬‎)",
42531         "om",
42532         "968"
42533       ],
42534       [
42535         "Pakistan (‫پاکستان‬‎)",
42536         "pk",
42537         "92"
42538       ],
42539       [
42540         "Palau",
42541         "pw",
42542         "680"
42543       ],
42544       [
42545         "Palestine (‫فلسطين‬‎)",
42546         "ps",
42547         "970"
42548       ],
42549       [
42550         "Panama (Panamá)",
42551         "pa",
42552         "507"
42553       ],
42554       [
42555         "Papua New Guinea",
42556         "pg",
42557         "675"
42558       ],
42559       [
42560         "Paraguay",
42561         "py",
42562         "595"
42563       ],
42564       [
42565         "Peru (Perú)",
42566         "pe",
42567         "51"
42568       ],
42569       [
42570         "Philippines",
42571         "ph",
42572         "63"
42573       ],
42574       [
42575         "Poland (Polska)",
42576         "pl",
42577         "48"
42578       ],
42579       [
42580         "Portugal",
42581         "pt",
42582         "351"
42583       ],
42584       [
42585         "Puerto Rico",
42586         "pr",
42587         "1",
42588         3,
42589         ["787", "939"]
42590       ],
42591       [
42592         "Qatar (‫قطر‬‎)",
42593         "qa",
42594         "974"
42595       ],
42596       [
42597         "Réunion (La Réunion)",
42598         "re",
42599         "262",
42600         0
42601       ],
42602       [
42603         "Romania (România)",
42604         "ro",
42605         "40"
42606       ],
42607       [
42608         "Russia (Россия)",
42609         "ru",
42610         "7",
42611         0
42612       ],
42613       [
42614         "Rwanda",
42615         "rw",
42616         "250"
42617       ],
42618       [
42619         "Saint Barthélemy",
42620         "bl",
42621         "590",
42622         1
42623       ],
42624       [
42625         "Saint Helena",
42626         "sh",
42627         "290"
42628       ],
42629       [
42630         "Saint Kitts and Nevis",
42631         "kn",
42632         "1869"
42633       ],
42634       [
42635         "Saint Lucia",
42636         "lc",
42637         "1758"
42638       ],
42639       [
42640         "Saint Martin (Saint-Martin (partie française))",
42641         "mf",
42642         "590",
42643         2
42644       ],
42645       [
42646         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42647         "pm",
42648         "508"
42649       ],
42650       [
42651         "Saint Vincent and the Grenadines",
42652         "vc",
42653         "1784"
42654       ],
42655       [
42656         "Samoa",
42657         "ws",
42658         "685"
42659       ],
42660       [
42661         "San Marino",
42662         "sm",
42663         "378"
42664       ],
42665       [
42666         "São Tomé and Príncipe (São Tomé e Príncipe)",
42667         "st",
42668         "239"
42669       ],
42670       [
42671         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42672         "sa",
42673         "966"
42674       ],
42675       [
42676         "Senegal (Sénégal)",
42677         "sn",
42678         "221"
42679       ],
42680       [
42681         "Serbia (Србија)",
42682         "rs",
42683         "381"
42684       ],
42685       [
42686         "Seychelles",
42687         "sc",
42688         "248"
42689       ],
42690       [
42691         "Sierra Leone",
42692         "sl",
42693         "232"
42694       ],
42695       [
42696         "Singapore",
42697         "sg",
42698         "65"
42699       ],
42700       [
42701         "Sint Maarten",
42702         "sx",
42703         "1721"
42704       ],
42705       [
42706         "Slovakia (Slovensko)",
42707         "sk",
42708         "421"
42709       ],
42710       [
42711         "Slovenia (Slovenija)",
42712         "si",
42713         "386"
42714       ],
42715       [
42716         "Solomon Islands",
42717         "sb",
42718         "677"
42719       ],
42720       [
42721         "Somalia (Soomaaliya)",
42722         "so",
42723         "252"
42724       ],
42725       [
42726         "South Africa",
42727         "za",
42728         "27"
42729       ],
42730       [
42731         "South Korea (대한민국)",
42732         "kr",
42733         "82"
42734       ],
42735       [
42736         "South Sudan (‫جنوب السودان‬‎)",
42737         "ss",
42738         "211"
42739       ],
42740       [
42741         "Spain (España)",
42742         "es",
42743         "34"
42744       ],
42745       [
42746         "Sri Lanka (ශ්‍රී ලංකාව)",
42747         "lk",
42748         "94"
42749       ],
42750       [
42751         "Sudan (‫السودان‬‎)",
42752         "sd",
42753         "249"
42754       ],
42755       [
42756         "Suriname",
42757         "sr",
42758         "597"
42759       ],
42760       [
42761         "Svalbard and Jan Mayen",
42762         "sj",
42763         "47",
42764         1
42765       ],
42766       [
42767         "Swaziland",
42768         "sz",
42769         "268"
42770       ],
42771       [
42772         "Sweden (Sverige)",
42773         "se",
42774         "46"
42775       ],
42776       [
42777         "Switzerland (Schweiz)",
42778         "ch",
42779         "41"
42780       ],
42781       [
42782         "Syria (‫سوريا‬‎)",
42783         "sy",
42784         "963"
42785       ],
42786       [
42787         "Taiwan (台灣)",
42788         "tw",
42789         "886"
42790       ],
42791       [
42792         "Tajikistan",
42793         "tj",
42794         "992"
42795       ],
42796       [
42797         "Tanzania",
42798         "tz",
42799         "255"
42800       ],
42801       [
42802         "Thailand (ไทย)",
42803         "th",
42804         "66"
42805       ],
42806       [
42807         "Timor-Leste",
42808         "tl",
42809         "670"
42810       ],
42811       [
42812         "Togo",
42813         "tg",
42814         "228"
42815       ],
42816       [
42817         "Tokelau",
42818         "tk",
42819         "690"
42820       ],
42821       [
42822         "Tonga",
42823         "to",
42824         "676"
42825       ],
42826       [
42827         "Trinidad and Tobago",
42828         "tt",
42829         "1868"
42830       ],
42831       [
42832         "Tunisia (‫تونس‬‎)",
42833         "tn",
42834         "216"
42835       ],
42836       [
42837         "Turkey (Türkiye)",
42838         "tr",
42839         "90"
42840       ],
42841       [
42842         "Turkmenistan",
42843         "tm",
42844         "993"
42845       ],
42846       [
42847         "Turks and Caicos Islands",
42848         "tc",
42849         "1649"
42850       ],
42851       [
42852         "Tuvalu",
42853         "tv",
42854         "688"
42855       ],
42856       [
42857         "U.S. Virgin Islands",
42858         "vi",
42859         "1340"
42860       ],
42861       [
42862         "Uganda",
42863         "ug",
42864         "256"
42865       ],
42866       [
42867         "Ukraine (Україна)",
42868         "ua",
42869         "380"
42870       ],
42871       [
42872         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42873         "ae",
42874         "971"
42875       ],
42876       [
42877         "United Kingdom",
42878         "gb",
42879         "44",
42880         0
42881       ],
42882       [
42883         "United States",
42884         "us",
42885         "1",
42886         0
42887       ],
42888       [
42889         "Uruguay",
42890         "uy",
42891         "598"
42892       ],
42893       [
42894         "Uzbekistan (Oʻzbekiston)",
42895         "uz",
42896         "998"
42897       ],
42898       [
42899         "Vanuatu",
42900         "vu",
42901         "678"
42902       ],
42903       [
42904         "Vatican City (Città del Vaticano)",
42905         "va",
42906         "39",
42907         1
42908       ],
42909       [
42910         "Venezuela",
42911         "ve",
42912         "58"
42913       ],
42914       [
42915         "Vietnam (Việt Nam)",
42916         "vn",
42917         "84"
42918       ],
42919       [
42920         "Wallis and Futuna (Wallis-et-Futuna)",
42921         "wf",
42922         "681"
42923       ],
42924       [
42925         "Western Sahara (‫الصحراء الغربية‬‎)",
42926         "eh",
42927         "212",
42928         1
42929       ],
42930       [
42931         "Yemen (‫اليمن‬‎)",
42932         "ye",
42933         "967"
42934       ],
42935       [
42936         "Zambia",
42937         "zm",
42938         "260"
42939       ],
42940       [
42941         "Zimbabwe",
42942         "zw",
42943         "263"
42944       ],
42945       [
42946         "Åland Islands",
42947         "ax",
42948         "358",
42949         1
42950       ]
42951   ];
42952   
42953   return d;
42954 }/**
42955 *    This script refer to:
42956 *    Title: International Telephone Input
42957 *    Author: Jack O'Connor
42958 *    Code version:  v12.1.12
42959 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42960 **/
42961
42962 /**
42963  * @class Roo.bootstrap.PhoneInput
42964  * @extends Roo.bootstrap.TriggerField
42965  * An input with International dial-code selection
42966  
42967  * @cfg {String} defaultDialCode default '+852'
42968  * @cfg {Array} preferedCountries default []
42969   
42970  * @constructor
42971  * Create a new PhoneInput.
42972  * @param {Object} config Configuration options
42973  */
42974
42975 Roo.bootstrap.PhoneInput = function(config) {
42976     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42977 };
42978
42979 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42980         
42981         listWidth: undefined,
42982         
42983         selectedClass: 'active',
42984         
42985         invalidClass : "has-warning",
42986         
42987         validClass: 'has-success',
42988         
42989         allowed: '0123456789',
42990         
42991         max_length: 15,
42992         
42993         /**
42994          * @cfg {String} defaultDialCode The default dial code when initializing the input
42995          */
42996         defaultDialCode: '+852',
42997         
42998         /**
42999          * @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
43000          */
43001         preferedCountries: false,
43002         
43003         getAutoCreate : function()
43004         {
43005             var data = Roo.bootstrap.PhoneInputData();
43006             var align = this.labelAlign || this.parentLabelAlign();
43007             var id = Roo.id();
43008             
43009             this.allCountries = [];
43010             this.dialCodeMapping = [];
43011             
43012             for (var i = 0; i < data.length; i++) {
43013               var c = data[i];
43014               this.allCountries[i] = {
43015                 name: c[0],
43016                 iso2: c[1],
43017                 dialCode: c[2],
43018                 priority: c[3] || 0,
43019                 areaCodes: c[4] || null
43020               };
43021               this.dialCodeMapping[c[2]] = {
43022                   name: c[0],
43023                   iso2: c[1],
43024                   priority: c[3] || 0,
43025                   areaCodes: c[4] || null
43026               };
43027             }
43028             
43029             var cfg = {
43030                 cls: 'form-group',
43031                 cn: []
43032             };
43033             
43034             var input =  {
43035                 tag: 'input',
43036                 id : id,
43037                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43038                 maxlength: this.max_length,
43039                 cls : 'form-control tel-input',
43040                 autocomplete: 'new-password'
43041             };
43042             
43043             var hiddenInput = {
43044                 tag: 'input',
43045                 type: 'hidden',
43046                 cls: 'hidden-tel-input'
43047             };
43048             
43049             if (this.name) {
43050                 hiddenInput.name = this.name;
43051             }
43052             
43053             if (this.disabled) {
43054                 input.disabled = true;
43055             }
43056             
43057             var flag_container = {
43058                 tag: 'div',
43059                 cls: 'flag-box',
43060                 cn: [
43061                     {
43062                         tag: 'div',
43063                         cls: 'flag'
43064                     },
43065                     {
43066                         tag: 'div',
43067                         cls: 'caret'
43068                     }
43069                 ]
43070             };
43071             
43072             var box = {
43073                 tag: 'div',
43074                 cls: this.hasFeedback ? 'has-feedback' : '',
43075                 cn: [
43076                     hiddenInput,
43077                     input,
43078                     {
43079                         tag: 'input',
43080                         cls: 'dial-code-holder',
43081                         disabled: true
43082                     }
43083                 ]
43084             };
43085             
43086             var container = {
43087                 cls: 'roo-select2-container input-group',
43088                 cn: [
43089                     flag_container,
43090                     box
43091                 ]
43092             };
43093             
43094             if (this.fieldLabel.length) {
43095                 var indicator = {
43096                     tag: 'i',
43097                     tooltip: 'This field is required'
43098                 };
43099                 
43100                 var label = {
43101                     tag: 'label',
43102                     'for':  id,
43103                     cls: 'control-label',
43104                     cn: []
43105                 };
43106                 
43107                 var label_text = {
43108                     tag: 'span',
43109                     html: this.fieldLabel
43110                 };
43111                 
43112                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43113                 label.cn = [
43114                     indicator,
43115                     label_text
43116                 ];
43117                 
43118                 if(this.indicatorpos == 'right') {
43119                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43120                     label.cn = [
43121                         label_text,
43122                         indicator
43123                     ];
43124                 }
43125                 
43126                 if(align == 'left') {
43127                     container = {
43128                         tag: 'div',
43129                         cn: [
43130                             container
43131                         ]
43132                     };
43133                     
43134                     if(this.labelWidth > 12){
43135                         label.style = "width: " + this.labelWidth + 'px';
43136                     }
43137                     if(this.labelWidth < 13 && this.labelmd == 0){
43138                         this.labelmd = this.labelWidth;
43139                     }
43140                     if(this.labellg > 0){
43141                         label.cls += ' col-lg-' + this.labellg;
43142                         input.cls += ' col-lg-' + (12 - this.labellg);
43143                     }
43144                     if(this.labelmd > 0){
43145                         label.cls += ' col-md-' + this.labelmd;
43146                         container.cls += ' col-md-' + (12 - this.labelmd);
43147                     }
43148                     if(this.labelsm > 0){
43149                         label.cls += ' col-sm-' + this.labelsm;
43150                         container.cls += ' col-sm-' + (12 - this.labelsm);
43151                     }
43152                     if(this.labelxs > 0){
43153                         label.cls += ' col-xs-' + this.labelxs;
43154                         container.cls += ' col-xs-' + (12 - this.labelxs);
43155                     }
43156                 }
43157             }
43158             
43159             cfg.cn = [
43160                 label,
43161                 container
43162             ];
43163             
43164             var settings = this;
43165             
43166             ['xs','sm','md','lg'].map(function(size){
43167                 if (settings[size]) {
43168                     cfg.cls += ' col-' + size + '-' + settings[size];
43169                 }
43170             });
43171             
43172             this.store = new Roo.data.Store({
43173                 proxy : new Roo.data.MemoryProxy({}),
43174                 reader : new Roo.data.JsonReader({
43175                     fields : [
43176                         {
43177                             'name' : 'name',
43178                             'type' : 'string'
43179                         },
43180                         {
43181                             'name' : 'iso2',
43182                             'type' : 'string'
43183                         },
43184                         {
43185                             'name' : 'dialCode',
43186                             'type' : 'string'
43187                         },
43188                         {
43189                             'name' : 'priority',
43190                             'type' : 'string'
43191                         },
43192                         {
43193                             'name' : 'areaCodes',
43194                             'type' : 'string'
43195                         }
43196                     ]
43197                 })
43198             });
43199             
43200             if(!this.preferedCountries) {
43201                 this.preferedCountries = [
43202                     'hk',
43203                     'gb',
43204                     'us'
43205                 ];
43206             }
43207             
43208             var p = this.preferedCountries.reverse();
43209             
43210             if(p) {
43211                 for (var i = 0; i < p.length; i++) {
43212                     for (var j = 0; j < this.allCountries.length; j++) {
43213                         if(this.allCountries[j].iso2 == p[i]) {
43214                             var t = this.allCountries[j];
43215                             this.allCountries.splice(j,1);
43216                             this.allCountries.unshift(t);
43217                         }
43218                     } 
43219                 }
43220             }
43221             
43222             this.store.proxy.data = {
43223                 success: true,
43224                 data: this.allCountries
43225             };
43226             
43227             return cfg;
43228         },
43229         
43230         initEvents : function()
43231         {
43232             this.createList();
43233             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43234             
43235             this.indicator = this.indicatorEl();
43236             this.flag = this.flagEl();
43237             this.dialCodeHolder = this.dialCodeHolderEl();
43238             
43239             this.trigger = this.el.select('div.flag-box',true).first();
43240             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43241             
43242             var _this = this;
43243             
43244             (function(){
43245                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43246                 _this.list.setWidth(lw);
43247             }).defer(100);
43248             
43249             this.list.on('mouseover', this.onViewOver, this);
43250             this.list.on('mousemove', this.onViewMove, this);
43251             this.inputEl().on("keyup", this.onKeyUp, this);
43252             this.inputEl().on("keypress", this.onKeyPress, this);
43253             
43254             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43255
43256             this.view = new Roo.View(this.list, this.tpl, {
43257                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43258             });
43259             
43260             this.view.on('click', this.onViewClick, this);
43261             this.setValue(this.defaultDialCode);
43262         },
43263         
43264         onTriggerClick : function(e)
43265         {
43266             Roo.log('trigger click');
43267             if(this.disabled){
43268                 return;
43269             }
43270             
43271             if(this.isExpanded()){
43272                 this.collapse();
43273                 this.hasFocus = false;
43274             }else {
43275                 this.store.load({});
43276                 this.hasFocus = true;
43277                 this.expand();
43278             }
43279         },
43280         
43281         isExpanded : function()
43282         {
43283             return this.list.isVisible();
43284         },
43285         
43286         collapse : function()
43287         {
43288             if(!this.isExpanded()){
43289                 return;
43290             }
43291             this.list.hide();
43292             Roo.get(document).un('mousedown', this.collapseIf, this);
43293             Roo.get(document).un('mousewheel', this.collapseIf, this);
43294             this.fireEvent('collapse', this);
43295             this.validate();
43296         },
43297         
43298         expand : function()
43299         {
43300             Roo.log('expand');
43301
43302             if(this.isExpanded() || !this.hasFocus){
43303                 return;
43304             }
43305             
43306             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43307             this.list.setWidth(lw);
43308             
43309             this.list.show();
43310             this.restrictHeight();
43311             
43312             Roo.get(document).on('mousedown', this.collapseIf, this);
43313             Roo.get(document).on('mousewheel', this.collapseIf, this);
43314             
43315             this.fireEvent('expand', this);
43316         },
43317         
43318         restrictHeight : function()
43319         {
43320             this.list.alignTo(this.inputEl(), this.listAlign);
43321             this.list.alignTo(this.inputEl(), this.listAlign);
43322         },
43323         
43324         onViewOver : function(e, t)
43325         {
43326             if(this.inKeyMode){
43327                 return;
43328             }
43329             var item = this.view.findItemFromChild(t);
43330             
43331             if(item){
43332                 var index = this.view.indexOf(item);
43333                 this.select(index, false);
43334             }
43335         },
43336
43337         // private
43338         onViewClick : function(view, doFocus, el, e)
43339         {
43340             var index = this.view.getSelectedIndexes()[0];
43341             
43342             var r = this.store.getAt(index);
43343             
43344             if(r){
43345                 this.onSelect(r, index);
43346             }
43347             if(doFocus !== false && !this.blockFocus){
43348                 this.inputEl().focus();
43349             }
43350         },
43351         
43352         onViewMove : function(e, t)
43353         {
43354             this.inKeyMode = false;
43355         },
43356         
43357         select : function(index, scrollIntoView)
43358         {
43359             this.selectedIndex = index;
43360             this.view.select(index);
43361             if(scrollIntoView !== false){
43362                 var el = this.view.getNode(index);
43363                 if(el){
43364                     this.list.scrollChildIntoView(el, false);
43365                 }
43366             }
43367         },
43368         
43369         createList : function()
43370         {
43371             this.list = Roo.get(document.body).createChild({
43372                 tag: 'ul',
43373                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43374                 style: 'display:none'
43375             });
43376             
43377             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43378         },
43379         
43380         collapseIf : function(e)
43381         {
43382             var in_combo  = e.within(this.el);
43383             var in_list =  e.within(this.list);
43384             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43385             
43386             if (in_combo || in_list || is_list) {
43387                 return;
43388             }
43389             this.collapse();
43390         },
43391         
43392         onSelect : function(record, index)
43393         {
43394             if(this.fireEvent('beforeselect', this, record, index) !== false){
43395                 
43396                 this.setFlagClass(record.data.iso2);
43397                 this.setDialCode(record.data.dialCode);
43398                 this.hasFocus = false;
43399                 this.collapse();
43400                 this.fireEvent('select', this, record, index);
43401             }
43402         },
43403         
43404         flagEl : function()
43405         {
43406             var flag = this.el.select('div.flag',true).first();
43407             if(!flag){
43408                 return false;
43409             }
43410             return flag;
43411         },
43412         
43413         dialCodeHolderEl : function()
43414         {
43415             var d = this.el.select('input.dial-code-holder',true).first();
43416             if(!d){
43417                 return false;
43418             }
43419             return d;
43420         },
43421         
43422         setDialCode : function(v)
43423         {
43424             this.dialCodeHolder.dom.value = '+'+v;
43425         },
43426         
43427         setFlagClass : function(n)
43428         {
43429             this.flag.dom.className = 'flag '+n;
43430         },
43431         
43432         getValue : function()
43433         {
43434             var v = this.inputEl().getValue();
43435             if(this.dialCodeHolder) {
43436                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43437             }
43438             return v;
43439         },
43440         
43441         setValue : function(v)
43442         {
43443             var d = this.getDialCode(v);
43444             
43445             //invalid dial code
43446             if(v.length == 0 || !d || d.length == 0) {
43447                 if(this.rendered){
43448                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43449                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43450                 }
43451                 return;
43452             }
43453             
43454             //valid dial code
43455             this.setFlagClass(this.dialCodeMapping[d].iso2);
43456             this.setDialCode(d);
43457             this.inputEl().dom.value = v.replace('+'+d,'');
43458             this.hiddenEl().dom.value = this.getValue();
43459             
43460             this.validate();
43461         },
43462         
43463         getDialCode : function(v)
43464         {
43465             v = v ||  '';
43466             
43467             if (v.length == 0) {
43468                 return this.dialCodeHolder.dom.value;
43469             }
43470             
43471             var dialCode = "";
43472             if (v.charAt(0) != "+") {
43473                 return false;
43474             }
43475             var numericChars = "";
43476             for (var i = 1; i < v.length; i++) {
43477               var c = v.charAt(i);
43478               if (!isNaN(c)) {
43479                 numericChars += c;
43480                 if (this.dialCodeMapping[numericChars]) {
43481                   dialCode = v.substr(1, i);
43482                 }
43483                 if (numericChars.length == 4) {
43484                   break;
43485                 }
43486               }
43487             }
43488             return dialCode;
43489         },
43490         
43491         reset : function()
43492         {
43493             this.setValue(this.defaultDialCode);
43494             this.validate();
43495         },
43496         
43497         hiddenEl : function()
43498         {
43499             return this.el.select('input.hidden-tel-input',true).first();
43500         },
43501         
43502         // after setting val
43503         onKeyUp : function(e){
43504             this.setValue(this.getValue());
43505         },
43506         
43507         onKeyPress : function(e){
43508             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43509                 e.stopEvent();
43510             }
43511         }
43512         
43513 });
43514 /**
43515  * @class Roo.bootstrap.MoneyField
43516  * @extends Roo.bootstrap.ComboBox
43517  * Bootstrap MoneyField class
43518  * 
43519  * @constructor
43520  * Create a new MoneyField.
43521  * @param {Object} config Configuration options
43522  */
43523
43524 Roo.bootstrap.MoneyField = function(config) {
43525     
43526     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43527     
43528 };
43529
43530 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43531     
43532     /**
43533      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43534      */
43535     allowDecimals : true,
43536     /**
43537      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43538      */
43539     decimalSeparator : ".",
43540     /**
43541      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43542      */
43543     decimalPrecision : 0,
43544     /**
43545      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43546      */
43547     allowNegative : true,
43548     /**
43549      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43550      */
43551     allowZero: true,
43552     /**
43553      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43554      */
43555     minValue : Number.NEGATIVE_INFINITY,
43556     /**
43557      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43558      */
43559     maxValue : Number.MAX_VALUE,
43560     /**
43561      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43562      */
43563     minText : "The minimum value for this field is {0}",
43564     /**
43565      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43566      */
43567     maxText : "The maximum value for this field is {0}",
43568     /**
43569      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43570      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43571      */
43572     nanText : "{0} is not a valid number",
43573     /**
43574      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43575      */
43576     castInt : true,
43577     /**
43578      * @cfg {String} defaults currency of the MoneyField
43579      * value should be in lkey
43580      */
43581     defaultCurrency : false,
43582     /**
43583      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43584      */
43585     thousandsDelimiter : false,
43586     /**
43587      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43588      */
43589     max_length: false,
43590     
43591     inputlg : 9,
43592     inputmd : 9,
43593     inputsm : 9,
43594     inputxs : 6,
43595     
43596     store : false,
43597     
43598     getAutoCreate : function()
43599     {
43600         var align = this.labelAlign || this.parentLabelAlign();
43601         
43602         var id = Roo.id();
43603
43604         var cfg = {
43605             cls: 'form-group',
43606             cn: []
43607         };
43608
43609         var input =  {
43610             tag: 'input',
43611             id : id,
43612             cls : 'form-control roo-money-amount-input',
43613             autocomplete: 'new-password'
43614         };
43615         
43616         var hiddenInput = {
43617             tag: 'input',
43618             type: 'hidden',
43619             id: Roo.id(),
43620             cls: 'hidden-number-input'
43621         };
43622         
43623         if(this.max_length) {
43624             input.maxlength = this.max_length; 
43625         }
43626         
43627         if (this.name) {
43628             hiddenInput.name = this.name;
43629         }
43630
43631         if (this.disabled) {
43632             input.disabled = true;
43633         }
43634
43635         var clg = 12 - this.inputlg;
43636         var cmd = 12 - this.inputmd;
43637         var csm = 12 - this.inputsm;
43638         var cxs = 12 - this.inputxs;
43639         
43640         var container = {
43641             tag : 'div',
43642             cls : 'row roo-money-field',
43643             cn : [
43644                 {
43645                     tag : 'div',
43646                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43647                     cn : [
43648                         {
43649                             tag : 'div',
43650                             cls: 'roo-select2-container input-group',
43651                             cn: [
43652                                 {
43653                                     tag : 'input',
43654                                     cls : 'form-control roo-money-currency-input',
43655                                     autocomplete: 'new-password',
43656                                     readOnly : 1,
43657                                     name : this.currencyName
43658                                 },
43659                                 {
43660                                     tag :'span',
43661                                     cls : 'input-group-addon',
43662                                     cn : [
43663                                         {
43664                                             tag: 'span',
43665                                             cls: 'caret'
43666                                         }
43667                                     ]
43668                                 }
43669                             ]
43670                         }
43671                     ]
43672                 },
43673                 {
43674                     tag : 'div',
43675                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43676                     cn : [
43677                         {
43678                             tag: 'div',
43679                             cls: this.hasFeedback ? 'has-feedback' : '',
43680                             cn: [
43681                                 input
43682                             ]
43683                         }
43684                     ]
43685                 }
43686             ]
43687             
43688         };
43689         
43690         if (this.fieldLabel.length) {
43691             var indicator = {
43692                 tag: 'i',
43693                 tooltip: 'This field is required'
43694             };
43695
43696             var label = {
43697                 tag: 'label',
43698                 'for':  id,
43699                 cls: 'control-label',
43700                 cn: []
43701             };
43702
43703             var label_text = {
43704                 tag: 'span',
43705                 html: this.fieldLabel
43706             };
43707
43708             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43709             label.cn = [
43710                 indicator,
43711                 label_text
43712             ];
43713
43714             if(this.indicatorpos == 'right') {
43715                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43716                 label.cn = [
43717                     label_text,
43718                     indicator
43719                 ];
43720             }
43721
43722             if(align == 'left') {
43723                 container = {
43724                     tag: 'div',
43725                     cn: [
43726                         container
43727                     ]
43728                 };
43729
43730                 if(this.labelWidth > 12){
43731                     label.style = "width: " + this.labelWidth + 'px';
43732                 }
43733                 if(this.labelWidth < 13 && this.labelmd == 0){
43734                     this.labelmd = this.labelWidth;
43735                 }
43736                 if(this.labellg > 0){
43737                     label.cls += ' col-lg-' + this.labellg;
43738                     input.cls += ' col-lg-' + (12 - this.labellg);
43739                 }
43740                 if(this.labelmd > 0){
43741                     label.cls += ' col-md-' + this.labelmd;
43742                     container.cls += ' col-md-' + (12 - this.labelmd);
43743                 }
43744                 if(this.labelsm > 0){
43745                     label.cls += ' col-sm-' + this.labelsm;
43746                     container.cls += ' col-sm-' + (12 - this.labelsm);
43747                 }
43748                 if(this.labelxs > 0){
43749                     label.cls += ' col-xs-' + this.labelxs;
43750                     container.cls += ' col-xs-' + (12 - this.labelxs);
43751                 }
43752             }
43753         }
43754
43755         cfg.cn = [
43756             label,
43757             container,
43758             hiddenInput
43759         ];
43760         
43761         var settings = this;
43762
43763         ['xs','sm','md','lg'].map(function(size){
43764             if (settings[size]) {
43765                 cfg.cls += ' col-' + size + '-' + settings[size];
43766             }
43767         });
43768         
43769         return cfg;
43770     },
43771     
43772     initEvents : function()
43773     {
43774         this.indicator = this.indicatorEl();
43775         
43776         this.initCurrencyEvent();
43777         
43778         this.initNumberEvent();
43779     },
43780     
43781     initCurrencyEvent : function()
43782     {
43783         if (!this.store) {
43784             throw "can not find store for combo";
43785         }
43786         
43787         this.store = Roo.factory(this.store, Roo.data);
43788         this.store.parent = this;
43789         
43790         this.createList();
43791         
43792         this.triggerEl = this.el.select('.input-group-addon', true).first();
43793         
43794         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43795         
43796         var _this = this;
43797         
43798         (function(){
43799             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43800             _this.list.setWidth(lw);
43801         }).defer(100);
43802         
43803         this.list.on('mouseover', this.onViewOver, this);
43804         this.list.on('mousemove', this.onViewMove, this);
43805         this.list.on('scroll', this.onViewScroll, this);
43806         
43807         if(!this.tpl){
43808             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43809         }
43810         
43811         this.view = new Roo.View(this.list, this.tpl, {
43812             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43813         });
43814         
43815         this.view.on('click', this.onViewClick, this);
43816         
43817         this.store.on('beforeload', this.onBeforeLoad, this);
43818         this.store.on('load', this.onLoad, this);
43819         this.store.on('loadexception', this.onLoadException, this);
43820         
43821         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43822             "up" : function(e){
43823                 this.inKeyMode = true;
43824                 this.selectPrev();
43825             },
43826
43827             "down" : function(e){
43828                 if(!this.isExpanded()){
43829                     this.onTriggerClick();
43830                 }else{
43831                     this.inKeyMode = true;
43832                     this.selectNext();
43833                 }
43834             },
43835
43836             "enter" : function(e){
43837                 this.collapse();
43838                 
43839                 if(this.fireEvent("specialkey", this, e)){
43840                     this.onViewClick(false);
43841                 }
43842                 
43843                 return true;
43844             },
43845
43846             "esc" : function(e){
43847                 this.collapse();
43848             },
43849
43850             "tab" : function(e){
43851                 this.collapse();
43852                 
43853                 if(this.fireEvent("specialkey", this, e)){
43854                     this.onViewClick(false);
43855                 }
43856                 
43857                 return true;
43858             },
43859
43860             scope : this,
43861
43862             doRelay : function(foo, bar, hname){
43863                 if(hname == 'down' || this.scope.isExpanded()){
43864                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43865                 }
43866                 return true;
43867             },
43868
43869             forceKeyDown: true
43870         });
43871         
43872         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43873         
43874     },
43875     
43876     initNumberEvent : function(e)
43877     {
43878         this.inputEl().on("keydown" , this.fireKey,  this);
43879         this.inputEl().on("focus", this.onFocus,  this);
43880         this.inputEl().on("blur", this.onBlur,  this);
43881         
43882         this.inputEl().relayEvent('keyup', this);
43883         
43884         if(this.indicator){
43885             this.indicator.addClass('invisible');
43886         }
43887  
43888         this.originalValue = this.getValue();
43889         
43890         if(this.validationEvent == 'keyup'){
43891             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43892             this.inputEl().on('keyup', this.filterValidation, this);
43893         }
43894         else if(this.validationEvent !== false){
43895             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43896         }
43897         
43898         if(this.selectOnFocus){
43899             this.on("focus", this.preFocus, this);
43900             
43901         }
43902         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43903             this.inputEl().on("keypress", this.filterKeys, this);
43904         } else {
43905             this.inputEl().relayEvent('keypress', this);
43906         }
43907         
43908         var allowed = "0123456789";
43909         
43910         if(this.allowDecimals){
43911             allowed += this.decimalSeparator;
43912         }
43913         
43914         if(this.allowNegative){
43915             allowed += "-";
43916         }
43917         
43918         if(this.thousandsDelimiter) {
43919             allowed += ",";
43920         }
43921         
43922         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43923         
43924         var keyPress = function(e){
43925             
43926             var k = e.getKey();
43927             
43928             var c = e.getCharCode();
43929             
43930             if(
43931                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43932                     allowed.indexOf(String.fromCharCode(c)) === -1
43933             ){
43934                 e.stopEvent();
43935                 return;
43936             }
43937             
43938             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43939                 return;
43940             }
43941             
43942             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43943                 e.stopEvent();
43944             }
43945         };
43946         
43947         this.inputEl().on("keypress", keyPress, this);
43948         
43949     },
43950     
43951     onTriggerClick : function(e)
43952     {   
43953         if(this.disabled){
43954             return;
43955         }
43956         
43957         this.page = 0;
43958         this.loadNext = false;
43959         
43960         if(this.isExpanded()){
43961             this.collapse();
43962             return;
43963         }
43964         
43965         this.hasFocus = true;
43966         
43967         if(this.triggerAction == 'all') {
43968             this.doQuery(this.allQuery, true);
43969             return;
43970         }
43971         
43972         this.doQuery(this.getRawValue());
43973     },
43974     
43975     getCurrency : function()
43976     {   
43977         var v = this.currencyEl().getValue();
43978         
43979         return v;
43980     },
43981     
43982     restrictHeight : function()
43983     {
43984         this.list.alignTo(this.currencyEl(), this.listAlign);
43985         this.list.alignTo(this.currencyEl(), this.listAlign);
43986     },
43987     
43988     onViewClick : function(view, doFocus, el, e)
43989     {
43990         var index = this.view.getSelectedIndexes()[0];
43991         
43992         var r = this.store.getAt(index);
43993         
43994         if(r){
43995             this.onSelect(r, index);
43996         }
43997     },
43998     
43999     onSelect : function(record, index){
44000         
44001         if(this.fireEvent('beforeselect', this, record, index) !== false){
44002         
44003             this.setFromCurrencyData(index > -1 ? record.data : false);
44004             
44005             this.collapse();
44006             
44007             this.fireEvent('select', this, record, index);
44008         }
44009     },
44010     
44011     setFromCurrencyData : function(o)
44012     {
44013         var currency = '';
44014         
44015         this.lastCurrency = o;
44016         
44017         if (this.currencyField) {
44018             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44019         } else {
44020             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44021         }
44022         
44023         this.lastSelectionText = currency;
44024         
44025         //setting default currency
44026         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44027             this.setCurrency(this.defaultCurrency);
44028             return;
44029         }
44030         
44031         this.setCurrency(currency);
44032     },
44033     
44034     setFromData : function(o)
44035     {
44036         var c = {};
44037         
44038         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44039         
44040         this.setFromCurrencyData(c);
44041         
44042         var value = '';
44043         
44044         if (this.name) {
44045             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44046         } else {
44047             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44048         }
44049         
44050         this.setValue(value);
44051         
44052     },
44053     
44054     setCurrency : function(v)
44055     {   
44056         this.currencyValue = v;
44057         
44058         if(this.rendered){
44059             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44060             this.validate();
44061         }
44062     },
44063     
44064     setValue : function(v)
44065     {
44066         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44067         
44068         this.value = v;
44069         
44070         if(this.rendered){
44071             
44072             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44073             
44074             this.inputEl().dom.value = (v == '') ? '' :
44075                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44076             
44077             if(!this.allowZero && v === '0') {
44078                 this.hiddenEl().dom.value = '';
44079                 this.inputEl().dom.value = '';
44080             }
44081             
44082             this.validate();
44083         }
44084     },
44085     
44086     getRawValue : function()
44087     {
44088         var v = this.inputEl().getValue();
44089         
44090         return v;
44091     },
44092     
44093     getValue : function()
44094     {
44095         return this.fixPrecision(this.parseValue(this.getRawValue()));
44096     },
44097     
44098     parseValue : function(value)
44099     {
44100         if(this.thousandsDelimiter) {
44101             value += "";
44102             r = new RegExp(",", "g");
44103             value = value.replace(r, "");
44104         }
44105         
44106         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44107         return isNaN(value) ? '' : value;
44108         
44109     },
44110     
44111     fixPrecision : function(value)
44112     {
44113         if(this.thousandsDelimiter) {
44114             value += "";
44115             r = new RegExp(",", "g");
44116             value = value.replace(r, "");
44117         }
44118         
44119         var nan = isNaN(value);
44120         
44121         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44122             return nan ? '' : value;
44123         }
44124         return parseFloat(value).toFixed(this.decimalPrecision);
44125     },
44126     
44127     decimalPrecisionFcn : function(v)
44128     {
44129         return Math.floor(v);
44130     },
44131     
44132     validateValue : function(value)
44133     {
44134         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44135             return false;
44136         }
44137         
44138         var num = this.parseValue(value);
44139         
44140         if(isNaN(num)){
44141             this.markInvalid(String.format(this.nanText, value));
44142             return false;
44143         }
44144         
44145         if(num < this.minValue){
44146             this.markInvalid(String.format(this.minText, this.minValue));
44147             return false;
44148         }
44149         
44150         if(num > this.maxValue){
44151             this.markInvalid(String.format(this.maxText, this.maxValue));
44152             return false;
44153         }
44154         
44155         return true;
44156     },
44157     
44158     validate : function()
44159     {
44160         if(this.disabled || this.allowBlank){
44161             this.markValid();
44162             return true;
44163         }
44164         
44165         var currency = this.getCurrency();
44166         
44167         if(this.validateValue(this.getRawValue()) && currency.length){
44168             this.markValid();
44169             return true;
44170         }
44171         
44172         this.markInvalid();
44173         return false;
44174     },
44175     
44176     getName: function()
44177     {
44178         return this.name;
44179     },
44180     
44181     beforeBlur : function()
44182     {
44183         if(!this.castInt){
44184             return;
44185         }
44186         
44187         var v = this.parseValue(this.getRawValue());
44188         
44189         if(v || v == 0){
44190             this.setValue(v);
44191         }
44192     },
44193     
44194     onBlur : function()
44195     {
44196         this.beforeBlur();
44197         
44198         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44199             //this.el.removeClass(this.focusClass);
44200         }
44201         
44202         this.hasFocus = false;
44203         
44204         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44205             this.validate();
44206         }
44207         
44208         var v = this.getValue();
44209         
44210         if(String(v) !== String(this.startValue)){
44211             this.fireEvent('change', this, v, this.startValue);
44212         }
44213         
44214         this.fireEvent("blur", this);
44215     },
44216     
44217     inputEl : function()
44218     {
44219         return this.el.select('.roo-money-amount-input', true).first();
44220     },
44221     
44222     currencyEl : function()
44223     {
44224         return this.el.select('.roo-money-currency-input', true).first();
44225     },
44226     
44227     hiddenEl : function()
44228     {
44229         return this.el.select('input.hidden-number-input',true).first();
44230     }
44231     
44232 });/**
44233  * @class Roo.bootstrap.BezierSignature
44234  * @extends Roo.bootstrap.Component
44235  * Bootstrap BezierSignature class
44236  * This script refer to:
44237  *    Title: Signature Pad
44238  *    Author: szimek
44239  *    Availability: https://github.com/szimek/signature_pad
44240  *
44241  * @constructor
44242  * Create a new BezierSignature
44243  * @param {Object} config The config object
44244  */
44245
44246 Roo.bootstrap.BezierSignature = function(config){
44247     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44248     this.addEvents({
44249         "resize" : true
44250     });
44251 };
44252
44253 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44254 {
44255      
44256     curve_data: [],
44257     
44258     is_empty: true,
44259     
44260     mouse_btn_down: true,
44261     
44262     /**
44263      * @cfg {int} canvas height
44264      */
44265     canvas_height: '200px',
44266     
44267     /**
44268      * @cfg {float|function} Radius of a single dot.
44269      */ 
44270     dot_size: false,
44271     
44272     /**
44273      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44274      */
44275     min_width: 0.5,
44276     
44277     /**
44278      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44279      */
44280     max_width: 2.5,
44281     
44282     /**
44283      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44284      */
44285     throttle: 16,
44286     
44287     /**
44288      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44289      */
44290     min_distance: 5,
44291     
44292     /**
44293      * @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.
44294      */
44295     bg_color: 'rgba(0, 0, 0, 0)',
44296     
44297     /**
44298      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44299      */
44300     dot_color: 'black',
44301     
44302     /**
44303      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44304      */ 
44305     velocity_filter_weight: 0.7,
44306     
44307     /**
44308      * @cfg {function} Callback when stroke begin. 
44309      */
44310     onBegin: false,
44311     
44312     /**
44313      * @cfg {function} Callback when stroke end.
44314      */
44315     onEnd: false,
44316     
44317     getAutoCreate : function()
44318     {
44319         var cls = 'roo-signature column';
44320         
44321         if(this.cls){
44322             cls += ' ' + this.cls;
44323         }
44324         
44325         var col_sizes = [
44326             'lg',
44327             'md',
44328             'sm',
44329             'xs'
44330         ];
44331         
44332         for(var i = 0; i < col_sizes.length; i++) {
44333             if(this[col_sizes[i]]) {
44334                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44335             }
44336         }
44337         
44338         var cfg = {
44339             tag: 'div',
44340             cls: cls,
44341             cn: [
44342                 {
44343                     tag: 'div',
44344                     cls: 'roo-signature-body',
44345                     cn: [
44346                         {
44347                             tag: 'canvas',
44348                             cls: 'roo-signature-body-canvas',
44349                             height: this.canvas_height,
44350                             width: this.canvas_width
44351                         }
44352                     ]
44353                 },
44354                 {
44355                     tag: 'input',
44356                     type: 'file',
44357                     style: 'display: none'
44358                 }
44359             ]
44360         };
44361         
44362         return cfg;
44363     },
44364     
44365     initEvents: function() 
44366     {
44367         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44368         
44369         var canvas = this.canvasEl();
44370         
44371         // mouse && touch event swapping...
44372         canvas.dom.style.touchAction = 'none';
44373         canvas.dom.style.msTouchAction = 'none';
44374         
44375         this.mouse_btn_down = false;
44376         canvas.on('mousedown', this._handleMouseDown, this);
44377         canvas.on('mousemove', this._handleMouseMove, this);
44378         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44379         
44380         if (window.PointerEvent) {
44381             canvas.on('pointerdown', this._handleMouseDown, this);
44382             canvas.on('pointermove', this._handleMouseMove, this);
44383             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44384         }
44385         
44386         if ('ontouchstart' in window) {
44387             canvas.on('touchstart', this._handleTouchStart, this);
44388             canvas.on('touchmove', this._handleTouchMove, this);
44389             canvas.on('touchend', this._handleTouchEnd, this);
44390         }
44391         
44392         Roo.EventManager.onWindowResize(this.resize, this, true);
44393         
44394         // file input event
44395         this.fileEl().on('change', this.uploadImage, this);
44396         
44397         this.clear();
44398         
44399         this.resize();
44400     },
44401     
44402     resize: function(){
44403         
44404         var canvas = this.canvasEl().dom;
44405         var ctx = this.canvasElCtx();
44406         var img_data = false;
44407         
44408         if(canvas.width > 0) {
44409             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44410         }
44411         // setting canvas width will clean img data
44412         canvas.width = 0;
44413         
44414         var style = window.getComputedStyle ? 
44415             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44416             
44417         var padding_left = parseInt(style.paddingLeft) || 0;
44418         var padding_right = parseInt(style.paddingRight) || 0;
44419         
44420         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44421         
44422         if(img_data) {
44423             ctx.putImageData(img_data, 0, 0);
44424         }
44425     },
44426     
44427     _handleMouseDown: function(e)
44428     {
44429         if (e.browserEvent.which === 1) {
44430             this.mouse_btn_down = true;
44431             this.strokeBegin(e);
44432         }
44433     },
44434     
44435     _handleMouseMove: function (e)
44436     {
44437         if (this.mouse_btn_down) {
44438             this.strokeMoveUpdate(e);
44439         }
44440     },
44441     
44442     _handleMouseUp: function (e)
44443     {
44444         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44445             this.mouse_btn_down = false;
44446             this.strokeEnd(e);
44447         }
44448     },
44449     
44450     _handleTouchStart: function (e) {
44451         
44452         e.preventDefault();
44453         if (e.browserEvent.targetTouches.length === 1) {
44454             // var touch = e.browserEvent.changedTouches[0];
44455             // this.strokeBegin(touch);
44456             
44457              this.strokeBegin(e); // assume e catching the correct xy...
44458         }
44459     },
44460     
44461     _handleTouchMove: function (e) {
44462         e.preventDefault();
44463         // var touch = event.targetTouches[0];
44464         // _this._strokeMoveUpdate(touch);
44465         this.strokeMoveUpdate(e);
44466     },
44467     
44468     _handleTouchEnd: function (e) {
44469         var wasCanvasTouched = e.target === this.canvasEl().dom;
44470         if (wasCanvasTouched) {
44471             e.preventDefault();
44472             // var touch = event.changedTouches[0];
44473             // _this._strokeEnd(touch);
44474             this.strokeEnd(e);
44475         }
44476     },
44477     
44478     reset: function () {
44479         this._lastPoints = [];
44480         this._lastVelocity = 0;
44481         this._lastWidth = (this.min_width + this.max_width) / 2;
44482         this.canvasElCtx().fillStyle = this.dot_color;
44483     },
44484     
44485     strokeMoveUpdate: function(e)
44486     {
44487         this.strokeUpdate(e);
44488         
44489         if (this.throttle) {
44490             this.throttleStroke(this.strokeUpdate, this.throttle);
44491         }
44492         else {
44493             this.strokeUpdate(e);
44494         }
44495     },
44496     
44497     strokeBegin: function(e)
44498     {
44499         var newPointGroup = {
44500             color: this.dot_color,
44501             points: []
44502         };
44503         
44504         if (typeof this.onBegin === 'function') {
44505             this.onBegin(e);
44506         }
44507         
44508         this.curve_data.push(newPointGroup);
44509         this.reset();
44510         this.strokeUpdate(e);
44511     },
44512     
44513     strokeUpdate: function(e)
44514     {
44515         var rect = this.canvasEl().dom.getBoundingClientRect();
44516         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44517         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44518         var lastPoints = lastPointGroup.points;
44519         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44520         var isLastPointTooClose = lastPoint
44521             ? point.distanceTo(lastPoint) <= this.min_distance
44522             : false;
44523         var color = lastPointGroup.color;
44524         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44525             var curve = this.addPoint(point);
44526             if (!lastPoint) {
44527                 this.drawDot({color: color, point: point});
44528             }
44529             else if (curve) {
44530                 this.drawCurve({color: color, curve: curve});
44531             }
44532             lastPoints.push({
44533                 time: point.time,
44534                 x: point.x,
44535                 y: point.y
44536             });
44537         }
44538     },
44539     
44540     strokeEnd: function(e)
44541     {
44542         this.strokeUpdate(e);
44543         if (typeof this.onEnd === 'function') {
44544             this.onEnd(e);
44545         }
44546     },
44547     
44548     addPoint:  function (point) {
44549         var _lastPoints = this._lastPoints;
44550         _lastPoints.push(point);
44551         if (_lastPoints.length > 2) {
44552             if (_lastPoints.length === 3) {
44553                 _lastPoints.unshift(_lastPoints[0]);
44554             }
44555             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44556             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44557             _lastPoints.shift();
44558             return curve;
44559         }
44560         return null;
44561     },
44562     
44563     calculateCurveWidths: function (startPoint, endPoint) {
44564         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44565             (1 - this.velocity_filter_weight) * this._lastVelocity;
44566
44567         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44568         var widths = {
44569             end: newWidth,
44570             start: this._lastWidth
44571         };
44572         
44573         this._lastVelocity = velocity;
44574         this._lastWidth = newWidth;
44575         return widths;
44576     },
44577     
44578     drawDot: function (_a) {
44579         var color = _a.color, point = _a.point;
44580         var ctx = this.canvasElCtx();
44581         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44582         ctx.beginPath();
44583         this.drawCurveSegment(point.x, point.y, width);
44584         ctx.closePath();
44585         ctx.fillStyle = color;
44586         ctx.fill();
44587     },
44588     
44589     drawCurve: function (_a) {
44590         var color = _a.color, curve = _a.curve;
44591         var ctx = this.canvasElCtx();
44592         var widthDelta = curve.endWidth - curve.startWidth;
44593         var drawSteps = Math.floor(curve.length()) * 2;
44594         ctx.beginPath();
44595         ctx.fillStyle = color;
44596         for (var i = 0; i < drawSteps; i += 1) {
44597         var t = i / drawSteps;
44598         var tt = t * t;
44599         var ttt = tt * t;
44600         var u = 1 - t;
44601         var uu = u * u;
44602         var uuu = uu * u;
44603         var x = uuu * curve.startPoint.x;
44604         x += 3 * uu * t * curve.control1.x;
44605         x += 3 * u * tt * curve.control2.x;
44606         x += ttt * curve.endPoint.x;
44607         var y = uuu * curve.startPoint.y;
44608         y += 3 * uu * t * curve.control1.y;
44609         y += 3 * u * tt * curve.control2.y;
44610         y += ttt * curve.endPoint.y;
44611         var width = curve.startWidth + ttt * widthDelta;
44612         this.drawCurveSegment(x, y, width);
44613         }
44614         ctx.closePath();
44615         ctx.fill();
44616     },
44617     
44618     drawCurveSegment: function (x, y, width) {
44619         var ctx = this.canvasElCtx();
44620         ctx.moveTo(x, y);
44621         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44622         this.is_empty = false;
44623     },
44624     
44625     clear: function()
44626     {
44627         var ctx = this.canvasElCtx();
44628         var canvas = this.canvasEl().dom;
44629         ctx.fillStyle = this.bg_color;
44630         ctx.clearRect(0, 0, canvas.width, canvas.height);
44631         ctx.fillRect(0, 0, canvas.width, canvas.height);
44632         this.curve_data = [];
44633         this.reset();
44634         this.is_empty = true;
44635     },
44636     
44637     fileEl: function()
44638     {
44639         return  this.el.select('input',true).first();
44640     },
44641     
44642     canvasEl: function()
44643     {
44644         return this.el.select('canvas',true).first();
44645     },
44646     
44647     canvasElCtx: function()
44648     {
44649         return this.el.select('canvas',true).first().dom.getContext('2d');
44650     },
44651     
44652     getImage: function(type)
44653     {
44654         if(this.is_empty) {
44655             return false;
44656         }
44657         
44658         // encryption ?
44659         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44660     },
44661     
44662     drawFromImage: function(img_src)
44663     {
44664         var img = new Image();
44665         
44666         img.onload = function(){
44667             this.canvasElCtx().drawImage(img, 0, 0);
44668         }.bind(this);
44669         
44670         img.src = img_src;
44671         
44672         this.is_empty = false;
44673     },
44674     
44675     selectImage: function()
44676     {
44677         this.fileEl().dom.click();
44678     },
44679     
44680     uploadImage: function(e)
44681     {
44682         var reader = new FileReader();
44683         
44684         reader.onload = function(e){
44685             var img = new Image();
44686             img.onload = function(){
44687                 this.reset();
44688                 this.canvasElCtx().drawImage(img, 0, 0);
44689             }.bind(this);
44690             img.src = e.target.result;
44691         }.bind(this);
44692         
44693         reader.readAsDataURL(e.target.files[0]);
44694     },
44695     
44696     // Bezier Point Constructor
44697     Point: (function () {
44698         function Point(x, y, time) {
44699             this.x = x;
44700             this.y = y;
44701             this.time = time || Date.now();
44702         }
44703         Point.prototype.distanceTo = function (start) {
44704             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44705         };
44706         Point.prototype.equals = function (other) {
44707             return this.x === other.x && this.y === other.y && this.time === other.time;
44708         };
44709         Point.prototype.velocityFrom = function (start) {
44710             return this.time !== start.time
44711             ? this.distanceTo(start) / (this.time - start.time)
44712             : 0;
44713         };
44714         return Point;
44715     }()),
44716     
44717     
44718     // Bezier Constructor
44719     Bezier: (function () {
44720         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44721             this.startPoint = startPoint;
44722             this.control2 = control2;
44723             this.control1 = control1;
44724             this.endPoint = endPoint;
44725             this.startWidth = startWidth;
44726             this.endWidth = endWidth;
44727         }
44728         Bezier.fromPoints = function (points, widths, scope) {
44729             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44730             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44731             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44732         };
44733         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44734             var dx1 = s1.x - s2.x;
44735             var dy1 = s1.y - s2.y;
44736             var dx2 = s2.x - s3.x;
44737             var dy2 = s2.y - s3.y;
44738             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44739             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44740             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44741             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44742             var dxm = m1.x - m2.x;
44743             var dym = m1.y - m2.y;
44744             var k = l2 / (l1 + l2);
44745             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44746             var tx = s2.x - cm.x;
44747             var ty = s2.y - cm.y;
44748             return {
44749                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44750                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44751             };
44752         };
44753         Bezier.prototype.length = function () {
44754             var steps = 10;
44755             var length = 0;
44756             var px;
44757             var py;
44758             for (var i = 0; i <= steps; i += 1) {
44759                 var t = i / steps;
44760                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44761                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44762                 if (i > 0) {
44763                     var xdiff = cx - px;
44764                     var ydiff = cy - py;
44765                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44766                 }
44767                 px = cx;
44768                 py = cy;
44769             }
44770             return length;
44771         };
44772         Bezier.prototype.point = function (t, start, c1, c2, end) {
44773             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44774             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44775             + (3.0 * c2 * (1.0 - t) * t * t)
44776             + (end * t * t * t);
44777         };
44778         return Bezier;
44779     }()),
44780     
44781     throttleStroke: function(fn, wait) {
44782       if (wait === void 0) { wait = 250; }
44783       var previous = 0;
44784       var timeout = null;
44785       var result;
44786       var storedContext;
44787       var storedArgs;
44788       var later = function () {
44789           previous = Date.now();
44790           timeout = null;
44791           result = fn.apply(storedContext, storedArgs);
44792           if (!timeout) {
44793               storedContext = null;
44794               storedArgs = [];
44795           }
44796       };
44797       return function wrapper() {
44798           var args = [];
44799           for (var _i = 0; _i < arguments.length; _i++) {
44800               args[_i] = arguments[_i];
44801           }
44802           var now = Date.now();
44803           var remaining = wait - (now - previous);
44804           storedContext = this;
44805           storedArgs = args;
44806           if (remaining <= 0 || remaining > wait) {
44807               if (timeout) {
44808                   clearTimeout(timeout);
44809                   timeout = null;
44810               }
44811               previous = now;
44812               result = fn.apply(storedContext, storedArgs);
44813               if (!timeout) {
44814                   storedContext = null;
44815                   storedArgs = [];
44816               }
44817           }
44818           else if (!timeout) {
44819               timeout = window.setTimeout(later, remaining);
44820           }
44821           return result;
44822       };
44823   }
44824   
44825 });
44826
44827  
44828
44829