sync
[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
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     getAutoCreate : function()
2915     {
2916         
2917         
2918         return  {
2919             cls :'div' ,
2920             cn : [
2921                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2922                 {
2923                     tag: 'input',
2924                     multiple : 'multiple',
2925                     type : 'file',
2926                     cls : 'd-none  roo-card-upload-selector' 
2927                   
2928                 }
2929                  
2930
2931             ]
2932         };
2933            
2934          
2935     },
2936      
2937    
2938     initEvents : function()
2939     {
2940         
2941         Roo.bootstrap.Button.prototype.initEvents.call(this);
2942         
2943         
2944         
2945         
2946         
2947         this.urlAPI = (window.createObjectURL && window) || 
2948                                 (window.URL && URL.revokeObjectURL && URL) || 
2949                                 (window.webkitURL && webkitURL);
2950                         
2951          
2952          
2953          
2954         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2955         
2956         this.selectorEl.on('change', this.onFileSelected, this);
2957          
2958          
2959        
2960     },
2961     
2962    
2963     onClick : function(e)
2964     {
2965         e.preventDefault();
2966         
2967         if ( this.fireEvent('beforeselect', this) === false) {
2968             return;
2969         }
2970          
2971         this.selectorEl.dom.click();
2972          
2973     },
2974     
2975     onFileSelected : function(e)
2976     {
2977         e.preventDefault();
2978         
2979         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2980             return;
2981         }
2982         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2983         this.selectorEl.dom.reset();
2984         
2985         this.fireEvent('uploaded', this,  files );
2986         
2987     },
2988     
2989        
2990    
2991     
2992     /**
2993      * addCard - add an Attachment to the uploader
2994      * @param data - the data about the image to upload
2995      *
2996      * {
2997           id : 123
2998           title : "Title of file",
2999           is_uploaded : false,
3000           src : "http://.....",
3001           srcfile : { the File upload object },
3002           mimetype : file.type,
3003           preview : false,
3004           is_deleted : 0
3005           .. any other data...
3006         }
3007      *
3008      * 
3009     */
3010      
3011     reset: function()
3012     {
3013          
3014          this.selectorEl
3015     } 
3016     
3017     
3018     
3019     
3020 });
3021  /*
3022  * - LGPL
3023  *
3024  * image
3025  * 
3026  */
3027
3028
3029 /**
3030  * @class Roo.bootstrap.Img
3031  * @extends Roo.bootstrap.Component
3032  * Bootstrap Img class
3033  * @cfg {Boolean} imgResponsive false | true
3034  * @cfg {String} border rounded | circle | thumbnail
3035  * @cfg {String} src image source
3036  * @cfg {String} alt image alternative text
3037  * @cfg {String} href a tag href
3038  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3039  * @cfg {String} xsUrl xs image source
3040  * @cfg {String} smUrl sm image source
3041  * @cfg {String} mdUrl md image source
3042  * @cfg {String} lgUrl lg image source
3043  * 
3044  * @constructor
3045  * Create a new Input
3046  * @param {Object} config The config object
3047  */
3048
3049 Roo.bootstrap.Img = function(config){
3050     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3051     
3052     this.addEvents({
3053         // img events
3054         /**
3055          * @event click
3056          * The img click event for the img.
3057          * @param {Roo.EventObject} e
3058          */
3059         "click" : true
3060     });
3061 };
3062
3063 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3064     
3065     imgResponsive: true,
3066     border: '',
3067     src: 'about:blank',
3068     href: false,
3069     target: false,
3070     xsUrl: '',
3071     smUrl: '',
3072     mdUrl: '',
3073     lgUrl: '',
3074
3075     getAutoCreate : function()
3076     {   
3077         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3078             return this.createSingleImg();
3079         }
3080         
3081         var cfg = {
3082             tag: 'div',
3083             cls: 'roo-image-responsive-group',
3084             cn: []
3085         };
3086         var _this = this;
3087         
3088         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3089             
3090             if(!_this[size + 'Url']){
3091                 return;
3092             }
3093             
3094             var img = {
3095                 tag: 'img',
3096                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3097                 html: _this.html || cfg.html,
3098                 src: _this[size + 'Url']
3099             };
3100             
3101             img.cls += ' roo-image-responsive-' + size;
3102             
3103             var s = ['xs', 'sm', 'md', 'lg'];
3104             
3105             s.splice(s.indexOf(size), 1);
3106             
3107             Roo.each(s, function(ss){
3108                 img.cls += ' hidden-' + ss;
3109             });
3110             
3111             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3112                 cfg.cls += ' img-' + _this.border;
3113             }
3114             
3115             if(_this.alt){
3116                 cfg.alt = _this.alt;
3117             }
3118             
3119             if(_this.href){
3120                 var a = {
3121                     tag: 'a',
3122                     href: _this.href,
3123                     cn: [
3124                         img
3125                     ]
3126                 };
3127
3128                 if(this.target){
3129                     a.target = _this.target;
3130                 }
3131             }
3132             
3133             cfg.cn.push((_this.href) ? a : img);
3134             
3135         });
3136         
3137         return cfg;
3138     },
3139     
3140     createSingleImg : function()
3141     {
3142         var cfg = {
3143             tag: 'img',
3144             cls: (this.imgResponsive) ? 'img-responsive' : '',
3145             html : null,
3146             src : 'about:blank'  // just incase src get's set to undefined?!?
3147         };
3148         
3149         cfg.html = this.html || cfg.html;
3150         
3151         cfg.src = this.src || cfg.src;
3152         
3153         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3154             cfg.cls += ' img-' + this.border;
3155         }
3156         
3157         if(this.alt){
3158             cfg.alt = this.alt;
3159         }
3160         
3161         if(this.href){
3162             var a = {
3163                 tag: 'a',
3164                 href: this.href,
3165                 cn: [
3166                     cfg
3167                 ]
3168             };
3169             
3170             if(this.target){
3171                 a.target = this.target;
3172             }
3173             
3174         }
3175         
3176         return (this.href) ? a : cfg;
3177     },
3178     
3179     initEvents: function() 
3180     {
3181         if(!this.href){
3182             this.el.on('click', this.onClick, this);
3183         }
3184         
3185     },
3186     
3187     onClick : function(e)
3188     {
3189         Roo.log('img onclick');
3190         this.fireEvent('click', this, e);
3191     },
3192     /**
3193      * Sets the url of the image - used to update it
3194      * @param {String} url the url of the image
3195      */
3196     
3197     setSrc : function(url)
3198     {
3199         this.src =  url;
3200         
3201         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3202             this.el.dom.src =  url;
3203             return;
3204         }
3205         
3206         this.el.select('img', true).first().dom.src =  url;
3207     }
3208     
3209     
3210    
3211 });
3212
3213  /*
3214  * - LGPL
3215  *
3216  * image
3217  * 
3218  */
3219
3220
3221 /**
3222  * @class Roo.bootstrap.Link
3223  * @extends Roo.bootstrap.Component
3224  * Bootstrap Link Class
3225  * @cfg {String} alt image alternative text
3226  * @cfg {String} href a tag href
3227  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3228  * @cfg {String} html the content of the link.
3229  * @cfg {String} anchor name for the anchor link
3230  * @cfg {String} fa - favicon
3231
3232  * @cfg {Boolean} preventDefault (true | false) default false
3233
3234  * 
3235  * @constructor
3236  * Create a new Input
3237  * @param {Object} config The config object
3238  */
3239
3240 Roo.bootstrap.Link = function(config){
3241     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3242     
3243     this.addEvents({
3244         // img events
3245         /**
3246          * @event click
3247          * The img click event for the img.
3248          * @param {Roo.EventObject} e
3249          */
3250         "click" : true
3251     });
3252 };
3253
3254 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3255     
3256     href: false,
3257     target: false,
3258     preventDefault: false,
3259     anchor : false,
3260     alt : false,
3261     fa: false,
3262
3263
3264     getAutoCreate : function()
3265     {
3266         var html = this.html || '';
3267         
3268         if (this.fa !== false) {
3269             html = '<i class="fa fa-' + this.fa + '"></i>';
3270         }
3271         var cfg = {
3272             tag: 'a'
3273         };
3274         // anchor's do not require html/href...
3275         if (this.anchor === false) {
3276             cfg.html = html;
3277             cfg.href = this.href || '#';
3278         } else {
3279             cfg.name = this.anchor;
3280             if (this.html !== false || this.fa !== false) {
3281                 cfg.html = html;
3282             }
3283             if (this.href !== false) {
3284                 cfg.href = this.href;
3285             }
3286         }
3287         
3288         if(this.alt !== false){
3289             cfg.alt = this.alt;
3290         }
3291         
3292         
3293         if(this.target !== false) {
3294             cfg.target = this.target;
3295         }
3296         
3297         return cfg;
3298     },
3299     
3300     initEvents: function() {
3301         
3302         if(!this.href || this.preventDefault){
3303             this.el.on('click', this.onClick, this);
3304         }
3305     },
3306     
3307     onClick : function(e)
3308     {
3309         if(this.preventDefault){
3310             e.preventDefault();
3311         }
3312         //Roo.log('img onclick');
3313         this.fireEvent('click', this, e);
3314     }
3315    
3316 });
3317
3318  /*
3319  * - LGPL
3320  *
3321  * header
3322  * 
3323  */
3324
3325 /**
3326  * @class Roo.bootstrap.Header
3327  * @extends Roo.bootstrap.Component
3328  * Bootstrap Header class
3329  * @cfg {String} html content of header
3330  * @cfg {Number} level (1|2|3|4|5|6) default 1
3331  * 
3332  * @constructor
3333  * Create a new Header
3334  * @param {Object} config The config object
3335  */
3336
3337
3338 Roo.bootstrap.Header  = function(config){
3339     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3340 };
3341
3342 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3343     
3344     //href : false,
3345     html : false,
3346     level : 1,
3347     
3348     
3349     
3350     getAutoCreate : function(){
3351         
3352         
3353         
3354         var cfg = {
3355             tag: 'h' + (1 *this.level),
3356             html: this.html || ''
3357         } ;
3358         
3359         return cfg;
3360     }
3361    
3362 });
3363
3364  
3365
3366  /*
3367  * Based on:
3368  * Ext JS Library 1.1.1
3369  * Copyright(c) 2006-2007, Ext JS, LLC.
3370  *
3371  * Originally Released Under LGPL - original licence link has changed is not relivant.
3372  *
3373  * Fork - LGPL
3374  * <script type="text/javascript">
3375  */
3376  
3377 /**
3378  * @class Roo.bootstrap.MenuMgr
3379  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3380  * @singleton
3381  */
3382 Roo.bootstrap.MenuMgr = function(){
3383    var menus, active, groups = {}, attached = false, lastShow = new Date();
3384
3385    // private - called when first menu is created
3386    function init(){
3387        menus = {};
3388        active = new Roo.util.MixedCollection();
3389        Roo.get(document).addKeyListener(27, function(){
3390            if(active.length > 0){
3391                hideAll();
3392            }
3393        });
3394    }
3395
3396    // private
3397    function hideAll(){
3398        if(active && active.length > 0){
3399            var c = active.clone();
3400            c.each(function(m){
3401                m.hide();
3402            });
3403        }
3404    }
3405
3406    // private
3407    function onHide(m){
3408        active.remove(m);
3409        if(active.length < 1){
3410            Roo.get(document).un("mouseup", onMouseDown);
3411             
3412            attached = false;
3413        }
3414    }
3415
3416    // private
3417    function onShow(m){
3418        var last = active.last();
3419        lastShow = new Date();
3420        active.add(m);
3421        if(!attached){
3422           Roo.get(document).on("mouseup", onMouseDown);
3423            
3424            attached = true;
3425        }
3426        if(m.parentMenu){
3427           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3428           m.parentMenu.activeChild = m;
3429        }else if(last && last.isVisible()){
3430           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3431        }
3432    }
3433
3434    // private
3435    function onBeforeHide(m){
3436        if(m.activeChild){
3437            m.activeChild.hide();
3438        }
3439        if(m.autoHideTimer){
3440            clearTimeout(m.autoHideTimer);
3441            delete m.autoHideTimer;
3442        }
3443    }
3444
3445    // private
3446    function onBeforeShow(m){
3447        var pm = m.parentMenu;
3448        if(!pm && !m.allowOtherMenus){
3449            hideAll();
3450        }else if(pm && pm.activeChild && active != m){
3451            pm.activeChild.hide();
3452        }
3453    }
3454
3455    // private this should really trigger on mouseup..
3456    function onMouseDown(e){
3457         Roo.log("on Mouse Up");
3458         
3459         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3460             Roo.log("MenuManager hideAll");
3461             hideAll();
3462             e.stopEvent();
3463         }
3464         
3465         
3466    }
3467
3468    // private
3469    function onBeforeCheck(mi, state){
3470        if(state){
3471            var g = groups[mi.group];
3472            for(var i = 0, l = g.length; i < l; i++){
3473                if(g[i] != mi){
3474                    g[i].setChecked(false);
3475                }
3476            }
3477        }
3478    }
3479
3480    return {
3481
3482        /**
3483         * Hides all menus that are currently visible
3484         */
3485        hideAll : function(){
3486             hideAll();  
3487        },
3488
3489        // private
3490        register : function(menu){
3491            if(!menus){
3492                init();
3493            }
3494            menus[menu.id] = menu;
3495            menu.on("beforehide", onBeforeHide);
3496            menu.on("hide", onHide);
3497            menu.on("beforeshow", onBeforeShow);
3498            menu.on("show", onShow);
3499            var g = menu.group;
3500            if(g && menu.events["checkchange"]){
3501                if(!groups[g]){
3502                    groups[g] = [];
3503                }
3504                groups[g].push(menu);
3505                menu.on("checkchange", onCheck);
3506            }
3507        },
3508
3509         /**
3510          * Returns a {@link Roo.menu.Menu} object
3511          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3512          * be used to generate and return a new Menu instance.
3513          */
3514        get : function(menu){
3515            if(typeof menu == "string"){ // menu id
3516                return menus[menu];
3517            }else if(menu.events){  // menu instance
3518                return menu;
3519            }
3520            /*else if(typeof menu.length == 'number'){ // array of menu items?
3521                return new Roo.bootstrap.Menu({items:menu});
3522            }else{ // otherwise, must be a config
3523                return new Roo.bootstrap.Menu(menu);
3524            }
3525            */
3526            return false;
3527        },
3528
3529        // private
3530        unregister : function(menu){
3531            delete menus[menu.id];
3532            menu.un("beforehide", onBeforeHide);
3533            menu.un("hide", onHide);
3534            menu.un("beforeshow", onBeforeShow);
3535            menu.un("show", onShow);
3536            var g = menu.group;
3537            if(g && menu.events["checkchange"]){
3538                groups[g].remove(menu);
3539                menu.un("checkchange", onCheck);
3540            }
3541        },
3542
3543        // private
3544        registerCheckable : function(menuItem){
3545            var g = menuItem.group;
3546            if(g){
3547                if(!groups[g]){
3548                    groups[g] = [];
3549                }
3550                groups[g].push(menuItem);
3551                menuItem.on("beforecheckchange", onBeforeCheck);
3552            }
3553        },
3554
3555        // private
3556        unregisterCheckable : function(menuItem){
3557            var g = menuItem.group;
3558            if(g){
3559                groups[g].remove(menuItem);
3560                menuItem.un("beforecheckchange", onBeforeCheck);
3561            }
3562        }
3563    };
3564 }();/*
3565  * - LGPL
3566  *
3567  * menu
3568  * 
3569  */
3570
3571 /**
3572  * @class Roo.bootstrap.Menu
3573  * @extends Roo.bootstrap.Component
3574  * Bootstrap Menu class - container for MenuItems
3575  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3576  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3577  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3578  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3579  * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3580  * 
3581  * @constructor
3582  * Create a new Menu
3583  * @param {Object} config The config object
3584  */
3585
3586
3587 Roo.bootstrap.Menu = function(config){
3588     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3589     if (this.registerMenu && this.type != 'treeview')  {
3590         Roo.bootstrap.MenuMgr.register(this);
3591     }
3592     
3593     
3594     this.addEvents({
3595         /**
3596          * @event beforeshow
3597          * Fires before this menu is displayed (return false to block)
3598          * @param {Roo.menu.Menu} this
3599          */
3600         beforeshow : true,
3601         /**
3602          * @event beforehide
3603          * Fires before this menu is hidden (return false to block)
3604          * @param {Roo.menu.Menu} this
3605          */
3606         beforehide : true,
3607         /**
3608          * @event show
3609          * Fires after this menu is displayed
3610          * @param {Roo.menu.Menu} this
3611          */
3612         show : true,
3613         /**
3614          * @event hide
3615          * Fires after this menu is hidden
3616          * @param {Roo.menu.Menu} this
3617          */
3618         hide : true,
3619         /**
3620          * @event click
3621          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3622          * @param {Roo.menu.Menu} this
3623          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3624          * @param {Roo.EventObject} e
3625          */
3626         click : true,
3627         /**
3628          * @event mouseover
3629          * Fires when the mouse is hovering over this menu
3630          * @param {Roo.menu.Menu} this
3631          * @param {Roo.EventObject} e
3632          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3633          */
3634         mouseover : true,
3635         /**
3636          * @event mouseout
3637          * Fires when the mouse exits this menu
3638          * @param {Roo.menu.Menu} this
3639          * @param {Roo.EventObject} e
3640          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3641          */
3642         mouseout : true,
3643         /**
3644          * @event itemclick
3645          * Fires when a menu item contained in this menu is clicked
3646          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3647          * @param {Roo.EventObject} e
3648          */
3649         itemclick: true
3650     });
3651     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3652 };
3653
3654 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3655     
3656    /// html : false,
3657     //align : '',
3658     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3659     type: false,
3660     /**
3661      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3662      */
3663     registerMenu : true,
3664     
3665     menuItems :false, // stores the menu items..
3666     
3667     hidden:true,
3668         
3669     parentMenu : false,
3670     
3671     stopEvent : true,
3672     
3673     isLink : false,
3674     
3675     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3676     
3677     hideTrigger : false,
3678     
3679     
3680     getChildContainer : function() {
3681         return this.el;  
3682     },
3683     
3684     getAutoCreate : function(){
3685          
3686         //if (['right'].indexOf(this.align)!==-1) {
3687         //    cfg.cn[1].cls += ' pull-right'
3688         //}
3689         
3690         
3691         var cfg = {
3692             tag : 'ul',
3693             cls : 'dropdown-menu' ,
3694             style : 'z-index:1000'
3695             
3696         };
3697         
3698         if (this.type === 'submenu') {
3699             cfg.cls = 'submenu active';
3700         }
3701         if (this.type === 'treeview') {
3702             cfg.cls = 'treeview-menu';
3703         }
3704         
3705         return cfg;
3706     },
3707     initEvents : function() {
3708         
3709        // Roo.log("ADD event");
3710        // Roo.log(this.triggerEl.dom);
3711         
3712         this.triggerEl.on('click', this.onTriggerClick, this);
3713         
3714         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3715         
3716         if (!this.hideTrigger) {
3717             if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3718                 // dropdown toggle on the 'a' in BS4?
3719                 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3720             } else {
3721                 this.triggerEl.addClass('dropdown-toggle');
3722             }
3723         }
3724         if (Roo.isTouch) {
3725             this.el.on('touchstart'  , this.onTouch, this);
3726         }
3727         this.el.on('click' , this.onClick, this);
3728
3729         this.el.on("mouseover", this.onMouseOver, this);
3730         this.el.on("mouseout", this.onMouseOut, this);
3731         
3732     },
3733     
3734     findTargetItem : function(e)
3735     {
3736         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3737         if(!t){
3738             return false;
3739         }
3740         //Roo.log(t);         Roo.log(t.id);
3741         if(t && t.id){
3742             //Roo.log(this.menuitems);
3743             return this.menuitems.get(t.id);
3744             
3745             //return this.items.get(t.menuItemId);
3746         }
3747         
3748         return false;
3749     },
3750     
3751     onTouch : function(e) 
3752     {
3753         Roo.log("menu.onTouch");
3754         //e.stopEvent(); this make the user popdown broken
3755         this.onClick(e);
3756     },
3757     
3758     onClick : function(e)
3759     {
3760         Roo.log("menu.onClick");
3761         
3762         var t = this.findTargetItem(e);
3763         if(!t || t.isContainer){
3764             return;
3765         }
3766         Roo.log(e);
3767         /*
3768         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3769             if(t == this.activeItem && t.shouldDeactivate(e)){
3770                 this.activeItem.deactivate();
3771                 delete this.activeItem;
3772                 return;
3773             }
3774             if(t.canActivate){
3775                 this.setActiveItem(t, true);
3776             }
3777             return;
3778             
3779             
3780         }
3781         */
3782        
3783         Roo.log('pass click event');
3784         
3785         t.onClick(e);
3786         
3787         this.fireEvent("click", this, t, e);
3788         
3789         var _this = this;
3790         
3791         if(!t.href.length || t.href == '#'){
3792             (function() { _this.hide(); }).defer(100);
3793         }
3794         
3795     },
3796     
3797     onMouseOver : function(e){
3798         var t  = this.findTargetItem(e);
3799         //Roo.log(t);
3800         //if(t){
3801         //    if(t.canActivate && !t.disabled){
3802         //        this.setActiveItem(t, true);
3803         //    }
3804         //}
3805         
3806         this.fireEvent("mouseover", this, e, t);
3807     },
3808     isVisible : function(){
3809         return !this.hidden;
3810     },
3811     onMouseOut : function(e){
3812         var t  = this.findTargetItem(e);
3813         
3814         //if(t ){
3815         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3816         //        this.activeItem.deactivate();
3817         //        delete this.activeItem;
3818         //    }
3819         //}
3820         this.fireEvent("mouseout", this, e, t);
3821     },
3822     
3823     
3824     /**
3825      * Displays this menu relative to another element
3826      * @param {String/HTMLElement/Roo.Element} element The element to align to
3827      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3828      * the element (defaults to this.defaultAlign)
3829      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3830      */
3831     show : function(el, pos, parentMenu)
3832     {
3833         if (false === this.fireEvent("beforeshow", this)) {
3834             Roo.log("show canceled");
3835             return;
3836         }
3837         this.parentMenu = parentMenu;
3838         if(!this.el){
3839             this.render();
3840         }
3841         
3842         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3843     },
3844      /**
3845      * Displays this menu at a specific xy position
3846      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3847      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3848      */
3849     showAt : function(xy, parentMenu, /* private: */_e){
3850         this.parentMenu = parentMenu;
3851         if(!this.el){
3852             this.render();
3853         }
3854         if(_e !== false){
3855             this.fireEvent("beforeshow", this);
3856             //xy = this.el.adjustForConstraints(xy);
3857         }
3858         
3859         //this.el.show();
3860         this.hideMenuItems();
3861         this.hidden = false;
3862         this.triggerEl.addClass('open');
3863         this.el.addClass('show');
3864         
3865         // reassign x when hitting right
3866         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3867             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3868         }
3869         
3870         // reassign y when hitting bottom
3871         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3872             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3873         }
3874         
3875         // but the list may align on trigger left or trigger top... should it be a properity?
3876         
3877         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3878             this.el.setXY(xy);
3879         }
3880         
3881         this.focus();
3882         this.fireEvent("show", this);
3883     },
3884     
3885     focus : function(){
3886         return;
3887         if(!this.hidden){
3888             this.doFocus.defer(50, this);
3889         }
3890     },
3891
3892     doFocus : function(){
3893         if(!this.hidden){
3894             this.focusEl.focus();
3895         }
3896     },
3897
3898     /**
3899      * Hides this menu and optionally all parent menus
3900      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3901      */
3902     hide : function(deep)
3903     {
3904         if (false === this.fireEvent("beforehide", this)) {
3905             Roo.log("hide canceled");
3906             return;
3907         }
3908         this.hideMenuItems();
3909         if(this.el && this.isVisible()){
3910            
3911             if(this.activeItem){
3912                 this.activeItem.deactivate();
3913                 this.activeItem = null;
3914             }
3915             this.triggerEl.removeClass('open');;
3916             this.el.removeClass('show');
3917             this.hidden = true;
3918             this.fireEvent("hide", this);
3919         }
3920         if(deep === true && this.parentMenu){
3921             this.parentMenu.hide(true);
3922         }
3923     },
3924     
3925     onTriggerClick : function(e)
3926     {
3927         Roo.log('trigger click');
3928         
3929         var target = e.getTarget();
3930         
3931         Roo.log(target.nodeName.toLowerCase());
3932         
3933         if(target.nodeName.toLowerCase() === 'i'){
3934             e.preventDefault();
3935         }
3936         
3937     },
3938     
3939     onTriggerPress  : function(e)
3940     {
3941         Roo.log('trigger press');
3942         //Roo.log(e.getTarget());
3943        // Roo.log(this.triggerEl.dom);
3944        
3945         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3946         var pel = Roo.get(e.getTarget());
3947         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3948             Roo.log('is treeview or dropdown?');
3949             return;
3950         }
3951         
3952         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3953             return;
3954         }
3955         
3956         if (this.isVisible()) {
3957             Roo.log('hide');
3958             this.hide();
3959         } else {
3960             Roo.log('show');
3961             this.show(this.triggerEl, '?', false);
3962         }
3963         
3964         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3965             e.stopEvent();
3966         }
3967         
3968     },
3969        
3970     
3971     hideMenuItems : function()
3972     {
3973         Roo.log("hide Menu Items");
3974         if (!this.el) { 
3975             return;
3976         }
3977         
3978         this.el.select('.open',true).each(function(aa) {
3979             
3980             aa.removeClass('open');
3981          
3982         });
3983     },
3984     addxtypeChild : function (tree, cntr) {
3985         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3986           
3987         this.menuitems.add(comp);
3988         return comp;
3989
3990     },
3991     getEl : function()
3992     {
3993         Roo.log(this.el);
3994         return this.el;
3995     },
3996     
3997     clear : function()
3998     {
3999         this.getEl().dom.innerHTML = '';
4000         this.menuitems.clear();
4001     }
4002 });
4003
4004  
4005  /*
4006  * - LGPL
4007  *
4008  * menu item
4009  * 
4010  */
4011
4012
4013 /**
4014  * @class Roo.bootstrap.MenuItem
4015  * @extends Roo.bootstrap.Component
4016  * Bootstrap MenuItem class
4017  * @cfg {String} html the menu label
4018  * @cfg {String} href the link
4019  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4020  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4021  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4022  * @cfg {String} fa favicon to show on left of menu item.
4023  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4024  * 
4025  * 
4026  * @constructor
4027  * Create a new MenuItem
4028  * @param {Object} config The config object
4029  */
4030
4031
4032 Roo.bootstrap.MenuItem = function(config){
4033     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4034     this.addEvents({
4035         // raw events
4036         /**
4037          * @event click
4038          * The raw click event for the entire grid.
4039          * @param {Roo.bootstrap.MenuItem} this
4040          * @param {Roo.EventObject} e
4041          */
4042         "click" : true
4043     });
4044 };
4045
4046 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4047     
4048     href : false,
4049     html : false,
4050     preventDefault: false,
4051     isContainer : false,
4052     active : false,
4053     fa: false,
4054     
4055     getAutoCreate : function(){
4056         
4057         if(this.isContainer){
4058             return {
4059                 tag: 'li',
4060                 cls: 'dropdown-menu-item '
4061             };
4062         }
4063         var ctag = {
4064             tag: 'span',
4065             html: 'Link'
4066         };
4067         
4068         var anc = {
4069             tag : 'a',
4070             cls : 'dropdown-item',
4071             href : '#',
4072             cn : [  ]
4073         };
4074         
4075         if (this.fa !== false) {
4076             anc.cn.push({
4077                 tag : 'i',
4078                 cls : 'fa fa-' + this.fa
4079             });
4080         }
4081         
4082         anc.cn.push(ctag);
4083         
4084         
4085         var cfg= {
4086             tag: 'li',
4087             cls: 'dropdown-menu-item',
4088             cn: [ anc ]
4089         };
4090         if (this.parent().type == 'treeview') {
4091             cfg.cls = 'treeview-menu';
4092         }
4093         if (this.active) {
4094             cfg.cls += ' active';
4095         }
4096         
4097         
4098         
4099         anc.href = this.href || cfg.cn[0].href ;
4100         ctag.html = this.html || cfg.cn[0].html ;
4101         return cfg;
4102     },
4103     
4104     initEvents: function()
4105     {
4106         if (this.parent().type == 'treeview') {
4107             this.el.select('a').on('click', this.onClick, this);
4108         }
4109         
4110         if (this.menu) {
4111             this.menu.parentType = this.xtype;
4112             this.menu.triggerEl = this.el;
4113             this.menu = this.addxtype(Roo.apply({}, this.menu));
4114         }
4115         
4116     },
4117     onClick : function(e)
4118     {
4119         Roo.log('item on click ');
4120         
4121         if(this.preventDefault){
4122             e.preventDefault();
4123         }
4124         //this.parent().hideMenuItems();
4125         
4126         this.fireEvent('click', this, e);
4127     },
4128     getEl : function()
4129     {
4130         return this.el;
4131     } 
4132 });
4133
4134  
4135
4136  /*
4137  * - LGPL
4138  *
4139  * menu separator
4140  * 
4141  */
4142
4143
4144 /**
4145  * @class Roo.bootstrap.MenuSeparator
4146  * @extends Roo.bootstrap.Component
4147  * Bootstrap MenuSeparator class
4148  * 
4149  * @constructor
4150  * Create a new MenuItem
4151  * @param {Object} config The config object
4152  */
4153
4154
4155 Roo.bootstrap.MenuSeparator = function(config){
4156     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4157 };
4158
4159 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4160     
4161     getAutoCreate : function(){
4162         var cfg = {
4163             cls: 'divider',
4164             tag : 'li'
4165         };
4166         
4167         return cfg;
4168     }
4169    
4170 });
4171
4172  
4173
4174  
4175 /*
4176 * Licence: LGPL
4177 */
4178
4179 /**
4180  * @class Roo.bootstrap.Modal
4181  * @extends Roo.bootstrap.Component
4182  * Bootstrap Modal class
4183  * @cfg {String} title Title of dialog
4184  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4185  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4186  * @cfg {Boolean} specificTitle default false
4187  * @cfg {Array} buttons Array of buttons or standard button set..
4188  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4189  * @cfg {Boolean} animate default true
4190  * @cfg {Boolean} allow_close default true
4191  * @cfg {Boolean} fitwindow default false
4192  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4193  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4194  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4195  * @cfg {String} size (sm|lg|xl) default empty
4196  * @cfg {Number} max_width set the max width of modal
4197  * @cfg {Boolean} editableTitle can the title be edited
4198
4199  *
4200  *
4201  * @constructor
4202  * Create a new Modal Dialog
4203  * @param {Object} config The config object
4204  */
4205
4206 Roo.bootstrap.Modal = function(config){
4207     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4208     this.addEvents({
4209         // raw events
4210         /**
4211          * @event btnclick
4212          * The raw btnclick event for the button
4213          * @param {Roo.EventObject} e
4214          */
4215         "btnclick" : true,
4216         /**
4217          * @event resize
4218          * Fire when dialog resize
4219          * @param {Roo.bootstrap.Modal} this
4220          * @param {Roo.EventObject} e
4221          */
4222         "resize" : true,
4223         /**
4224          * @event titlechanged
4225          * Fire when the editable title has been changed
4226          * @param {Roo.bootstrap.Modal} this
4227          * @param {Roo.EventObject} value
4228          */
4229         "titlechanged" : true 
4230         
4231     });
4232     this.buttons = this.buttons || [];
4233
4234     if (this.tmpl) {
4235         this.tmpl = Roo.factory(this.tmpl);
4236     }
4237
4238 };
4239
4240 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4241
4242     title : 'test dialog',
4243
4244     buttons : false,
4245
4246     // set on load...
4247
4248     html: false,
4249
4250     tmp: false,
4251
4252     specificTitle: false,
4253
4254     buttonPosition: 'right',
4255
4256     allow_close : true,
4257
4258     animate : true,
4259
4260     fitwindow: false,
4261     
4262      // private
4263     dialogEl: false,
4264     bodyEl:  false,
4265     footerEl:  false,
4266     titleEl:  false,
4267     closeEl:  false,
4268
4269     size: '',
4270     
4271     max_width: 0,
4272     
4273     max_height: 0,
4274     
4275     fit_content: false,
4276     editableTitle  : false,
4277
4278     onRender : function(ct, position)
4279     {
4280         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4281
4282         if(!this.el){
4283             var cfg = Roo.apply({},  this.getAutoCreate());
4284             cfg.id = Roo.id();
4285             //if(!cfg.name){
4286             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4287             //}
4288             //if (!cfg.name.length) {
4289             //    delete cfg.name;
4290            // }
4291             if (this.cls) {
4292                 cfg.cls += ' ' + this.cls;
4293             }
4294             if (this.style) {
4295                 cfg.style = this.style;
4296             }
4297             this.el = Roo.get(document.body).createChild(cfg, position);
4298         }
4299         //var type = this.el.dom.type;
4300
4301
4302         if(this.tabIndex !== undefined){
4303             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4304         }
4305
4306         this.dialogEl = this.el.select('.modal-dialog',true).first();
4307         this.bodyEl = this.el.select('.modal-body',true).first();
4308         this.closeEl = this.el.select('.modal-header .close', true).first();
4309         this.headerEl = this.el.select('.modal-header',true).first();
4310         this.titleEl = this.el.select('.modal-title',true).first();
4311         this.footerEl = this.el.select('.modal-footer',true).first();
4312
4313         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4314         
4315         //this.el.addClass("x-dlg-modal");
4316
4317         if (this.buttons.length) {
4318             Roo.each(this.buttons, function(bb) {
4319                 var b = Roo.apply({}, bb);
4320                 b.xns = b.xns || Roo.bootstrap;
4321                 b.xtype = b.xtype || 'Button';
4322                 if (typeof(b.listeners) == 'undefined') {
4323                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4324                 }
4325
4326                 var btn = Roo.factory(b);
4327
4328                 btn.render(this.getButtonContainer());
4329
4330             },this);
4331         }
4332         // render the children.
4333         var nitems = [];
4334
4335         if(typeof(this.items) != 'undefined'){
4336             var items = this.items;
4337             delete this.items;
4338
4339             for(var i =0;i < items.length;i++) {
4340                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4341             }
4342         }
4343
4344         this.items = nitems;
4345
4346         // where are these used - they used to be body/close/footer
4347
4348
4349         this.initEvents();
4350         //this.el.addClass([this.fieldClass, this.cls]);
4351
4352     },
4353
4354     getAutoCreate : function()
4355     {
4356         // we will default to modal-body-overflow - might need to remove or make optional later.
4357         var bdy = {
4358                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4359                 html : this.html || ''
4360         };
4361
4362         var title = {
4363             tag: 'h5',
4364             cls : 'modal-title',
4365             html : this.title
4366         };
4367
4368         if(this.specificTitle){ // WTF is this?
4369             title = this.title;
4370         }
4371
4372         var header = [];
4373         if (this.allow_close && Roo.bootstrap.version == 3) {
4374             header.push({
4375                 tag: 'button',
4376                 cls : 'close',
4377                 html : '&times'
4378             });
4379         }
4380
4381         header.push(title);
4382
4383         if (this.editableTitle) {
4384             header.push({
4385                 cls: 'form-control roo-editable-title d-none',
4386                 tag: 'input',
4387                 type: 'text'
4388             });
4389         }
4390         
4391         if (this.allow_close && Roo.bootstrap.version == 4) {
4392             header.push({
4393                 tag: 'button',
4394                 cls : 'close',
4395                 html : '&times'
4396             });
4397         }
4398         
4399         var size = '';
4400
4401         if(this.size.length){
4402             size = 'modal-' + this.size;
4403         }
4404         
4405         var footer = Roo.bootstrap.version == 3 ?
4406             {
4407                 cls : 'modal-footer',
4408                 cn : [
4409                     {
4410                         tag: 'div',
4411                         cls: 'btn-' + this.buttonPosition
4412                     }
4413                 ]
4414
4415             } :
4416             {  // BS4 uses mr-auto on left buttons....
4417                 cls : 'modal-footer'
4418             };
4419
4420             
4421
4422         
4423         
4424         var modal = {
4425             cls: "modal",
4426              cn : [
4427                 {
4428                     cls: "modal-dialog " + size,
4429                     cn : [
4430                         {
4431                             cls : "modal-content",
4432                             cn : [
4433                                 {
4434                                     cls : 'modal-header',
4435                                     cn : header
4436                                 },
4437                                 bdy,
4438                                 footer
4439                             ]
4440
4441                         }
4442                     ]
4443
4444                 }
4445             ]
4446         };
4447
4448         if(this.animate){
4449             modal.cls += ' fade';
4450         }
4451
4452         return modal;
4453
4454     },
4455     getChildContainer : function() {
4456
4457          return this.bodyEl;
4458
4459     },
4460     getButtonContainer : function() {
4461         
4462          return Roo.bootstrap.version == 4 ?
4463             this.el.select('.modal-footer',true).first()
4464             : this.el.select('.modal-footer div',true).first();
4465
4466     },
4467     initEvents : function()
4468     {
4469         if (this.allow_close) {
4470             this.closeEl.on('click', this.hide, this);
4471         }
4472         Roo.EventManager.onWindowResize(this.resize, this, true);
4473         if (this.editableTitle) {
4474             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4475             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4476             this.headerEditEl.on('keyup', function(e) {
4477                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4478                         this.toggleHeaderInput(false)
4479                     }
4480                 }, this);
4481             this.headerEditEl.on('blur', function(e) {
4482                 this.toggleHeaderInput(false)
4483             },this);
4484         }
4485
4486     },
4487   
4488
4489     resize : function()
4490     {
4491         this.maskEl.setSize(
4492             Roo.lib.Dom.getViewWidth(true),
4493             Roo.lib.Dom.getViewHeight(true)
4494         );
4495         
4496         if (this.fitwindow) {
4497             
4498            this.dialogEl.setStyle( { 'max-width' : '100%' });
4499             this.setSize(
4500                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4501                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4502             );
4503             return;
4504         }
4505         
4506         if(this.max_width !== 0) {
4507             
4508             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4509             
4510             if(this.height) {
4511                 this.setSize(w, this.height);
4512                 return;
4513             }
4514             
4515             if(this.max_height) {
4516                 this.setSize(w,Math.min(
4517                     this.max_height,
4518                     Roo.lib.Dom.getViewportHeight(true) - 60
4519                 ));
4520                 
4521                 return;
4522             }
4523             
4524             if(!this.fit_content) {
4525                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4526                 return;
4527             }
4528             
4529             this.setSize(w, Math.min(
4530                 60 +
4531                 this.headerEl.getHeight() + 
4532                 this.footerEl.getHeight() + 
4533                 this.getChildHeight(this.bodyEl.dom.childNodes),
4534                 Roo.lib.Dom.getViewportHeight(true) - 60)
4535             );
4536         }
4537         
4538     },
4539
4540     setSize : function(w,h)
4541     {
4542         if (!w && !h) {
4543             return;
4544         }
4545         
4546         this.resizeTo(w,h);
4547     },
4548
4549     show : function() {
4550
4551         if (!this.rendered) {
4552             this.render();
4553         }
4554         this.toggleHeaderInput(false);
4555         //this.el.setStyle('display', 'block');
4556         this.el.removeClass('hideing');
4557         this.el.dom.style.display='block';
4558         
4559         Roo.get(document.body).addClass('modal-open');
4560  
4561         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4562             
4563             (function(){
4564                 this.el.addClass('show');
4565                 this.el.addClass('in');
4566             }).defer(50, this);
4567         }else{
4568             this.el.addClass('show');
4569             this.el.addClass('in');
4570         }
4571
4572         // not sure how we can show data in here..
4573         //if (this.tmpl) {
4574         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4575         //}
4576
4577         Roo.get(document.body).addClass("x-body-masked");
4578         
4579         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4580         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4581         this.maskEl.dom.style.display = 'block';
4582         this.maskEl.addClass('show');
4583         
4584         
4585         this.resize();
4586         
4587         this.fireEvent('show', this);
4588
4589         // set zindex here - otherwise it appears to be ignored...
4590         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4591
4592         (function () {
4593             this.items.forEach( function(e) {
4594                 e.layout ? e.layout() : false;
4595
4596             });
4597         }).defer(100,this);
4598
4599     },
4600     hide : function()
4601     {
4602         if(this.fireEvent("beforehide", this) !== false){
4603             
4604             this.maskEl.removeClass('show');
4605             
4606             this.maskEl.dom.style.display = '';
4607             Roo.get(document.body).removeClass("x-body-masked");
4608             this.el.removeClass('in');
4609             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4610
4611             if(this.animate){ // why
4612                 this.el.addClass('hideing');
4613                 this.el.removeClass('show');
4614                 (function(){
4615                     if (!this.el.hasClass('hideing')) {
4616                         return; // it's been shown again...
4617                     }
4618                     
4619                     this.el.dom.style.display='';
4620
4621                     Roo.get(document.body).removeClass('modal-open');
4622                     this.el.removeClass('hideing');
4623                 }).defer(150,this);
4624                 
4625             }else{
4626                 this.el.removeClass('show');
4627                 this.el.dom.style.display='';
4628                 Roo.get(document.body).removeClass('modal-open');
4629
4630             }
4631             this.fireEvent('hide', this);
4632         }
4633     },
4634     isVisible : function()
4635     {
4636         
4637         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4638         
4639     },
4640
4641     addButton : function(str, cb)
4642     {
4643
4644
4645         var b = Roo.apply({}, { html : str } );
4646         b.xns = b.xns || Roo.bootstrap;
4647         b.xtype = b.xtype || 'Button';
4648         if (typeof(b.listeners) == 'undefined') {
4649             b.listeners = { click : cb.createDelegate(this)  };
4650         }
4651
4652         var btn = Roo.factory(b);
4653
4654         btn.render(this.getButtonContainer());
4655
4656         return btn;
4657
4658     },
4659
4660     setDefaultButton : function(btn)
4661     {
4662         //this.el.select('.modal-footer').()
4663     },
4664
4665     resizeTo: function(w,h)
4666     {
4667         this.dialogEl.setWidth(w);
4668         
4669         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4670
4671         this.bodyEl.setHeight(h - diff);
4672         
4673         this.fireEvent('resize', this);
4674     },
4675     
4676     setContentSize  : function(w, h)
4677     {
4678
4679     },
4680     onButtonClick: function(btn,e)
4681     {
4682         //Roo.log([a,b,c]);
4683         this.fireEvent('btnclick', btn.name, e);
4684     },
4685      /**
4686      * Set the title of the Dialog
4687      * @param {String} str new Title
4688      */
4689     setTitle: function(str) {
4690         this.titleEl.dom.innerHTML = str;
4691         this.title = str;
4692     },
4693     /**
4694      * Set the body of the Dialog
4695      * @param {String} str new Title
4696      */
4697     setBody: function(str) {
4698         this.bodyEl.dom.innerHTML = str;
4699     },
4700     /**
4701      * Set the body of the Dialog using the template
4702      * @param {Obj} data - apply this data to the template and replace the body contents.
4703      */
4704     applyBody: function(obj)
4705     {
4706         if (!this.tmpl) {
4707             Roo.log("Error - using apply Body without a template");
4708             //code
4709         }
4710         this.tmpl.overwrite(this.bodyEl, obj);
4711     },
4712     
4713     getChildHeight : function(child_nodes)
4714     {
4715         if(
4716             !child_nodes ||
4717             child_nodes.length == 0
4718         ) {
4719             return 0;
4720         }
4721         
4722         var child_height = 0;
4723         
4724         for(var i = 0; i < child_nodes.length; i++) {
4725             
4726             /*
4727             * for modal with tabs...
4728             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4729                 
4730                 var layout_childs = child_nodes[i].childNodes;
4731                 
4732                 for(var j = 0; j < layout_childs.length; j++) {
4733                     
4734                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4735                         
4736                         var layout_body_childs = layout_childs[j].childNodes;
4737                         
4738                         for(var k = 0; k < layout_body_childs.length; k++) {
4739                             
4740                             if(layout_body_childs[k].classList.contains('navbar')) {
4741                                 child_height += layout_body_childs[k].offsetHeight;
4742                                 continue;
4743                             }
4744                             
4745                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4746                                 
4747                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4748                                 
4749                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4750                                     
4751                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4752                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4753                                         continue;
4754                                     }
4755                                     
4756                                 }
4757                                 
4758                             }
4759                             
4760                         }
4761                     }
4762                 }
4763                 continue;
4764             }
4765             */
4766             
4767             child_height += child_nodes[i].offsetHeight;
4768             // Roo.log(child_nodes[i].offsetHeight);
4769         }
4770         
4771         return child_height;
4772     },
4773     toggleHeaderInput : function(is_edit)
4774     {
4775         if (!this.editableTitle) {
4776             return; // not editable.
4777         }
4778         if (is_edit && this.is_header_editing) {
4779             return; // already editing..
4780         }
4781         if (is_edit) {
4782     
4783             this.headerEditEl.dom.value = this.title;
4784             this.headerEditEl.removeClass('d-none');
4785             this.headerEditEl.dom.focus();
4786             this.titleEl.addClass('d-none');
4787             
4788             this.is_header_editing = true;
4789             return
4790         }
4791         // flip back to not editing.
4792         this.title = this.headerEditEl.dom.value;
4793         this.headerEditEl.addClass('d-none');
4794         this.titleEl.removeClass('d-none');
4795         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4796         this.is_header_editing = false;
4797         this.fireEvent('titlechanged', this, this.title);
4798     
4799             
4800         
4801     }
4802
4803 });
4804
4805
4806 Roo.apply(Roo.bootstrap.Modal,  {
4807     /**
4808          * Button config that displays a single OK button
4809          * @type Object
4810          */
4811         OK :  [{
4812             name : 'ok',
4813             weight : 'primary',
4814             html : 'OK'
4815         }],
4816         /**
4817          * Button config that displays Yes and No buttons
4818          * @type Object
4819          */
4820         YESNO : [
4821             {
4822                 name  : 'no',
4823                 html : 'No'
4824             },
4825             {
4826                 name  :'yes',
4827                 weight : 'primary',
4828                 html : 'Yes'
4829             }
4830         ],
4831
4832         /**
4833          * Button config that displays OK and Cancel buttons
4834          * @type Object
4835          */
4836         OKCANCEL : [
4837             {
4838                name : 'cancel',
4839                 html : 'Cancel'
4840             },
4841             {
4842                 name : 'ok',
4843                 weight : 'primary',
4844                 html : 'OK'
4845             }
4846         ],
4847         /**
4848          * Button config that displays Yes, No and Cancel buttons
4849          * @type Object
4850          */
4851         YESNOCANCEL : [
4852             {
4853                 name : 'yes',
4854                 weight : 'primary',
4855                 html : 'Yes'
4856             },
4857             {
4858                 name : 'no',
4859                 html : 'No'
4860             },
4861             {
4862                 name : 'cancel',
4863                 html : 'Cancel'
4864             }
4865         ],
4866         
4867         zIndex : 10001
4868 });
4869
4870 /*
4871  * - LGPL
4872  *
4873  * messagebox - can be used as a replace
4874  * 
4875  */
4876 /**
4877  * @class Roo.MessageBox
4878  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4879  * Example usage:
4880  *<pre><code>
4881 // Basic alert:
4882 Roo.Msg.alert('Status', 'Changes saved successfully.');
4883
4884 // Prompt for user data:
4885 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4886     if (btn == 'ok'){
4887         // process text value...
4888     }
4889 });
4890
4891 // Show a dialog using config options:
4892 Roo.Msg.show({
4893    title:'Save Changes?',
4894    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4895    buttons: Roo.Msg.YESNOCANCEL,
4896    fn: processResult,
4897    animEl: 'elId'
4898 });
4899 </code></pre>
4900  * @singleton
4901  */
4902 Roo.bootstrap.MessageBox = function(){
4903     var dlg, opt, mask, waitTimer;
4904     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4905     var buttons, activeTextEl, bwidth;
4906
4907     
4908     // private
4909     var handleButton = function(button){
4910         dlg.hide();
4911         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4912     };
4913
4914     // private
4915     var handleHide = function(){
4916         if(opt && opt.cls){
4917             dlg.el.removeClass(opt.cls);
4918         }
4919         //if(waitTimer){
4920         //    Roo.TaskMgr.stop(waitTimer);
4921         //    waitTimer = null;
4922         //}
4923     };
4924
4925     // private
4926     var updateButtons = function(b){
4927         var width = 0;
4928         if(!b){
4929             buttons["ok"].hide();
4930             buttons["cancel"].hide();
4931             buttons["yes"].hide();
4932             buttons["no"].hide();
4933             dlg.footerEl.hide();
4934             
4935             return width;
4936         }
4937         dlg.footerEl.show();
4938         for(var k in buttons){
4939             if(typeof buttons[k] != "function"){
4940                 if(b[k]){
4941                     buttons[k].show();
4942                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4943                     width += buttons[k].el.getWidth()+15;
4944                 }else{
4945                     buttons[k].hide();
4946                 }
4947             }
4948         }
4949         return width;
4950     };
4951
4952     // private
4953     var handleEsc = function(d, k, e){
4954         if(opt && opt.closable !== false){
4955             dlg.hide();
4956         }
4957         if(e){
4958             e.stopEvent();
4959         }
4960     };
4961
4962     return {
4963         /**
4964          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4965          * @return {Roo.BasicDialog} The BasicDialog element
4966          */
4967         getDialog : function(){
4968            if(!dlg){
4969                 dlg = new Roo.bootstrap.Modal( {
4970                     //draggable: true,
4971                     //resizable:false,
4972                     //constraintoviewport:false,
4973                     //fixedcenter:true,
4974                     //collapsible : false,
4975                     //shim:true,
4976                     //modal: true,
4977                 //    width: 'auto',
4978                   //  height:100,
4979                     //buttonAlign:"center",
4980                     closeClick : function(){
4981                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4982                             handleButton("no");
4983                         }else{
4984                             handleButton("cancel");
4985                         }
4986                     }
4987                 });
4988                 dlg.render();
4989                 dlg.on("hide", handleHide);
4990                 mask = dlg.mask;
4991                 //dlg.addKeyListener(27, handleEsc);
4992                 buttons = {};
4993                 this.buttons = buttons;
4994                 var bt = this.buttonText;
4995                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4996                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4997                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4998                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4999                 //Roo.log(buttons);
5000                 bodyEl = dlg.bodyEl.createChild({
5001
5002                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5003                         '<textarea class="roo-mb-textarea"></textarea>' +
5004                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5005                 });
5006                 msgEl = bodyEl.dom.firstChild;
5007                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5008                 textboxEl.enableDisplayMode();
5009                 textboxEl.addKeyListener([10,13], function(){
5010                     if(dlg.isVisible() && opt && opt.buttons){
5011                         if(opt.buttons.ok){
5012                             handleButton("ok");
5013                         }else if(opt.buttons.yes){
5014                             handleButton("yes");
5015                         }
5016                     }
5017                 });
5018                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5019                 textareaEl.enableDisplayMode();
5020                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5021                 progressEl.enableDisplayMode();
5022                 
5023                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5024                 var pf = progressEl.dom.firstChild;
5025                 if (pf) {
5026                     pp = Roo.get(pf.firstChild);
5027                     pp.setHeight(pf.offsetHeight);
5028                 }
5029                 
5030             }
5031             return dlg;
5032         },
5033
5034         /**
5035          * Updates the message box body text
5036          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5037          * the XHTML-compliant non-breaking space character '&amp;#160;')
5038          * @return {Roo.MessageBox} This message box
5039          */
5040         updateText : function(text)
5041         {
5042             if(!dlg.isVisible() && !opt.width){
5043                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5044                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5045             }
5046             msgEl.innerHTML = text || '&#160;';
5047       
5048             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5049             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5050             var w = Math.max(
5051                     Math.min(opt.width || cw , this.maxWidth), 
5052                     Math.max(opt.minWidth || this.minWidth, bwidth)
5053             );
5054             if(opt.prompt){
5055                 activeTextEl.setWidth(w);
5056             }
5057             if(dlg.isVisible()){
5058                 dlg.fixedcenter = false;
5059             }
5060             // to big, make it scroll. = But as usual stupid IE does not support
5061             // !important..
5062             
5063             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5064                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5065                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5066             } else {
5067                 bodyEl.dom.style.height = '';
5068                 bodyEl.dom.style.overflowY = '';
5069             }
5070             if (cw > w) {
5071                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5072             } else {
5073                 bodyEl.dom.style.overflowX = '';
5074             }
5075             
5076             dlg.setContentSize(w, bodyEl.getHeight());
5077             if(dlg.isVisible()){
5078                 dlg.fixedcenter = true;
5079             }
5080             return this;
5081         },
5082
5083         /**
5084          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5085          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5086          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5087          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5088          * @return {Roo.MessageBox} This message box
5089          */
5090         updateProgress : function(value, text){
5091             if(text){
5092                 this.updateText(text);
5093             }
5094             
5095             if (pp) { // weird bug on my firefox - for some reason this is not defined
5096                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5097                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5098             }
5099             return this;
5100         },        
5101
5102         /**
5103          * Returns true if the message box is currently displayed
5104          * @return {Boolean} True if the message box is visible, else false
5105          */
5106         isVisible : function(){
5107             return dlg && dlg.isVisible();  
5108         },
5109
5110         /**
5111          * Hides the message box if it is displayed
5112          */
5113         hide : function(){
5114             if(this.isVisible()){
5115                 dlg.hide();
5116             }  
5117         },
5118
5119         /**
5120          * Displays a new message box, or reinitializes an existing message box, based on the config options
5121          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5122          * The following config object properties are supported:
5123          * <pre>
5124 Property    Type             Description
5125 ----------  ---------------  ------------------------------------------------------------------------------------
5126 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5127                                    closes (defaults to undefined)
5128 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5129                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5130 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5131                                    progress and wait dialogs will ignore this property and always hide the
5132                                    close button as they can only be closed programmatically.
5133 cls               String           A custom CSS class to apply to the message box element
5134 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5135                                    displayed (defaults to 75)
5136 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5137                                    function will be btn (the name of the button that was clicked, if applicable,
5138                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5139                                    Progress and wait dialogs will ignore this option since they do not respond to
5140                                    user actions and can only be closed programmatically, so any required function
5141                                    should be called by the same code after it closes the dialog.
5142 icon              String           A CSS class that provides a background image to be used as an icon for
5143                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5144 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5145 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5146 modal             Boolean          False to allow user interaction with the page while the message box is
5147                                    displayed (defaults to true)
5148 msg               String           A string that will replace the existing message box body text (defaults
5149                                    to the XHTML-compliant non-breaking space character '&#160;')
5150 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5151 progress          Boolean          True to display a progress bar (defaults to false)
5152 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5153 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5154 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5155 title             String           The title text
5156 value             String           The string value to set into the active textbox element if displayed
5157 wait              Boolean          True to display a progress bar (defaults to false)
5158 width             Number           The width of the dialog in pixels
5159 </pre>
5160          *
5161          * Example usage:
5162          * <pre><code>
5163 Roo.Msg.show({
5164    title: 'Address',
5165    msg: 'Please enter your address:',
5166    width: 300,
5167    buttons: Roo.MessageBox.OKCANCEL,
5168    multiline: true,
5169    fn: saveAddress,
5170    animEl: 'addAddressBtn'
5171 });
5172 </code></pre>
5173          * @param {Object} config Configuration options
5174          * @return {Roo.MessageBox} This message box
5175          */
5176         show : function(options)
5177         {
5178             
5179             // this causes nightmares if you show one dialog after another
5180             // especially on callbacks..
5181              
5182             if(this.isVisible()){
5183                 
5184                 this.hide();
5185                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5186                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5187                 Roo.log("New Dialog Message:" +  options.msg )
5188                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5189                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5190                 
5191             }
5192             var d = this.getDialog();
5193             opt = options;
5194             d.setTitle(opt.title || "&#160;");
5195             d.closeEl.setDisplayed(opt.closable !== false);
5196             activeTextEl = textboxEl;
5197             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5198             if(opt.prompt){
5199                 if(opt.multiline){
5200                     textboxEl.hide();
5201                     textareaEl.show();
5202                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5203                         opt.multiline : this.defaultTextHeight);
5204                     activeTextEl = textareaEl;
5205                 }else{
5206                     textboxEl.show();
5207                     textareaEl.hide();
5208                 }
5209             }else{
5210                 textboxEl.hide();
5211                 textareaEl.hide();
5212             }
5213             progressEl.setDisplayed(opt.progress === true);
5214             if (opt.progress) {
5215                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5216             }
5217             this.updateProgress(0);
5218             activeTextEl.dom.value = opt.value || "";
5219             if(opt.prompt){
5220                 dlg.setDefaultButton(activeTextEl);
5221             }else{
5222                 var bs = opt.buttons;
5223                 var db = null;
5224                 if(bs && bs.ok){
5225                     db = buttons["ok"];
5226                 }else if(bs && bs.yes){
5227                     db = buttons["yes"];
5228                 }
5229                 dlg.setDefaultButton(db);
5230             }
5231             bwidth = updateButtons(opt.buttons);
5232             this.updateText(opt.msg);
5233             if(opt.cls){
5234                 d.el.addClass(opt.cls);
5235             }
5236             d.proxyDrag = opt.proxyDrag === true;
5237             d.modal = opt.modal !== false;
5238             d.mask = opt.modal !== false ? mask : false;
5239             if(!d.isVisible()){
5240                 // force it to the end of the z-index stack so it gets a cursor in FF
5241                 document.body.appendChild(dlg.el.dom);
5242                 d.animateTarget = null;
5243                 d.show(options.animEl);
5244             }
5245             return this;
5246         },
5247
5248         /**
5249          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5250          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5251          * and closing the message box when the process is complete.
5252          * @param {String} title The title bar text
5253          * @param {String} msg The message box body text
5254          * @return {Roo.MessageBox} This message box
5255          */
5256         progress : function(title, msg){
5257             this.show({
5258                 title : title,
5259                 msg : msg,
5260                 buttons: false,
5261                 progress:true,
5262                 closable:false,
5263                 minWidth: this.minProgressWidth,
5264                 modal : true
5265             });
5266             return this;
5267         },
5268
5269         /**
5270          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5271          * If a callback function is passed it will be called after the user clicks the button, and the
5272          * id of the button that was clicked will be passed as the only parameter to the callback
5273          * (could also be the top-right close button).
5274          * @param {String} title The title bar text
5275          * @param {String} msg The message box body text
5276          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5277          * @param {Object} scope (optional) The scope of the callback function
5278          * @return {Roo.MessageBox} This message box
5279          */
5280         alert : function(title, msg, fn, scope)
5281         {
5282             this.show({
5283                 title : title,
5284                 msg : msg,
5285                 buttons: this.OK,
5286                 fn: fn,
5287                 closable : false,
5288                 scope : scope,
5289                 modal : true
5290             });
5291             return this;
5292         },
5293
5294         /**
5295          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5296          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5297          * You are responsible for closing the message box when the process is complete.
5298          * @param {String} msg The message box body text
5299          * @param {String} title (optional) The title bar text
5300          * @return {Roo.MessageBox} This message box
5301          */
5302         wait : function(msg, title){
5303             this.show({
5304                 title : title,
5305                 msg : msg,
5306                 buttons: false,
5307                 closable:false,
5308                 progress:true,
5309                 modal:true,
5310                 width:300,
5311                 wait:true
5312             });
5313             waitTimer = Roo.TaskMgr.start({
5314                 run: function(i){
5315                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5316                 },
5317                 interval: 1000
5318             });
5319             return this;
5320         },
5321
5322         /**
5323          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5324          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5325          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5326          * @param {String} title The title bar text
5327          * @param {String} msg The message box body text
5328          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5329          * @param {Object} scope (optional) The scope of the callback function
5330          * @return {Roo.MessageBox} This message box
5331          */
5332         confirm : function(title, msg, fn, scope){
5333             this.show({
5334                 title : title,
5335                 msg : msg,
5336                 buttons: this.YESNO,
5337                 fn: fn,
5338                 scope : scope,
5339                 modal : true
5340             });
5341             return this;
5342         },
5343
5344         /**
5345          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5346          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5347          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5348          * (could also be the top-right close button) and the text that was entered will be passed as the two
5349          * parameters to the callback.
5350          * @param {String} title The title bar text
5351          * @param {String} msg The message box body text
5352          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5353          * @param {Object} scope (optional) The scope of the callback function
5354          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5355          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5356          * @return {Roo.MessageBox} This message box
5357          */
5358         prompt : function(title, msg, fn, scope, multiline){
5359             this.show({
5360                 title : title,
5361                 msg : msg,
5362                 buttons: this.OKCANCEL,
5363                 fn: fn,
5364                 minWidth:250,
5365                 scope : scope,
5366                 prompt:true,
5367                 multiline: multiline,
5368                 modal : true
5369             });
5370             return this;
5371         },
5372
5373         /**
5374          * Button config that displays a single OK button
5375          * @type Object
5376          */
5377         OK : {ok:true},
5378         /**
5379          * Button config that displays Yes and No buttons
5380          * @type Object
5381          */
5382         YESNO : {yes:true, no:true},
5383         /**
5384          * Button config that displays OK and Cancel buttons
5385          * @type Object
5386          */
5387         OKCANCEL : {ok:true, cancel:true},
5388         /**
5389          * Button config that displays Yes, No and Cancel buttons
5390          * @type Object
5391          */
5392         YESNOCANCEL : {yes:true, no:true, cancel:true},
5393
5394         /**
5395          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5396          * @type Number
5397          */
5398         defaultTextHeight : 75,
5399         /**
5400          * The maximum width in pixels of the message box (defaults to 600)
5401          * @type Number
5402          */
5403         maxWidth : 600,
5404         /**
5405          * The minimum width in pixels of the message box (defaults to 100)
5406          * @type Number
5407          */
5408         minWidth : 100,
5409         /**
5410          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5411          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5412          * @type Number
5413          */
5414         minProgressWidth : 250,
5415         /**
5416          * An object containing the default button text strings that can be overriden for localized language support.
5417          * Supported properties are: ok, cancel, yes and no.
5418          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5419          * @type Object
5420          */
5421         buttonText : {
5422             ok : "OK",
5423             cancel : "Cancel",
5424             yes : "Yes",
5425             no : "No"
5426         }
5427     };
5428 }();
5429
5430 /**
5431  * Shorthand for {@link Roo.MessageBox}
5432  */
5433 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5434 Roo.Msg = Roo.Msg || Roo.MessageBox;
5435 /*
5436  * - LGPL
5437  *
5438  * navbar
5439  * 
5440  */
5441
5442 /**
5443  * @class Roo.bootstrap.Navbar
5444  * @extends Roo.bootstrap.Component
5445  * Bootstrap Navbar class
5446
5447  * @constructor
5448  * Create a new Navbar
5449  * @param {Object} config The config object
5450  */
5451
5452
5453 Roo.bootstrap.Navbar = function(config){
5454     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5455     this.addEvents({
5456         // raw events
5457         /**
5458          * @event beforetoggle
5459          * Fire before toggle the menu
5460          * @param {Roo.EventObject} e
5461          */
5462         "beforetoggle" : true
5463     });
5464 };
5465
5466 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5467     
5468     
5469    
5470     // private
5471     navItems : false,
5472     loadMask : false,
5473     
5474     
5475     getAutoCreate : function(){
5476         
5477         
5478         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5479         
5480     },
5481     
5482     initEvents :function ()
5483     {
5484         //Roo.log(this.el.select('.navbar-toggle',true));
5485         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5486         
5487         var mark = {
5488             tag: "div",
5489             cls:"x-dlg-mask"
5490         };
5491         
5492         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5493         
5494         var size = this.el.getSize();
5495         this.maskEl.setSize(size.width, size.height);
5496         this.maskEl.enableDisplayMode("block");
5497         this.maskEl.hide();
5498         
5499         if(this.loadMask){
5500             this.maskEl.show();
5501         }
5502     },
5503     
5504     
5505     getChildContainer : function()
5506     {
5507         if (this.el && this.el.select('.collapse').getCount()) {
5508             return this.el.select('.collapse',true).first();
5509         }
5510         
5511         return this.el;
5512     },
5513     
5514     mask : function()
5515     {
5516         this.maskEl.show();
5517     },
5518     
5519     unmask : function()
5520     {
5521         this.maskEl.hide();
5522     },
5523     onToggle : function()
5524     {
5525         
5526         if(this.fireEvent('beforetoggle', this) === false){
5527             return;
5528         }
5529         var ce = this.el.select('.navbar-collapse',true).first();
5530       
5531         if (!ce.hasClass('show')) {
5532            this.expand();
5533         } else {
5534             this.collapse();
5535         }
5536         
5537         
5538     
5539     },
5540     /**
5541      * Expand the navbar pulldown 
5542      */
5543     expand : function ()
5544     {
5545        
5546         var ce = this.el.select('.navbar-collapse',true).first();
5547         if (ce.hasClass('collapsing')) {
5548             return;
5549         }
5550         ce.dom.style.height = '';
5551                // show it...
5552         ce.addClass('in'); // old...
5553         ce.removeClass('collapse');
5554         ce.addClass('show');
5555         var h = ce.getHeight();
5556         Roo.log(h);
5557         ce.removeClass('show');
5558         // at this point we should be able to see it..
5559         ce.addClass('collapsing');
5560         
5561         ce.setHeight(0); // resize it ...
5562         ce.on('transitionend', function() {
5563             //Roo.log('done transition');
5564             ce.removeClass('collapsing');
5565             ce.addClass('show');
5566             ce.removeClass('collapse');
5567
5568             ce.dom.style.height = '';
5569         }, this, { single: true} );
5570         ce.setHeight(h);
5571         ce.dom.scrollTop = 0;
5572     },
5573     /**
5574      * Collapse the navbar pulldown 
5575      */
5576     collapse : function()
5577     {
5578          var ce = this.el.select('.navbar-collapse',true).first();
5579        
5580         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5581             // it's collapsed or collapsing..
5582             return;
5583         }
5584         ce.removeClass('in'); // old...
5585         ce.setHeight(ce.getHeight());
5586         ce.removeClass('show');
5587         ce.addClass('collapsing');
5588         
5589         ce.on('transitionend', function() {
5590             ce.dom.style.height = '';
5591             ce.removeClass('collapsing');
5592             ce.addClass('collapse');
5593         }, this, { single: true} );
5594         ce.setHeight(0);
5595     }
5596     
5597     
5598     
5599 });
5600
5601
5602
5603  
5604
5605  /*
5606  * - LGPL
5607  *
5608  * navbar
5609  * 
5610  */
5611
5612 /**
5613  * @class Roo.bootstrap.NavSimplebar
5614  * @extends Roo.bootstrap.Navbar
5615  * Bootstrap Sidebar class
5616  *
5617  * @cfg {Boolean} inverse is inverted color
5618  * 
5619  * @cfg {String} type (nav | pills | tabs)
5620  * @cfg {Boolean} arrangement stacked | justified
5621  * @cfg {String} align (left | right) alignment
5622  * 
5623  * @cfg {Boolean} main (true|false) main nav bar? default false
5624  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5625  * 
5626  * @cfg {String} tag (header|footer|nav|div) default is nav 
5627
5628  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5629  * 
5630  * 
5631  * @constructor
5632  * Create a new Sidebar
5633  * @param {Object} config The config object
5634  */
5635
5636
5637 Roo.bootstrap.NavSimplebar = function(config){
5638     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5639 };
5640
5641 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5642     
5643     inverse: false,
5644     
5645     type: false,
5646     arrangement: '',
5647     align : false,
5648     
5649     weight : 'light',
5650     
5651     main : false,
5652     
5653     
5654     tag : false,
5655     
5656     
5657     getAutoCreate : function(){
5658         
5659         
5660         var cfg = {
5661             tag : this.tag || 'div',
5662             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5663         };
5664         if (['light','white'].indexOf(this.weight) > -1) {
5665             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5666         }
5667         cfg.cls += ' bg-' + this.weight;
5668         
5669         if (this.inverse) {
5670             cfg.cls += ' navbar-inverse';
5671             
5672         }
5673         
5674         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5675         
5676         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5677             return cfg;
5678         }
5679         
5680         
5681     
5682         
5683         cfg.cn = [
5684             {
5685                 cls: 'nav nav-' + this.xtype,
5686                 tag : 'ul'
5687             }
5688         ];
5689         
5690          
5691         this.type = this.type || 'nav';
5692         if (['tabs','pills'].indexOf(this.type) != -1) {
5693             cfg.cn[0].cls += ' nav-' + this.type
5694         
5695         
5696         } else {
5697             if (this.type!=='nav') {
5698                 Roo.log('nav type must be nav/tabs/pills')
5699             }
5700             cfg.cn[0].cls += ' navbar-nav'
5701         }
5702         
5703         
5704         
5705         
5706         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5707             cfg.cn[0].cls += ' nav-' + this.arrangement;
5708         }
5709         
5710         
5711         if (this.align === 'right') {
5712             cfg.cn[0].cls += ' navbar-right';
5713         }
5714         
5715         
5716         
5717         
5718         return cfg;
5719     
5720         
5721     }
5722     
5723     
5724     
5725 });
5726
5727
5728
5729  
5730
5731  
5732        /*
5733  * - LGPL
5734  *
5735  * navbar
5736  * navbar-fixed-top
5737  * navbar-expand-md  fixed-top 
5738  */
5739
5740 /**
5741  * @class Roo.bootstrap.NavHeaderbar
5742  * @extends Roo.bootstrap.NavSimplebar
5743  * Bootstrap Sidebar class
5744  *
5745  * @cfg {String} brand what is brand
5746  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5747  * @cfg {String} brand_href href of the brand
5748  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5749  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5750  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5751  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5752  * 
5753  * @constructor
5754  * Create a new Sidebar
5755  * @param {Object} config The config object
5756  */
5757
5758
5759 Roo.bootstrap.NavHeaderbar = function(config){
5760     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5761       
5762 };
5763
5764 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5765     
5766     position: '',
5767     brand: '',
5768     brand_href: false,
5769     srButton : true,
5770     autohide : false,
5771     desktopCenter : false,
5772    
5773     
5774     getAutoCreate : function(){
5775         
5776         var   cfg = {
5777             tag: this.nav || 'nav',
5778             cls: 'navbar navbar-expand-md',
5779             role: 'navigation',
5780             cn: []
5781         };
5782         
5783         var cn = cfg.cn;
5784         if (this.desktopCenter) {
5785             cn.push({cls : 'container', cn : []});
5786             cn = cn[0].cn;
5787         }
5788         
5789         if(this.srButton){
5790             var btn = {
5791                 tag: 'button',
5792                 type: 'button',
5793                 cls: 'navbar-toggle navbar-toggler',
5794                 'data-toggle': 'collapse',
5795                 cn: [
5796                     {
5797                         tag: 'span',
5798                         cls: 'sr-only',
5799                         html: 'Toggle navigation'
5800                     },
5801                     {
5802                         tag: 'span',
5803                         cls: 'icon-bar navbar-toggler-icon'
5804                     },
5805                     {
5806                         tag: 'span',
5807                         cls: 'icon-bar'
5808                     },
5809                     {
5810                         tag: 'span',
5811                         cls: 'icon-bar'
5812                     }
5813                 ]
5814             };
5815             
5816             cn.push( Roo.bootstrap.version == 4 ? btn : {
5817                 tag: 'div',
5818                 cls: 'navbar-header',
5819                 cn: [
5820                     btn
5821                 ]
5822             });
5823         }
5824         
5825         cn.push({
5826             tag: 'div',
5827             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5828             cn : []
5829         });
5830         
5831         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5832         
5833         if (['light','white'].indexOf(this.weight) > -1) {
5834             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5835         }
5836         cfg.cls += ' bg-' + this.weight;
5837         
5838         
5839         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5840             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5841             
5842             // tag can override this..
5843             
5844             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5845         }
5846         
5847         if (this.brand !== '') {
5848             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5849             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5850                 tag: 'a',
5851                 href: this.brand_href ? this.brand_href : '#',
5852                 cls: 'navbar-brand',
5853                 cn: [
5854                 this.brand
5855                 ]
5856             });
5857         }
5858         
5859         if(this.main){
5860             cfg.cls += ' main-nav';
5861         }
5862         
5863         
5864         return cfg;
5865
5866         
5867     },
5868     getHeaderChildContainer : function()
5869     {
5870         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5871             return this.el.select('.navbar-header',true).first();
5872         }
5873         
5874         return this.getChildContainer();
5875     },
5876     
5877     getChildContainer : function()
5878     {
5879          
5880         return this.el.select('.roo-navbar-collapse',true).first();
5881          
5882         
5883     },
5884     
5885     initEvents : function()
5886     {
5887         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5888         
5889         if (this.autohide) {
5890             
5891             var prevScroll = 0;
5892             var ft = this.el;
5893             
5894             Roo.get(document).on('scroll',function(e) {
5895                 var ns = Roo.get(document).getScroll().top;
5896                 var os = prevScroll;
5897                 prevScroll = ns;
5898                 
5899                 if(ns > os){
5900                     ft.removeClass('slideDown');
5901                     ft.addClass('slideUp');
5902                     return;
5903                 }
5904                 ft.removeClass('slideUp');
5905                 ft.addClass('slideDown');
5906                  
5907               
5908           },this);
5909         }
5910     }    
5911     
5912 });
5913
5914
5915
5916  
5917
5918  /*
5919  * - LGPL
5920  *
5921  * navbar
5922  * 
5923  */
5924
5925 /**
5926  * @class Roo.bootstrap.NavSidebar
5927  * @extends Roo.bootstrap.Navbar
5928  * Bootstrap Sidebar class
5929  * 
5930  * @constructor
5931  * Create a new Sidebar
5932  * @param {Object} config The config object
5933  */
5934
5935
5936 Roo.bootstrap.NavSidebar = function(config){
5937     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5938 };
5939
5940 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5941     
5942     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5943     
5944     getAutoCreate : function(){
5945         
5946         
5947         return  {
5948             tag: 'div',
5949             cls: 'sidebar sidebar-nav'
5950         };
5951     
5952         
5953     }
5954     
5955     
5956     
5957 });
5958
5959
5960
5961  
5962
5963  /*
5964  * - LGPL
5965  *
5966  * nav group
5967  * 
5968  */
5969
5970 /**
5971  * @class Roo.bootstrap.NavGroup
5972  * @extends Roo.bootstrap.Component
5973  * Bootstrap NavGroup class
5974  * @cfg {String} align (left|right)
5975  * @cfg {Boolean} inverse
5976  * @cfg {String} type (nav|pills|tab) default nav
5977  * @cfg {String} navId - reference Id for navbar.
5978  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5979  * 
5980  * @constructor
5981  * Create a new nav group
5982  * @param {Object} config The config object
5983  */
5984
5985 Roo.bootstrap.NavGroup = function(config){
5986     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5987     this.navItems = [];
5988    
5989     Roo.bootstrap.NavGroup.register(this);
5990      this.addEvents({
5991         /**
5992              * @event changed
5993              * Fires when the active item changes
5994              * @param {Roo.bootstrap.NavGroup} this
5995              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5996              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5997          */
5998         'changed': true
5999      });
6000     
6001 };
6002
6003 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6004     
6005     align: '',
6006     inverse: false,
6007     form: false,
6008     type: 'nav',
6009     navId : '',
6010     // private
6011     pilltype : true,
6012     
6013     navItems : false, 
6014     
6015     getAutoCreate : function()
6016     {
6017         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6018         
6019         cfg = {
6020             tag : 'ul',
6021             cls: 'nav' 
6022         };
6023         if (Roo.bootstrap.version == 4) {
6024             if (['tabs','pills'].indexOf(this.type) != -1) {
6025                 cfg.cls += ' nav-' + this.type; 
6026             } else {
6027                 // trying to remove so header bar can right align top?
6028                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6029                     // do not use on header bar... 
6030                     cfg.cls += ' navbar-nav';
6031                 }
6032             }
6033             
6034         } else {
6035             if (['tabs','pills'].indexOf(this.type) != -1) {
6036                 cfg.cls += ' nav-' + this.type
6037             } else {
6038                 if (this.type !== 'nav') {
6039                     Roo.log('nav type must be nav/tabs/pills')
6040                 }
6041                 cfg.cls += ' navbar-nav'
6042             }
6043         }
6044         
6045         if (this.parent() && this.parent().sidebar) {
6046             cfg = {
6047                 tag: 'ul',
6048                 cls: 'dashboard-menu sidebar-menu'
6049             };
6050             
6051             return cfg;
6052         }
6053         
6054         if (this.form === true) {
6055             cfg = {
6056                 tag: 'form',
6057                 cls: 'navbar-form form-inline'
6058             };
6059             //nav navbar-right ml-md-auto
6060             if (this.align === 'right') {
6061                 cfg.cls += ' navbar-right ml-md-auto';
6062             } else {
6063                 cfg.cls += ' navbar-left';
6064             }
6065         }
6066         
6067         if (this.align === 'right') {
6068             cfg.cls += ' navbar-right ml-md-auto';
6069         } else {
6070             cfg.cls += ' mr-auto';
6071         }
6072         
6073         if (this.inverse) {
6074             cfg.cls += ' navbar-inverse';
6075             
6076         }
6077         
6078         
6079         return cfg;
6080     },
6081     /**
6082     * sets the active Navigation item
6083     * @param {Roo.bootstrap.NavItem} the new current navitem
6084     */
6085     setActiveItem : function(item)
6086     {
6087         var prev = false;
6088         Roo.each(this.navItems, function(v){
6089             if (v == item) {
6090                 return ;
6091             }
6092             if (v.isActive()) {
6093                 v.setActive(false, true);
6094                 prev = v;
6095                 
6096             }
6097             
6098         });
6099
6100         item.setActive(true, true);
6101         this.fireEvent('changed', this, item, prev);
6102         
6103         
6104     },
6105     /**
6106     * gets the active Navigation item
6107     * @return {Roo.bootstrap.NavItem} the current navitem
6108     */
6109     getActive : function()
6110     {
6111         
6112         var prev = false;
6113         Roo.each(this.navItems, function(v){
6114             
6115             if (v.isActive()) {
6116                 prev = v;
6117                 
6118             }
6119             
6120         });
6121         return prev;
6122     },
6123     
6124     indexOfNav : function()
6125     {
6126         
6127         var prev = false;
6128         Roo.each(this.navItems, function(v,i){
6129             
6130             if (v.isActive()) {
6131                 prev = i;
6132                 
6133             }
6134             
6135         });
6136         return prev;
6137     },
6138     /**
6139     * adds a Navigation item
6140     * @param {Roo.bootstrap.NavItem} the navitem to add
6141     */
6142     addItem : function(cfg)
6143     {
6144         if (this.form && Roo.bootstrap.version == 4) {
6145             cfg.tag = 'div';
6146         }
6147         var cn = new Roo.bootstrap.NavItem(cfg);
6148         this.register(cn);
6149         cn.parentId = this.id;
6150         cn.onRender(this.el, null);
6151         return cn;
6152     },
6153     /**
6154     * register a Navigation item
6155     * @param {Roo.bootstrap.NavItem} the navitem to add
6156     */
6157     register : function(item)
6158     {
6159         this.navItems.push( item);
6160         item.navId = this.navId;
6161     
6162     },
6163     
6164     /**
6165     * clear all the Navigation item
6166     */
6167    
6168     clearAll : function()
6169     {
6170         this.navItems = [];
6171         this.el.dom.innerHTML = '';
6172     },
6173     
6174     getNavItem: function(tabId)
6175     {
6176         var ret = false;
6177         Roo.each(this.navItems, function(e) {
6178             if (e.tabId == tabId) {
6179                ret =  e;
6180                return false;
6181             }
6182             return true;
6183             
6184         });
6185         return ret;
6186     },
6187     
6188     setActiveNext : function()
6189     {
6190         var i = this.indexOfNav(this.getActive());
6191         if (i > this.navItems.length) {
6192             return;
6193         }
6194         this.setActiveItem(this.navItems[i+1]);
6195     },
6196     setActivePrev : function()
6197     {
6198         var i = this.indexOfNav(this.getActive());
6199         if (i  < 1) {
6200             return;
6201         }
6202         this.setActiveItem(this.navItems[i-1]);
6203     },
6204     clearWasActive : function(except) {
6205         Roo.each(this.navItems, function(e) {
6206             if (e.tabId != except.tabId && e.was_active) {
6207                e.was_active = false;
6208                return false;
6209             }
6210             return true;
6211             
6212         });
6213     },
6214     getWasActive : function ()
6215     {
6216         var r = false;
6217         Roo.each(this.navItems, function(e) {
6218             if (e.was_active) {
6219                r = e;
6220                return false;
6221             }
6222             return true;
6223             
6224         });
6225         return r;
6226     }
6227     
6228     
6229 });
6230
6231  
6232 Roo.apply(Roo.bootstrap.NavGroup, {
6233     
6234     groups: {},
6235      /**
6236     * register a Navigation Group
6237     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6238     */
6239     register : function(navgrp)
6240     {
6241         this.groups[navgrp.navId] = navgrp;
6242         
6243     },
6244     /**
6245     * fetch a Navigation Group based on the navigation ID
6246     * @param {string} the navgroup to add
6247     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6248     */
6249     get: function(navId) {
6250         if (typeof(this.groups[navId]) == 'undefined') {
6251             return false;
6252             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6253         }
6254         return this.groups[navId] ;
6255     }
6256     
6257     
6258     
6259 });
6260
6261  /*
6262  * - LGPL
6263  *
6264  * row
6265  * 
6266  */
6267
6268 /**
6269  * @class Roo.bootstrap.NavItem
6270  * @extends Roo.bootstrap.Component
6271  * Bootstrap Navbar.NavItem class
6272  * @cfg {String} href  link to
6273  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6274  * @cfg {Boolean} button_outline show and outlined button
6275  * @cfg {String} html content of button
6276  * @cfg {String} badge text inside badge
6277  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6278  * @cfg {String} glyphicon DEPRICATED - use fa
6279  * @cfg {String} icon DEPRICATED - use fa
6280  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6281  * @cfg {Boolean} active Is item active
6282  * @cfg {Boolean} disabled Is item disabled
6283  * @cfg {String} linkcls  Link Class
6284  * @cfg {Boolean} preventDefault (true | false) default false
6285  * @cfg {String} tabId the tab that this item activates.
6286  * @cfg {String} tagtype (a|span) render as a href or span?
6287  * @cfg {Boolean} animateRef (true|false) link to element default false  
6288   
6289  * @constructor
6290  * Create a new Navbar Item
6291  * @param {Object} config The config object
6292  */
6293 Roo.bootstrap.NavItem = function(config){
6294     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6295     this.addEvents({
6296         // raw events
6297         /**
6298          * @event click
6299          * The raw click event for the entire grid.
6300          * @param {Roo.EventObject} e
6301          */
6302         "click" : true,
6303          /**
6304             * @event changed
6305             * Fires when the active item active state changes
6306             * @param {Roo.bootstrap.NavItem} this
6307             * @param {boolean} state the new state
6308              
6309          */
6310         'changed': true,
6311         /**
6312             * @event scrollto
6313             * Fires when scroll to element
6314             * @param {Roo.bootstrap.NavItem} this
6315             * @param {Object} options
6316             * @param {Roo.EventObject} e
6317              
6318          */
6319         'scrollto': true
6320     });
6321    
6322 };
6323
6324 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6325     
6326     href: false,
6327     html: '',
6328     badge: '',
6329     icon: false,
6330     fa : false,
6331     glyphicon: false,
6332     active: false,
6333     preventDefault : false,
6334     tabId : false,
6335     tagtype : 'a',
6336     tag: 'li',
6337     disabled : false,
6338     animateRef : false,
6339     was_active : false,
6340     button_weight : '',
6341     button_outline : false,
6342     linkcls : '',
6343     navLink: false,
6344     
6345     getAutoCreate : function(){
6346          
6347         var cfg = {
6348             tag: this.tag,
6349             cls: 'nav-item'
6350         };
6351         
6352         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6353         
6354         if (this.active) {
6355             cfg.cls +=  ' active' ;
6356         }
6357         if (this.disabled) {
6358             cfg.cls += ' disabled';
6359         }
6360         
6361         // BS4 only?
6362         if (this.button_weight.length) {
6363             cfg.tag = this.href ? 'a' : 'button';
6364             cfg.html = this.html || '';
6365             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6366             if (this.href) {
6367                 cfg.href = this.href;
6368             }
6369             if (this.fa) {
6370                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6371             } else {
6372                 cfg.cls += " nav-html";
6373             }
6374             
6375             // menu .. should add dropdown-menu class - so no need for carat..
6376             
6377             if (this.badge !== '') {
6378                  
6379                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6380             }
6381             return cfg;
6382         }
6383         
6384         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6385             cfg.cn = [
6386                 {
6387                     tag: this.tagtype,
6388                     href : this.href || "#",
6389                     html: this.html || '',
6390                     cls : ''
6391                 }
6392             ];
6393             if (this.tagtype == 'a') {
6394                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6395         
6396             }
6397             if (this.icon) {
6398                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6399             } else  if (this.fa) {
6400                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6401             } else if(this.glyphicon) {
6402                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6403             } else {
6404                 cfg.cn[0].cls += " nav-html";
6405             }
6406             
6407             if (this.menu) {
6408                 cfg.cn[0].html += " <span class='caret'></span>";
6409              
6410             }
6411             
6412             if (this.badge !== '') {
6413                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6414             }
6415         }
6416         
6417         
6418         
6419         return cfg;
6420     },
6421     onRender : function(ct, position)
6422     {
6423        // Roo.log("Call onRender: " + this.xtype);
6424         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6425             this.tag = 'div';
6426         }
6427         
6428         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6429         this.navLink = this.el.select('.nav-link',true).first();
6430         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6431         return ret;
6432     },
6433       
6434     
6435     initEvents: function() 
6436     {
6437         if (typeof (this.menu) != 'undefined') {
6438             this.menu.parentType = this.xtype;
6439             this.menu.triggerEl = this.el;
6440             this.menu = this.addxtype(Roo.apply({}, this.menu));
6441         }
6442         
6443         this.el.on('click', this.onClick, this);
6444         
6445         //if(this.tagtype == 'span'){
6446         //    this.el.select('span',true).on('click', this.onClick, this);
6447         //}
6448        
6449         // at this point parent should be available..
6450         this.parent().register(this);
6451     },
6452     
6453     onClick : function(e)
6454     {
6455         if (e.getTarget('.dropdown-menu-item')) {
6456             // did you click on a menu itemm.... - then don't trigger onclick..
6457             return;
6458         }
6459         
6460         if(
6461                 this.preventDefault || 
6462                 this.href == '#' 
6463         ){
6464             Roo.log("NavItem - prevent Default?");
6465             e.preventDefault();
6466         }
6467         
6468         if (this.disabled) {
6469             return;
6470         }
6471         
6472         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6473         if (tg && tg.transition) {
6474             Roo.log("waiting for the transitionend");
6475             return;
6476         }
6477         
6478         
6479         
6480         //Roo.log("fire event clicked");
6481         if(this.fireEvent('click', this, e) === false){
6482             return;
6483         };
6484         
6485         if(this.tagtype == 'span'){
6486             return;
6487         }
6488         
6489         //Roo.log(this.href);
6490         var ael = this.el.select('a',true).first();
6491         //Roo.log(ael);
6492         
6493         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6494             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6495             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6496                 return; // ignore... - it's a 'hash' to another page.
6497             }
6498             Roo.log("NavItem - prevent Default?");
6499             e.preventDefault();
6500             this.scrollToElement(e);
6501         }
6502         
6503         
6504         var p =  this.parent();
6505    
6506         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6507             if (typeof(p.setActiveItem) !== 'undefined') {
6508                 p.setActiveItem(this);
6509             }
6510         }
6511         
6512         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6513         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6514             // remove the collapsed menu expand...
6515             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6516         }
6517     },
6518     
6519     isActive: function () {
6520         return this.active
6521     },
6522     setActive : function(state, fire, is_was_active)
6523     {
6524         if (this.active && !state && this.navId) {
6525             this.was_active = true;
6526             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6527             if (nv) {
6528                 nv.clearWasActive(this);
6529             }
6530             
6531         }
6532         this.active = state;
6533         
6534         if (!state ) {
6535             this.el.removeClass('active');
6536             this.navLink ? this.navLink.removeClass('active') : false;
6537         } else if (!this.el.hasClass('active')) {
6538             
6539             this.el.addClass('active');
6540             if (Roo.bootstrap.version == 4 && this.navLink ) {
6541                 this.navLink.addClass('active');
6542             }
6543             
6544         }
6545         if (fire) {
6546             this.fireEvent('changed', this, state);
6547         }
6548         
6549         // show a panel if it's registered and related..
6550         
6551         if (!this.navId || !this.tabId || !state || is_was_active) {
6552             return;
6553         }
6554         
6555         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6556         if (!tg) {
6557             return;
6558         }
6559         var pan = tg.getPanelByName(this.tabId);
6560         if (!pan) {
6561             return;
6562         }
6563         // if we can not flip to new panel - go back to old nav highlight..
6564         if (false == tg.showPanel(pan)) {
6565             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6566             if (nv) {
6567                 var onav = nv.getWasActive();
6568                 if (onav) {
6569                     onav.setActive(true, false, true);
6570                 }
6571             }
6572             
6573         }
6574         
6575         
6576         
6577     },
6578      // this should not be here...
6579     setDisabled : function(state)
6580     {
6581         this.disabled = state;
6582         if (!state ) {
6583             this.el.removeClass('disabled');
6584         } else if (!this.el.hasClass('disabled')) {
6585             this.el.addClass('disabled');
6586         }
6587         
6588     },
6589     
6590     /**
6591      * Fetch the element to display the tooltip on.
6592      * @return {Roo.Element} defaults to this.el
6593      */
6594     tooltipEl : function()
6595     {
6596         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6597     },
6598     
6599     scrollToElement : function(e)
6600     {
6601         var c = document.body;
6602         
6603         /*
6604          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6605          */
6606         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6607             c = document.documentElement;
6608         }
6609         
6610         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6611         
6612         if(!target){
6613             return;
6614         }
6615
6616         var o = target.calcOffsetsTo(c);
6617         
6618         var options = {
6619             target : target,
6620             value : o[1]
6621         };
6622         
6623         this.fireEvent('scrollto', this, options, e);
6624         
6625         Roo.get(c).scrollTo('top', options.value, true);
6626         
6627         return;
6628     },
6629     /**
6630      * Set the HTML (text content) of the item
6631      * @param {string} html  content for the nav item
6632      */
6633     setHtml : function(html)
6634     {
6635         this.html = html;
6636         this.htmlEl.dom.innerHTML = html;
6637         
6638     } 
6639 });
6640  
6641
6642  /*
6643  * - LGPL
6644  *
6645  * sidebar item
6646  *
6647  *  li
6648  *    <span> icon </span>
6649  *    <span> text </span>
6650  *    <span>badge </span>
6651  */
6652
6653 /**
6654  * @class Roo.bootstrap.NavSidebarItem
6655  * @extends Roo.bootstrap.NavItem
6656  * Bootstrap Navbar.NavSidebarItem class
6657  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6658  * {Boolean} open is the menu open
6659  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6660  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6661  * {String} buttonSize (sm|md|lg)the extra classes for the button
6662  * {Boolean} showArrow show arrow next to the text (default true)
6663  * @constructor
6664  * Create a new Navbar Button
6665  * @param {Object} config The config object
6666  */
6667 Roo.bootstrap.NavSidebarItem = function(config){
6668     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6669     this.addEvents({
6670         // raw events
6671         /**
6672          * @event click
6673          * The raw click event for the entire grid.
6674          * @param {Roo.EventObject} e
6675          */
6676         "click" : true,
6677          /**
6678             * @event changed
6679             * Fires when the active item active state changes
6680             * @param {Roo.bootstrap.NavSidebarItem} this
6681             * @param {boolean} state the new state
6682              
6683          */
6684         'changed': true
6685     });
6686    
6687 };
6688
6689 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6690     
6691     badgeWeight : 'default',
6692     
6693     open: false,
6694     
6695     buttonView : false,
6696     
6697     buttonWeight : 'default',
6698     
6699     buttonSize : 'md',
6700     
6701     showArrow : true,
6702     
6703     getAutoCreate : function(){
6704         
6705         
6706         var a = {
6707                 tag: 'a',
6708                 href : this.href || '#',
6709                 cls: '',
6710                 html : '',
6711                 cn : []
6712         };
6713         
6714         if(this.buttonView){
6715             a = {
6716                 tag: 'button',
6717                 href : this.href || '#',
6718                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6719                 html : this.html,
6720                 cn : []
6721             };
6722         }
6723         
6724         var cfg = {
6725             tag: 'li',
6726             cls: '',
6727             cn: [ a ]
6728         };
6729         
6730         if (this.active) {
6731             cfg.cls += ' active';
6732         }
6733         
6734         if (this.disabled) {
6735             cfg.cls += ' disabled';
6736         }
6737         if (this.open) {
6738             cfg.cls += ' open x-open';
6739         }
6740         // left icon..
6741         if (this.glyphicon || this.icon) {
6742             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6743             a.cn.push({ tag : 'i', cls : c }) ;
6744         }
6745         
6746         if(!this.buttonView){
6747             var span = {
6748                 tag: 'span',
6749                 html : this.html || ''
6750             };
6751
6752             a.cn.push(span);
6753             
6754         }
6755         
6756         if (this.badge !== '') {
6757             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6758         }
6759         
6760         if (this.menu) {
6761             
6762             if(this.showArrow){
6763                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6764             }
6765             
6766             a.cls += ' dropdown-toggle treeview' ;
6767         }
6768         
6769         return cfg;
6770     },
6771     
6772     initEvents : function()
6773     { 
6774         if (typeof (this.menu) != 'undefined') {
6775             this.menu.parentType = this.xtype;
6776             this.menu.triggerEl = this.el;
6777             this.menu = this.addxtype(Roo.apply({}, this.menu));
6778         }
6779         
6780         this.el.on('click', this.onClick, this);
6781         
6782         if(this.badge !== ''){
6783             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6784         }
6785         
6786     },
6787     
6788     onClick : function(e)
6789     {
6790         if(this.disabled){
6791             e.preventDefault();
6792             return;
6793         }
6794         
6795         if(this.preventDefault){
6796             e.preventDefault();
6797         }
6798         
6799         this.fireEvent('click', this, e);
6800     },
6801     
6802     disable : function()
6803     {
6804         this.setDisabled(true);
6805     },
6806     
6807     enable : function()
6808     {
6809         this.setDisabled(false);
6810     },
6811     
6812     setDisabled : function(state)
6813     {
6814         if(this.disabled == state){
6815             return;
6816         }
6817         
6818         this.disabled = state;
6819         
6820         if (state) {
6821             this.el.addClass('disabled');
6822             return;
6823         }
6824         
6825         this.el.removeClass('disabled');
6826         
6827         return;
6828     },
6829     
6830     setActive : function(state)
6831     {
6832         if(this.active == state){
6833             return;
6834         }
6835         
6836         this.active = state;
6837         
6838         if (state) {
6839             this.el.addClass('active');
6840             return;
6841         }
6842         
6843         this.el.removeClass('active');
6844         
6845         return;
6846     },
6847     
6848     isActive: function () 
6849     {
6850         return this.active;
6851     },
6852     
6853     setBadge : function(str)
6854     {
6855         if(!this.badgeEl){
6856             return;
6857         }
6858         
6859         this.badgeEl.dom.innerHTML = str;
6860     }
6861     
6862    
6863      
6864  
6865 });
6866  
6867
6868  /*
6869  * - LGPL
6870  *
6871  *  Breadcrumb Nav
6872  * 
6873  */
6874 Roo.namespace('Roo.bootstrap.breadcrumb');
6875
6876
6877 /**
6878  * @class Roo.bootstrap.breadcrumb.Nav
6879  * @extends Roo.bootstrap.Component
6880  * Bootstrap Breadcrumb Nav Class
6881  *  
6882  * @children Roo.bootstrap.breadcrumb.Item
6883  * 
6884  * @constructor
6885  * Create a new breadcrumb.Nav
6886  * @param {Object} config The config object
6887  */
6888
6889
6890 Roo.bootstrap.breadcrumb.Nav = function(config){
6891     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6892     
6893     
6894 };
6895
6896 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6897     
6898     getAutoCreate : function()
6899     {
6900
6901         var cfg = {
6902             tag: 'nav',
6903             cn : [
6904                 {
6905                     tag : 'ol',
6906                     cls : 'breadcrumb'
6907                 }
6908             ]
6909             
6910         };
6911           
6912         return cfg;
6913     },
6914     
6915     initEvents: function()
6916     {
6917         this.olEl = this.el.select('ol',true).first();    
6918     },
6919     getChildContainer : function()
6920     {
6921         return this.olEl;  
6922     }
6923     
6924 });
6925
6926  /*
6927  * - LGPL
6928  *
6929  *  Breadcrumb Item
6930  * 
6931  */
6932
6933
6934 /**
6935  * @class Roo.bootstrap.breadcrumb.Nav
6936  * @extends Roo.bootstrap.Component
6937  * Bootstrap Breadcrumb Nav Class
6938  *  
6939  * @children Roo.bootstrap.breadcrumb.Component
6940  * @cfg {String} html the content of the link.
6941  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6942  * @cfg {Boolean} active is it active
6943
6944  * 
6945  * @constructor
6946  * Create a new breadcrumb.Nav
6947  * @param {Object} config The config object
6948  */
6949
6950 Roo.bootstrap.breadcrumb.Item = function(config){
6951     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6952     this.addEvents({
6953         // img events
6954         /**
6955          * @event click
6956          * The img click event for the img.
6957          * @param {Roo.EventObject} e
6958          */
6959         "click" : true
6960     });
6961     
6962 };
6963
6964 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6965     
6966     href: false,
6967     html : '',
6968     
6969     getAutoCreate : function()
6970     {
6971
6972         var cfg = {
6973             tag: 'li',
6974             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6975         };
6976         if (this.href !== false) {
6977             cfg.cn = [{
6978                 tag : 'a',
6979                 href : this.href,
6980                 html : this.html
6981             }];
6982         } else {
6983             cfg.html = this.html;
6984         }
6985         
6986         return cfg;
6987     },
6988     
6989     initEvents: function()
6990     {
6991         if (this.href) {
6992             this.el.select('a', true).first().on('click',this.onClick, this)
6993         }
6994         
6995     },
6996     onClick : function(e)
6997     {
6998         e.preventDefault();
6999         this.fireEvent('click',this,  e);
7000     }
7001     
7002 });
7003
7004  /*
7005  * - LGPL
7006  *
7007  * row
7008  * 
7009  */
7010
7011 /**
7012  * @class Roo.bootstrap.Row
7013  * @extends Roo.bootstrap.Component
7014  * Bootstrap Row class (contains columns...)
7015  * 
7016  * @constructor
7017  * Create a new Row
7018  * @param {Object} config The config object
7019  */
7020
7021 Roo.bootstrap.Row = function(config){
7022     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7023 };
7024
7025 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7026     
7027     getAutoCreate : function(){
7028        return {
7029             cls: 'row clearfix'
7030        };
7031     }
7032     
7033     
7034 });
7035
7036  
7037
7038  /*
7039  * - LGPL
7040  *
7041  * pagination
7042  * 
7043  */
7044
7045 /**
7046  * @class Roo.bootstrap.Pagination
7047  * @extends Roo.bootstrap.Component
7048  * Bootstrap Pagination class
7049  * @cfg {String} size xs | sm | md | lg
7050  * @cfg {Boolean} inverse false | true
7051  * 
7052  * @constructor
7053  * Create a new Pagination
7054  * @param {Object} config The config object
7055  */
7056
7057 Roo.bootstrap.Pagination = function(config){
7058     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7059 };
7060
7061 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7062     
7063     cls: false,
7064     size: false,
7065     inverse: false,
7066     
7067     getAutoCreate : function(){
7068         var cfg = {
7069             tag: 'ul',
7070                 cls: 'pagination'
7071         };
7072         if (this.inverse) {
7073             cfg.cls += ' inverse';
7074         }
7075         if (this.html) {
7076             cfg.html=this.html;
7077         }
7078         if (this.cls) {
7079             cfg.cls += " " + this.cls;
7080         }
7081         return cfg;
7082     }
7083    
7084 });
7085
7086  
7087
7088  /*
7089  * - LGPL
7090  *
7091  * Pagination item
7092  * 
7093  */
7094
7095
7096 /**
7097  * @class Roo.bootstrap.PaginationItem
7098  * @extends Roo.bootstrap.Component
7099  * Bootstrap PaginationItem class
7100  * @cfg {String} html text
7101  * @cfg {String} href the link
7102  * @cfg {Boolean} preventDefault (true | false) default true
7103  * @cfg {Boolean} active (true | false) default false
7104  * @cfg {Boolean} disabled default false
7105  * 
7106  * 
7107  * @constructor
7108  * Create a new PaginationItem
7109  * @param {Object} config The config object
7110  */
7111
7112
7113 Roo.bootstrap.PaginationItem = function(config){
7114     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7115     this.addEvents({
7116         // raw events
7117         /**
7118          * @event click
7119          * The raw click event for the entire grid.
7120          * @param {Roo.EventObject} e
7121          */
7122         "click" : true
7123     });
7124 };
7125
7126 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7127     
7128     href : false,
7129     html : false,
7130     preventDefault: true,
7131     active : false,
7132     cls : false,
7133     disabled: false,
7134     
7135     getAutoCreate : function(){
7136         var cfg= {
7137             tag: 'li',
7138             cn: [
7139                 {
7140                     tag : 'a',
7141                     href : this.href ? this.href : '#',
7142                     html : this.html ? this.html : ''
7143                 }
7144             ]
7145         };
7146         
7147         if(this.cls){
7148             cfg.cls = this.cls;
7149         }
7150         
7151         if(this.disabled){
7152             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7153         }
7154         
7155         if(this.active){
7156             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7157         }
7158         
7159         return cfg;
7160     },
7161     
7162     initEvents: function() {
7163         
7164         this.el.on('click', this.onClick, this);
7165         
7166     },
7167     onClick : function(e)
7168     {
7169         Roo.log('PaginationItem on click ');
7170         if(this.preventDefault){
7171             e.preventDefault();
7172         }
7173         
7174         if(this.disabled){
7175             return;
7176         }
7177         
7178         this.fireEvent('click', this, e);
7179     }
7180    
7181 });
7182
7183  
7184
7185  /*
7186  * - LGPL
7187  *
7188  * slider
7189  * 
7190  */
7191
7192
7193 /**
7194  * @class Roo.bootstrap.Slider
7195  * @extends Roo.bootstrap.Component
7196  * Bootstrap Slider class
7197  *    
7198  * @constructor
7199  * Create a new Slider
7200  * @param {Object} config The config object
7201  */
7202
7203 Roo.bootstrap.Slider = function(config){
7204     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7205 };
7206
7207 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7208     
7209     getAutoCreate : function(){
7210         
7211         var cfg = {
7212             tag: 'div',
7213             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7214             cn: [
7215                 {
7216                     tag: 'a',
7217                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7218                 }
7219             ]
7220         };
7221         
7222         return cfg;
7223     }
7224    
7225 });
7226
7227  /*
7228  * Based on:
7229  * Ext JS Library 1.1.1
7230  * Copyright(c) 2006-2007, Ext JS, LLC.
7231  *
7232  * Originally Released Under LGPL - original licence link has changed is not relivant.
7233  *
7234  * Fork - LGPL
7235  * <script type="text/javascript">
7236  */
7237  
7238
7239 /**
7240  * @class Roo.grid.ColumnModel
7241  * @extends Roo.util.Observable
7242  * This is the default implementation of a ColumnModel used by the Grid. It defines
7243  * the columns in the grid.
7244  * <br>Usage:<br>
7245  <pre><code>
7246  var colModel = new Roo.grid.ColumnModel([
7247         {header: "Ticker", width: 60, sortable: true, locked: true},
7248         {header: "Company Name", width: 150, sortable: true},
7249         {header: "Market Cap.", width: 100, sortable: true},
7250         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7251         {header: "Employees", width: 100, sortable: true, resizable: false}
7252  ]);
7253  </code></pre>
7254  * <p>
7255  
7256  * The config options listed for this class are options which may appear in each
7257  * individual column definition.
7258  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7259  * @constructor
7260  * @param {Object} config An Array of column config objects. See this class's
7261  * config objects for details.
7262 */
7263 Roo.grid.ColumnModel = function(config){
7264         /**
7265      * The config passed into the constructor
7266      */
7267     this.config = config;
7268     this.lookup = {};
7269
7270     // if no id, create one
7271     // if the column does not have a dataIndex mapping,
7272     // map it to the order it is in the config
7273     for(var i = 0, len = config.length; i < len; i++){
7274         var c = config[i];
7275         if(typeof c.dataIndex == "undefined"){
7276             c.dataIndex = i;
7277         }
7278         if(typeof c.renderer == "string"){
7279             c.renderer = Roo.util.Format[c.renderer];
7280         }
7281         if(typeof c.id == "undefined"){
7282             c.id = Roo.id();
7283         }
7284         if(c.editor && c.editor.xtype){
7285             c.editor  = Roo.factory(c.editor, Roo.grid);
7286         }
7287         if(c.editor && c.editor.isFormField){
7288             c.editor = new Roo.grid.GridEditor(c.editor);
7289         }
7290         this.lookup[c.id] = c;
7291     }
7292
7293     /**
7294      * The width of columns which have no width specified (defaults to 100)
7295      * @type Number
7296      */
7297     this.defaultWidth = 100;
7298
7299     /**
7300      * Default sortable of columns which have no sortable specified (defaults to false)
7301      * @type Boolean
7302      */
7303     this.defaultSortable = false;
7304
7305     this.addEvents({
7306         /**
7307              * @event widthchange
7308              * Fires when the width of a column changes.
7309              * @param {ColumnModel} this
7310              * @param {Number} columnIndex The column index
7311              * @param {Number} newWidth The new width
7312              */
7313             "widthchange": true,
7314         /**
7315              * @event headerchange
7316              * Fires when the text of a header changes.
7317              * @param {ColumnModel} this
7318              * @param {Number} columnIndex The column index
7319              * @param {Number} newText The new header text
7320              */
7321             "headerchange": true,
7322         /**
7323              * @event hiddenchange
7324              * Fires when a column is hidden or "unhidden".
7325              * @param {ColumnModel} this
7326              * @param {Number} columnIndex The column index
7327              * @param {Boolean} hidden true if hidden, false otherwise
7328              */
7329             "hiddenchange": true,
7330             /**
7331          * @event columnmoved
7332          * Fires when a column is moved.
7333          * @param {ColumnModel} this
7334          * @param {Number} oldIndex
7335          * @param {Number} newIndex
7336          */
7337         "columnmoved" : true,
7338         /**
7339          * @event columlockchange
7340          * Fires when a column's locked state is changed
7341          * @param {ColumnModel} this
7342          * @param {Number} colIndex
7343          * @param {Boolean} locked true if locked
7344          */
7345         "columnlockchange" : true
7346     });
7347     Roo.grid.ColumnModel.superclass.constructor.call(this);
7348 };
7349 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7350     /**
7351      * @cfg {String} header The header text to display in the Grid view.
7352      */
7353     /**
7354      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7355      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7356      * specified, the column's index is used as an index into the Record's data Array.
7357      */
7358     /**
7359      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7360      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7361      */
7362     /**
7363      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7364      * Defaults to the value of the {@link #defaultSortable} property.
7365      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7366      */
7367     /**
7368      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7369      */
7370     /**
7371      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7372      */
7373     /**
7374      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7375      */
7376     /**
7377      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7378      */
7379     /**
7380      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7381      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7382      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7383      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7384      */
7385        /**
7386      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7387      */
7388     /**
7389      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7390      */
7391     /**
7392      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7393      */
7394     /**
7395      * @cfg {String} cursor (Optional)
7396      */
7397     /**
7398      * @cfg {String} tooltip (Optional)
7399      */
7400     /**
7401      * @cfg {Number} xs (Optional)
7402      */
7403     /**
7404      * @cfg {Number} sm (Optional)
7405      */
7406     /**
7407      * @cfg {Number} md (Optional)
7408      */
7409     /**
7410      * @cfg {Number} lg (Optional)
7411      */
7412     /**
7413      * Returns the id of the column at the specified index.
7414      * @param {Number} index The column index
7415      * @return {String} the id
7416      */
7417     getColumnId : function(index){
7418         return this.config[index].id;
7419     },
7420
7421     /**
7422      * Returns the column for a specified id.
7423      * @param {String} id The column id
7424      * @return {Object} the column
7425      */
7426     getColumnById : function(id){
7427         return this.lookup[id];
7428     },
7429
7430     
7431     /**
7432      * Returns the column for a specified dataIndex.
7433      * @param {String} dataIndex The column dataIndex
7434      * @return {Object|Boolean} the column or false if not found
7435      */
7436     getColumnByDataIndex: function(dataIndex){
7437         var index = this.findColumnIndex(dataIndex);
7438         return index > -1 ? this.config[index] : false;
7439     },
7440     
7441     /**
7442      * Returns the index for a specified column id.
7443      * @param {String} id The column id
7444      * @return {Number} the index, or -1 if not found
7445      */
7446     getIndexById : function(id){
7447         for(var i = 0, len = this.config.length; i < len; i++){
7448             if(this.config[i].id == id){
7449                 return i;
7450             }
7451         }
7452         return -1;
7453     },
7454     
7455     /**
7456      * Returns the index for a specified column dataIndex.
7457      * @param {String} dataIndex The column dataIndex
7458      * @return {Number} the index, or -1 if not found
7459      */
7460     
7461     findColumnIndex : function(dataIndex){
7462         for(var i = 0, len = this.config.length; i < len; i++){
7463             if(this.config[i].dataIndex == dataIndex){
7464                 return i;
7465             }
7466         }
7467         return -1;
7468     },
7469     
7470     
7471     moveColumn : function(oldIndex, newIndex){
7472         var c = this.config[oldIndex];
7473         this.config.splice(oldIndex, 1);
7474         this.config.splice(newIndex, 0, c);
7475         this.dataMap = null;
7476         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7477     },
7478
7479     isLocked : function(colIndex){
7480         return this.config[colIndex].locked === true;
7481     },
7482
7483     setLocked : function(colIndex, value, suppressEvent){
7484         if(this.isLocked(colIndex) == value){
7485             return;
7486         }
7487         this.config[colIndex].locked = value;
7488         if(!suppressEvent){
7489             this.fireEvent("columnlockchange", this, colIndex, value);
7490         }
7491     },
7492
7493     getTotalLockedWidth : function(){
7494         var totalWidth = 0;
7495         for(var i = 0; i < this.config.length; i++){
7496             if(this.isLocked(i) && !this.isHidden(i)){
7497                 this.totalWidth += this.getColumnWidth(i);
7498             }
7499         }
7500         return totalWidth;
7501     },
7502
7503     getLockedCount : function(){
7504         for(var i = 0, len = this.config.length; i < len; i++){
7505             if(!this.isLocked(i)){
7506                 return i;
7507             }
7508         }
7509         
7510         return this.config.length;
7511     },
7512
7513     /**
7514      * Returns the number of columns.
7515      * @return {Number}
7516      */
7517     getColumnCount : function(visibleOnly){
7518         if(visibleOnly === true){
7519             var c = 0;
7520             for(var i = 0, len = this.config.length; i < len; i++){
7521                 if(!this.isHidden(i)){
7522                     c++;
7523                 }
7524             }
7525             return c;
7526         }
7527         return this.config.length;
7528     },
7529
7530     /**
7531      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7532      * @param {Function} fn
7533      * @param {Object} scope (optional)
7534      * @return {Array} result
7535      */
7536     getColumnsBy : function(fn, scope){
7537         var r = [];
7538         for(var i = 0, len = this.config.length; i < len; i++){
7539             var c = this.config[i];
7540             if(fn.call(scope||this, c, i) === true){
7541                 r[r.length] = c;
7542             }
7543         }
7544         return r;
7545     },
7546
7547     /**
7548      * Returns true if the specified column is sortable.
7549      * @param {Number} col The column index
7550      * @return {Boolean}
7551      */
7552     isSortable : function(col){
7553         if(typeof this.config[col].sortable == "undefined"){
7554             return this.defaultSortable;
7555         }
7556         return this.config[col].sortable;
7557     },
7558
7559     /**
7560      * Returns the rendering (formatting) function defined for the column.
7561      * @param {Number} col The column index.
7562      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7563      */
7564     getRenderer : function(col){
7565         if(!this.config[col].renderer){
7566             return Roo.grid.ColumnModel.defaultRenderer;
7567         }
7568         return this.config[col].renderer;
7569     },
7570
7571     /**
7572      * Sets the rendering (formatting) function for a column.
7573      * @param {Number} col The column index
7574      * @param {Function} fn The function to use to process the cell's raw data
7575      * to return HTML markup for the grid view. The render function is called with
7576      * the following parameters:<ul>
7577      * <li>Data value.</li>
7578      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7579      * <li>css A CSS style string to apply to the table cell.</li>
7580      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7581      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7582      * <li>Row index</li>
7583      * <li>Column index</li>
7584      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7585      */
7586     setRenderer : function(col, fn){
7587         this.config[col].renderer = fn;
7588     },
7589
7590     /**
7591      * Returns the width for the specified column.
7592      * @param {Number} col The column index
7593      * @return {Number}
7594      */
7595     getColumnWidth : function(col){
7596         return this.config[col].width * 1 || this.defaultWidth;
7597     },
7598
7599     /**
7600      * Sets the width for a column.
7601      * @param {Number} col The column index
7602      * @param {Number} width The new width
7603      */
7604     setColumnWidth : function(col, width, suppressEvent){
7605         this.config[col].width = width;
7606         this.totalWidth = null;
7607         if(!suppressEvent){
7608              this.fireEvent("widthchange", this, col, width);
7609         }
7610     },
7611
7612     /**
7613      * Returns the total width of all columns.
7614      * @param {Boolean} includeHidden True to include hidden column widths
7615      * @return {Number}
7616      */
7617     getTotalWidth : function(includeHidden){
7618         if(!this.totalWidth){
7619             this.totalWidth = 0;
7620             for(var i = 0, len = this.config.length; i < len; i++){
7621                 if(includeHidden || !this.isHidden(i)){
7622                     this.totalWidth += this.getColumnWidth(i);
7623                 }
7624             }
7625         }
7626         return this.totalWidth;
7627     },
7628
7629     /**
7630      * Returns the header for the specified column.
7631      * @param {Number} col The column index
7632      * @return {String}
7633      */
7634     getColumnHeader : function(col){
7635         return this.config[col].header;
7636     },
7637
7638     /**
7639      * Sets the header for a column.
7640      * @param {Number} col The column index
7641      * @param {String} header The new header
7642      */
7643     setColumnHeader : function(col, header){
7644         this.config[col].header = header;
7645         this.fireEvent("headerchange", this, col, header);
7646     },
7647
7648     /**
7649      * Returns the tooltip for the specified column.
7650      * @param {Number} col The column index
7651      * @return {String}
7652      */
7653     getColumnTooltip : function(col){
7654             return this.config[col].tooltip;
7655     },
7656     /**
7657      * Sets the tooltip for a column.
7658      * @param {Number} col The column index
7659      * @param {String} tooltip The new tooltip
7660      */
7661     setColumnTooltip : function(col, tooltip){
7662             this.config[col].tooltip = tooltip;
7663     },
7664
7665     /**
7666      * Returns the dataIndex for the specified column.
7667      * @param {Number} col The column index
7668      * @return {Number}
7669      */
7670     getDataIndex : function(col){
7671         return this.config[col].dataIndex;
7672     },
7673
7674     /**
7675      * Sets the dataIndex for a column.
7676      * @param {Number} col The column index
7677      * @param {Number} dataIndex The new dataIndex
7678      */
7679     setDataIndex : function(col, dataIndex){
7680         this.config[col].dataIndex = dataIndex;
7681     },
7682
7683     
7684     
7685     /**
7686      * Returns true if the cell is editable.
7687      * @param {Number} colIndex The column index
7688      * @param {Number} rowIndex The row index - this is nto actually used..?
7689      * @return {Boolean}
7690      */
7691     isCellEditable : function(colIndex, rowIndex){
7692         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7693     },
7694
7695     /**
7696      * Returns the editor defined for the cell/column.
7697      * return false or null to disable editing.
7698      * @param {Number} colIndex The column index
7699      * @param {Number} rowIndex The row index
7700      * @return {Object}
7701      */
7702     getCellEditor : function(colIndex, rowIndex){
7703         return this.config[colIndex].editor;
7704     },
7705
7706     /**
7707      * Sets if a column is editable.
7708      * @param {Number} col The column index
7709      * @param {Boolean} editable True if the column is editable
7710      */
7711     setEditable : function(col, editable){
7712         this.config[col].editable = editable;
7713     },
7714
7715
7716     /**
7717      * Returns true if the column is hidden.
7718      * @param {Number} colIndex The column index
7719      * @return {Boolean}
7720      */
7721     isHidden : function(colIndex){
7722         return this.config[colIndex].hidden;
7723     },
7724
7725
7726     /**
7727      * Returns true if the column width cannot be changed
7728      */
7729     isFixed : function(colIndex){
7730         return this.config[colIndex].fixed;
7731     },
7732
7733     /**
7734      * Returns true if the column can be resized
7735      * @return {Boolean}
7736      */
7737     isResizable : function(colIndex){
7738         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7739     },
7740     /**
7741      * Sets if a column is hidden.
7742      * @param {Number} colIndex The column index
7743      * @param {Boolean} hidden True if the column is hidden
7744      */
7745     setHidden : function(colIndex, hidden){
7746         this.config[colIndex].hidden = hidden;
7747         this.totalWidth = null;
7748         this.fireEvent("hiddenchange", this, colIndex, hidden);
7749     },
7750
7751     /**
7752      * Sets the editor for a column.
7753      * @param {Number} col The column index
7754      * @param {Object} editor The editor object
7755      */
7756     setEditor : function(col, editor){
7757         this.config[col].editor = editor;
7758     }
7759 });
7760
7761 Roo.grid.ColumnModel.defaultRenderer = function(value)
7762 {
7763     if(typeof value == "object") {
7764         return value;
7765     }
7766         if(typeof value == "string" && value.length < 1){
7767             return "&#160;";
7768         }
7769     
7770         return String.format("{0}", value);
7771 };
7772
7773 // Alias for backwards compatibility
7774 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7775 /*
7776  * Based on:
7777  * Ext JS Library 1.1.1
7778  * Copyright(c) 2006-2007, Ext JS, LLC.
7779  *
7780  * Originally Released Under LGPL - original licence link has changed is not relivant.
7781  *
7782  * Fork - LGPL
7783  * <script type="text/javascript">
7784  */
7785  
7786 /**
7787  * @class Roo.LoadMask
7788  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7789  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7790  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7791  * element's UpdateManager load indicator and will be destroyed after the initial load.
7792  * @constructor
7793  * Create a new LoadMask
7794  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7795  * @param {Object} config The config object
7796  */
7797 Roo.LoadMask = function(el, config){
7798     this.el = Roo.get(el);
7799     Roo.apply(this, config);
7800     if(this.store){
7801         this.store.on('beforeload', this.onBeforeLoad, this);
7802         this.store.on('load', this.onLoad, this);
7803         this.store.on('loadexception', this.onLoadException, this);
7804         this.removeMask = false;
7805     }else{
7806         var um = this.el.getUpdateManager();
7807         um.showLoadIndicator = false; // disable the default indicator
7808         um.on('beforeupdate', this.onBeforeLoad, this);
7809         um.on('update', this.onLoad, this);
7810         um.on('failure', this.onLoad, this);
7811         this.removeMask = true;
7812     }
7813 };
7814
7815 Roo.LoadMask.prototype = {
7816     /**
7817      * @cfg {Boolean} removeMask
7818      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7819      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7820      */
7821     /**
7822      * @cfg {String} msg
7823      * The text to display in a centered loading message box (defaults to 'Loading...')
7824      */
7825     msg : 'Loading...',
7826     /**
7827      * @cfg {String} msgCls
7828      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7829      */
7830     msgCls : 'x-mask-loading',
7831
7832     /**
7833      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7834      * @type Boolean
7835      */
7836     disabled: false,
7837
7838     /**
7839      * Disables the mask to prevent it from being displayed
7840      */
7841     disable : function(){
7842        this.disabled = true;
7843     },
7844
7845     /**
7846      * Enables the mask so that it can be displayed
7847      */
7848     enable : function(){
7849         this.disabled = false;
7850     },
7851     
7852     onLoadException : function()
7853     {
7854         Roo.log(arguments);
7855         
7856         if (typeof(arguments[3]) != 'undefined') {
7857             Roo.MessageBox.alert("Error loading",arguments[3]);
7858         } 
7859         /*
7860         try {
7861             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7862                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7863             }   
7864         } catch(e) {
7865             
7866         }
7867         */
7868     
7869         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7870     },
7871     // private
7872     onLoad : function()
7873     {
7874         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7875     },
7876
7877     // private
7878     onBeforeLoad : function(){
7879         if(!this.disabled){
7880             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7881         }
7882     },
7883
7884     // private
7885     destroy : function(){
7886         if(this.store){
7887             this.store.un('beforeload', this.onBeforeLoad, this);
7888             this.store.un('load', this.onLoad, this);
7889             this.store.un('loadexception', this.onLoadException, this);
7890         }else{
7891             var um = this.el.getUpdateManager();
7892             um.un('beforeupdate', this.onBeforeLoad, this);
7893             um.un('update', this.onLoad, this);
7894             um.un('failure', this.onLoad, this);
7895         }
7896     }
7897 };/*
7898  * - LGPL
7899  *
7900  * table
7901  * 
7902  */
7903
7904 /**
7905  * @class Roo.bootstrap.Table
7906  * @extends Roo.bootstrap.Component
7907  * Bootstrap Table class
7908  * @cfg {String} cls table class
7909  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7910  * @cfg {String} bgcolor Specifies the background color for a table
7911  * @cfg {Number} border Specifies whether the table cells should have borders or not
7912  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7913  * @cfg {Number} cellspacing Specifies the space between cells
7914  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7915  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7916  * @cfg {String} sortable Specifies that the table should be sortable
7917  * @cfg {String} summary Specifies a summary of the content of a table
7918  * @cfg {Number} width Specifies the width of a table
7919  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7920  * 
7921  * @cfg {boolean} striped Should the rows be alternative striped
7922  * @cfg {boolean} bordered Add borders to the table
7923  * @cfg {boolean} hover Add hover highlighting
7924  * @cfg {boolean} condensed Format condensed
7925  * @cfg {boolean} responsive Format condensed
7926  * @cfg {Boolean} loadMask (true|false) default false
7927  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7928  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7929  * @cfg {Boolean} rowSelection (true|false) default false
7930  * @cfg {Boolean} cellSelection (true|false) default false
7931  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7932  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7933  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7934  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7935  
7936  * 
7937  * @constructor
7938  * Create a new Table
7939  * @param {Object} config The config object
7940  */
7941
7942 Roo.bootstrap.Table = function(config){
7943     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7944     
7945   
7946     
7947     // BC...
7948     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7949     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7950     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7951     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7952     
7953     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7954     if (this.sm) {
7955         this.sm.grid = this;
7956         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7957         this.sm = this.selModel;
7958         this.sm.xmodule = this.xmodule || false;
7959     }
7960     
7961     if (this.cm && typeof(this.cm.config) == 'undefined') {
7962         this.colModel = new Roo.grid.ColumnModel(this.cm);
7963         this.cm = this.colModel;
7964         this.cm.xmodule = this.xmodule || false;
7965     }
7966     if (this.store) {
7967         this.store= Roo.factory(this.store, Roo.data);
7968         this.ds = this.store;
7969         this.ds.xmodule = this.xmodule || false;
7970          
7971     }
7972     if (this.footer && this.store) {
7973         this.footer.dataSource = this.ds;
7974         this.footer = Roo.factory(this.footer);
7975     }
7976     
7977     /** @private */
7978     this.addEvents({
7979         /**
7980          * @event cellclick
7981          * Fires when a cell is clicked
7982          * @param {Roo.bootstrap.Table} this
7983          * @param {Roo.Element} el
7984          * @param {Number} rowIndex
7985          * @param {Number} columnIndex
7986          * @param {Roo.EventObject} e
7987          */
7988         "cellclick" : true,
7989         /**
7990          * @event celldblclick
7991          * Fires when a cell is double clicked
7992          * @param {Roo.bootstrap.Table} this
7993          * @param {Roo.Element} el
7994          * @param {Number} rowIndex
7995          * @param {Number} columnIndex
7996          * @param {Roo.EventObject} e
7997          */
7998         "celldblclick" : true,
7999         /**
8000          * @event rowclick
8001          * Fires when a row is clicked
8002          * @param {Roo.bootstrap.Table} this
8003          * @param {Roo.Element} el
8004          * @param {Number} rowIndex
8005          * @param {Roo.EventObject} e
8006          */
8007         "rowclick" : true,
8008         /**
8009          * @event rowdblclick
8010          * Fires when a row is double clicked
8011          * @param {Roo.bootstrap.Table} this
8012          * @param {Roo.Element} el
8013          * @param {Number} rowIndex
8014          * @param {Roo.EventObject} e
8015          */
8016         "rowdblclick" : true,
8017         /**
8018          * @event mouseover
8019          * Fires when a mouseover occur
8020          * @param {Roo.bootstrap.Table} this
8021          * @param {Roo.Element} el
8022          * @param {Number} rowIndex
8023          * @param {Number} columnIndex
8024          * @param {Roo.EventObject} e
8025          */
8026         "mouseover" : true,
8027         /**
8028          * @event mouseout
8029          * Fires when a mouseout occur
8030          * @param {Roo.bootstrap.Table} this
8031          * @param {Roo.Element} el
8032          * @param {Number} rowIndex
8033          * @param {Number} columnIndex
8034          * @param {Roo.EventObject} e
8035          */
8036         "mouseout" : true,
8037         /**
8038          * @event rowclass
8039          * Fires when a row is rendered, so you can change add a style to it.
8040          * @param {Roo.bootstrap.Table} this
8041          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8042          */
8043         'rowclass' : true,
8044           /**
8045          * @event rowsrendered
8046          * Fires when all the  rows have been rendered
8047          * @param {Roo.bootstrap.Table} this
8048          */
8049         'rowsrendered' : true,
8050         /**
8051          * @event contextmenu
8052          * The raw contextmenu event for the entire grid.
8053          * @param {Roo.EventObject} e
8054          */
8055         "contextmenu" : true,
8056         /**
8057          * @event rowcontextmenu
8058          * Fires when a row is right clicked
8059          * @param {Roo.bootstrap.Table} this
8060          * @param {Number} rowIndex
8061          * @param {Roo.EventObject} e
8062          */
8063         "rowcontextmenu" : true,
8064         /**
8065          * @event cellcontextmenu
8066          * Fires when a cell is right clicked
8067          * @param {Roo.bootstrap.Table} this
8068          * @param {Number} rowIndex
8069          * @param {Number} cellIndex
8070          * @param {Roo.EventObject} e
8071          */
8072          "cellcontextmenu" : true,
8073          /**
8074          * @event headercontextmenu
8075          * Fires when a header is right clicked
8076          * @param {Roo.bootstrap.Table} this
8077          * @param {Number} columnIndex
8078          * @param {Roo.EventObject} e
8079          */
8080         "headercontextmenu" : true
8081     });
8082 };
8083
8084 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8085     
8086     cls: false,
8087     align: false,
8088     bgcolor: false,
8089     border: false,
8090     cellpadding: false,
8091     cellspacing: false,
8092     frame: false,
8093     rules: false,
8094     sortable: false,
8095     summary: false,
8096     width: false,
8097     striped : false,
8098     scrollBody : false,
8099     bordered: false,
8100     hover:  false,
8101     condensed : false,
8102     responsive : false,
8103     sm : false,
8104     cm : false,
8105     store : false,
8106     loadMask : false,
8107     footerShow : true,
8108     headerShow : true,
8109   
8110     rowSelection : false,
8111     cellSelection : false,
8112     layout : false,
8113     
8114     // Roo.Element - the tbody
8115     mainBody: false,
8116     // Roo.Element - thead element
8117     mainHead: false,
8118     
8119     container: false, // used by gridpanel...
8120     
8121     lazyLoad : false,
8122     
8123     CSS : Roo.util.CSS,
8124     
8125     auto_hide_footer : false,
8126     
8127     getAutoCreate : function()
8128     {
8129         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8130         
8131         cfg = {
8132             tag: 'table',
8133             cls : 'table',
8134             cn : []
8135         };
8136         if (this.scrollBody) {
8137             cfg.cls += ' table-body-fixed';
8138         }    
8139         if (this.striped) {
8140             cfg.cls += ' table-striped';
8141         }
8142         
8143         if (this.hover) {
8144             cfg.cls += ' table-hover';
8145         }
8146         if (this.bordered) {
8147             cfg.cls += ' table-bordered';
8148         }
8149         if (this.condensed) {
8150             cfg.cls += ' table-condensed';
8151         }
8152         if (this.responsive) {
8153             cfg.cls += ' table-responsive';
8154         }
8155         
8156         if (this.cls) {
8157             cfg.cls+=  ' ' +this.cls;
8158         }
8159         
8160         // this lot should be simplifed...
8161         var _t = this;
8162         var cp = [
8163             'align',
8164             'bgcolor',
8165             'border',
8166             'cellpadding',
8167             'cellspacing',
8168             'frame',
8169             'rules',
8170             'sortable',
8171             'summary',
8172             'width'
8173         ].forEach(function(k) {
8174             if (_t[k]) {
8175                 cfg[k] = _t[k];
8176             }
8177         });
8178         
8179         
8180         if (this.layout) {
8181             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8182         }
8183         
8184         if(this.store || this.cm){
8185             if(this.headerShow){
8186                 cfg.cn.push(this.renderHeader());
8187             }
8188             
8189             cfg.cn.push(this.renderBody());
8190             
8191             if(this.footerShow){
8192                 cfg.cn.push(this.renderFooter());
8193             }
8194             // where does this come from?
8195             //cfg.cls+=  ' TableGrid';
8196         }
8197         
8198         return { cn : [ cfg ] };
8199     },
8200     
8201     initEvents : function()
8202     {   
8203         if(!this.store || !this.cm){
8204             return;
8205         }
8206         if (this.selModel) {
8207             this.selModel.initEvents();
8208         }
8209         
8210         
8211         //Roo.log('initEvents with ds!!!!');
8212         
8213         this.mainBody = this.el.select('tbody', true).first();
8214         this.mainHead = this.el.select('thead', true).first();
8215         this.mainFoot = this.el.select('tfoot', true).first();
8216         
8217         
8218         
8219         var _this = this;
8220         
8221         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8222             e.on('click', _this.sort, _this);
8223         });
8224         
8225         this.mainBody.on("click", this.onClick, this);
8226         this.mainBody.on("dblclick", this.onDblClick, this);
8227         
8228         // why is this done????? = it breaks dialogs??
8229         //this.parent().el.setStyle('position', 'relative');
8230         
8231         
8232         if (this.footer) {
8233             this.footer.parentId = this.id;
8234             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8235             
8236             if(this.lazyLoad){
8237                 this.el.select('tfoot tr td').first().addClass('hide');
8238             }
8239         } 
8240         
8241         if(this.loadMask) {
8242             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8243         }
8244         
8245         this.store.on('load', this.onLoad, this);
8246         this.store.on('beforeload', this.onBeforeLoad, this);
8247         this.store.on('update', this.onUpdate, this);
8248         this.store.on('add', this.onAdd, this);
8249         this.store.on("clear", this.clear, this);
8250         
8251         this.el.on("contextmenu", this.onContextMenu, this);
8252         
8253         this.mainBody.on('scroll', this.onBodyScroll, this);
8254         
8255         this.cm.on("headerchange", this.onHeaderChange, this);
8256         
8257         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8258         
8259     },
8260     
8261     onContextMenu : function(e, t)
8262     {
8263         this.processEvent("contextmenu", e);
8264     },
8265     
8266     processEvent : function(name, e)
8267     {
8268         if (name != 'touchstart' ) {
8269             this.fireEvent(name, e);    
8270         }
8271         
8272         var t = e.getTarget();
8273         
8274         var cell = Roo.get(t);
8275         
8276         if(!cell){
8277             return;
8278         }
8279         
8280         if(cell.findParent('tfoot', false, true)){
8281             return;
8282         }
8283         
8284         if(cell.findParent('thead', false, true)){
8285             
8286             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8287                 cell = Roo.get(t).findParent('th', false, true);
8288                 if (!cell) {
8289                     Roo.log("failed to find th in thead?");
8290                     Roo.log(e.getTarget());
8291                     return;
8292                 }
8293             }
8294             
8295             var cellIndex = cell.dom.cellIndex;
8296             
8297             var ename = name == 'touchstart' ? 'click' : name;
8298             this.fireEvent("header" + ename, this, cellIndex, e);
8299             
8300             return;
8301         }
8302         
8303         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8304             cell = Roo.get(t).findParent('td', false, true);
8305             if (!cell) {
8306                 Roo.log("failed to find th in tbody?");
8307                 Roo.log(e.getTarget());
8308                 return;
8309             }
8310         }
8311         
8312         var row = cell.findParent('tr', false, true);
8313         var cellIndex = cell.dom.cellIndex;
8314         var rowIndex = row.dom.rowIndex - 1;
8315         
8316         if(row !== false){
8317             
8318             this.fireEvent("row" + name, this, rowIndex, e);
8319             
8320             if(cell !== false){
8321             
8322                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8323             }
8324         }
8325         
8326     },
8327     
8328     onMouseover : function(e, el)
8329     {
8330         var cell = Roo.get(el);
8331         
8332         if(!cell){
8333             return;
8334         }
8335         
8336         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8337             cell = cell.findParent('td', false, true);
8338         }
8339         
8340         var row = cell.findParent('tr', false, true);
8341         var cellIndex = cell.dom.cellIndex;
8342         var rowIndex = row.dom.rowIndex - 1; // start from 0
8343         
8344         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8345         
8346     },
8347     
8348     onMouseout : function(e, el)
8349     {
8350         var cell = Roo.get(el);
8351         
8352         if(!cell){
8353             return;
8354         }
8355         
8356         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8357             cell = cell.findParent('td', false, true);
8358         }
8359         
8360         var row = cell.findParent('tr', false, true);
8361         var cellIndex = cell.dom.cellIndex;
8362         var rowIndex = row.dom.rowIndex - 1; // start from 0
8363         
8364         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8365         
8366     },
8367     
8368     onClick : function(e, el)
8369     {
8370         var cell = Roo.get(el);
8371         
8372         if(!cell || (!this.cellSelection && !this.rowSelection)){
8373             return;
8374         }
8375         
8376         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8377             cell = cell.findParent('td', false, true);
8378         }
8379         
8380         if(!cell || typeof(cell) == 'undefined'){
8381             return;
8382         }
8383         
8384         var row = cell.findParent('tr', false, true);
8385         
8386         if(!row || typeof(row) == 'undefined'){
8387             return;
8388         }
8389         
8390         var cellIndex = cell.dom.cellIndex;
8391         var rowIndex = this.getRowIndex(row);
8392         
8393         // why??? - should these not be based on SelectionModel?
8394         if(this.cellSelection){
8395             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8396         }
8397         
8398         if(this.rowSelection){
8399             this.fireEvent('rowclick', this, row, rowIndex, e);
8400         }
8401         
8402         
8403     },
8404         
8405     onDblClick : function(e,el)
8406     {
8407         var cell = Roo.get(el);
8408         
8409         if(!cell || (!this.cellSelection && !this.rowSelection)){
8410             return;
8411         }
8412         
8413         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8414             cell = cell.findParent('td', false, true);
8415         }
8416         
8417         if(!cell || typeof(cell) == 'undefined'){
8418             return;
8419         }
8420         
8421         var row = cell.findParent('tr', false, true);
8422         
8423         if(!row || typeof(row) == 'undefined'){
8424             return;
8425         }
8426         
8427         var cellIndex = cell.dom.cellIndex;
8428         var rowIndex = this.getRowIndex(row);
8429         
8430         if(this.cellSelection){
8431             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8432         }
8433         
8434         if(this.rowSelection){
8435             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8436         }
8437     },
8438     
8439     sort : function(e,el)
8440     {
8441         var col = Roo.get(el);
8442         
8443         if(!col.hasClass('sortable')){
8444             return;
8445         }
8446         
8447         var sort = col.attr('sort');
8448         var dir = 'ASC';
8449         
8450         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8451             dir = 'DESC';
8452         }
8453         
8454         this.store.sortInfo = {field : sort, direction : dir};
8455         
8456         if (this.footer) {
8457             Roo.log("calling footer first");
8458             this.footer.onClick('first');
8459         } else {
8460         
8461             this.store.load({ params : { start : 0 } });
8462         }
8463     },
8464     
8465     renderHeader : function()
8466     {
8467         var header = {
8468             tag: 'thead',
8469             cn : []
8470         };
8471         
8472         var cm = this.cm;
8473         this.totalWidth = 0;
8474         
8475         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8476             
8477             var config = cm.config[i];
8478             
8479             var c = {
8480                 tag: 'th',
8481                 cls : 'x-hcol-' + i,
8482                 style : '',
8483                 html: cm.getColumnHeader(i)
8484             };
8485             
8486             var hh = '';
8487             
8488             if(typeof(config.sortable) != 'undefined' && config.sortable){
8489                 c.cls = 'sortable';
8490                 c.html = '<i class="glyphicon"></i>' + c.html;
8491             }
8492             
8493             // could use BS4 hidden-..-down 
8494             
8495             if(typeof(config.lgHeader) != 'undefined'){
8496                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8497             }
8498             
8499             if(typeof(config.mdHeader) != 'undefined'){
8500                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8501             }
8502             
8503             if(typeof(config.smHeader) != 'undefined'){
8504                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8505             }
8506             
8507             if(typeof(config.xsHeader) != 'undefined'){
8508                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8509             }
8510             
8511             if(hh.length){
8512                 c.html = hh;
8513             }
8514             
8515             if(typeof(config.tooltip) != 'undefined'){
8516                 c.tooltip = config.tooltip;
8517             }
8518             
8519             if(typeof(config.colspan) != 'undefined'){
8520                 c.colspan = config.colspan;
8521             }
8522             
8523             if(typeof(config.hidden) != 'undefined' && config.hidden){
8524                 c.style += ' display:none;';
8525             }
8526             
8527             if(typeof(config.dataIndex) != 'undefined'){
8528                 c.sort = config.dataIndex;
8529             }
8530             
8531            
8532             
8533             if(typeof(config.align) != 'undefined' && config.align.length){
8534                 c.style += ' text-align:' + config.align + ';';
8535             }
8536             
8537             if(typeof(config.width) != 'undefined'){
8538                 c.style += ' width:' + config.width + 'px;';
8539                 this.totalWidth += config.width;
8540             } else {
8541                 this.totalWidth += 100; // assume minimum of 100 per column?
8542             }
8543             
8544             if(typeof(config.cls) != 'undefined'){
8545                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8546             }
8547             
8548             ['xs','sm','md','lg'].map(function(size){
8549                 
8550                 if(typeof(config[size]) == 'undefined'){
8551                     return;
8552                 }
8553                  
8554                 if (!config[size]) { // 0 = hidden
8555                     // BS 4 '0' is treated as hide that column and below.
8556                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8557                     return;
8558                 }
8559                 
8560                 c.cls += ' col-' + size + '-' + config[size] + (
8561                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8562                 );
8563                 
8564                 
8565             });
8566             
8567             header.cn.push(c)
8568         }
8569         
8570         return header;
8571     },
8572     
8573     renderBody : function()
8574     {
8575         var body = {
8576             tag: 'tbody',
8577             cn : [
8578                 {
8579                     tag: 'tr',
8580                     cn : [
8581                         {
8582                             tag : 'td',
8583                             colspan :  this.cm.getColumnCount()
8584                         }
8585                     ]
8586                 }
8587             ]
8588         };
8589         
8590         return body;
8591     },
8592     
8593     renderFooter : function()
8594     {
8595         var footer = {
8596             tag: 'tfoot',
8597             cn : [
8598                 {
8599                     tag: 'tr',
8600                     cn : [
8601                         {
8602                             tag : 'td',
8603                             colspan :  this.cm.getColumnCount()
8604                         }
8605                     ]
8606                 }
8607             ]
8608         };
8609         
8610         return footer;
8611     },
8612     
8613     
8614     
8615     onLoad : function()
8616     {
8617 //        Roo.log('ds onload');
8618         this.clear();
8619         
8620         var _this = this;
8621         var cm = this.cm;
8622         var ds = this.store;
8623         
8624         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8625             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8626             if (_this.store.sortInfo) {
8627                     
8628                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8629                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8630                 }
8631                 
8632                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8633                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8634                 }
8635             }
8636         });
8637         
8638         var tbody =  this.mainBody;
8639               
8640         if(ds.getCount() > 0){
8641             ds.data.each(function(d,rowIndex){
8642                 var row =  this.renderRow(cm, ds, rowIndex);
8643                 
8644                 tbody.createChild(row);
8645                 
8646                 var _this = this;
8647                 
8648                 if(row.cellObjects.length){
8649                     Roo.each(row.cellObjects, function(r){
8650                         _this.renderCellObject(r);
8651                     })
8652                 }
8653                 
8654             }, this);
8655         }
8656         
8657         var tfoot = this.el.select('tfoot', true).first();
8658         
8659         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8660             
8661             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8662             
8663             var total = this.ds.getTotalCount();
8664             
8665             if(this.footer.pageSize < total){
8666                 this.mainFoot.show();
8667             }
8668         }
8669         
8670         Roo.each(this.el.select('tbody td', true).elements, function(e){
8671             e.on('mouseover', _this.onMouseover, _this);
8672         });
8673         
8674         Roo.each(this.el.select('tbody td', true).elements, function(e){
8675             e.on('mouseout', _this.onMouseout, _this);
8676         });
8677         this.fireEvent('rowsrendered', this);
8678         
8679         this.autoSize();
8680     },
8681     
8682     
8683     onUpdate : function(ds,record)
8684     {
8685         this.refreshRow(record);
8686         this.autoSize();
8687     },
8688     
8689     onRemove : function(ds, record, index, isUpdate){
8690         if(isUpdate !== true){
8691             this.fireEvent("beforerowremoved", this, index, record);
8692         }
8693         var bt = this.mainBody.dom;
8694         
8695         var rows = this.el.select('tbody > tr', true).elements;
8696         
8697         if(typeof(rows[index]) != 'undefined'){
8698             bt.removeChild(rows[index].dom);
8699         }
8700         
8701 //        if(bt.rows[index]){
8702 //            bt.removeChild(bt.rows[index]);
8703 //        }
8704         
8705         if(isUpdate !== true){
8706             //this.stripeRows(index);
8707             //this.syncRowHeights(index, index);
8708             //this.layout();
8709             this.fireEvent("rowremoved", this, index, record);
8710         }
8711     },
8712     
8713     onAdd : function(ds, records, rowIndex)
8714     {
8715         //Roo.log('on Add called');
8716         // - note this does not handle multiple adding very well..
8717         var bt = this.mainBody.dom;
8718         for (var i =0 ; i < records.length;i++) {
8719             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8720             //Roo.log(records[i]);
8721             //Roo.log(this.store.getAt(rowIndex+i));
8722             this.insertRow(this.store, rowIndex + i, false);
8723             return;
8724         }
8725         
8726     },
8727     
8728     
8729     refreshRow : function(record){
8730         var ds = this.store, index;
8731         if(typeof record == 'number'){
8732             index = record;
8733             record = ds.getAt(index);
8734         }else{
8735             index = ds.indexOf(record);
8736             if (index < 0) {
8737                 return; // should not happen - but seems to 
8738             }
8739         }
8740         this.insertRow(ds, index, true);
8741         this.autoSize();
8742         this.onRemove(ds, record, index+1, true);
8743         this.autoSize();
8744         //this.syncRowHeights(index, index);
8745         //this.layout();
8746         this.fireEvent("rowupdated", this, index, record);
8747     },
8748     
8749     insertRow : function(dm, rowIndex, isUpdate){
8750         
8751         if(!isUpdate){
8752             this.fireEvent("beforerowsinserted", this, rowIndex);
8753         }
8754             //var s = this.getScrollState();
8755         var row = this.renderRow(this.cm, this.store, rowIndex);
8756         // insert before rowIndex..
8757         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8758         
8759         var _this = this;
8760                 
8761         if(row.cellObjects.length){
8762             Roo.each(row.cellObjects, function(r){
8763                 _this.renderCellObject(r);
8764             })
8765         }
8766             
8767         if(!isUpdate){
8768             this.fireEvent("rowsinserted", this, rowIndex);
8769             //this.syncRowHeights(firstRow, lastRow);
8770             //this.stripeRows(firstRow);
8771             //this.layout();
8772         }
8773         
8774     },
8775     
8776     
8777     getRowDom : function(rowIndex)
8778     {
8779         var rows = this.el.select('tbody > tr', true).elements;
8780         
8781         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8782         
8783     },
8784     // returns the object tree for a tr..
8785   
8786     
8787     renderRow : function(cm, ds, rowIndex) 
8788     {
8789         var d = ds.getAt(rowIndex);
8790         
8791         var row = {
8792             tag : 'tr',
8793             cls : 'x-row-' + rowIndex,
8794             cn : []
8795         };
8796             
8797         var cellObjects = [];
8798         
8799         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8800             var config = cm.config[i];
8801             
8802             var renderer = cm.getRenderer(i);
8803             var value = '';
8804             var id = false;
8805             
8806             if(typeof(renderer) !== 'undefined'){
8807                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8808             }
8809             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8810             // and are rendered into the cells after the row is rendered - using the id for the element.
8811             
8812             if(typeof(value) === 'object'){
8813                 id = Roo.id();
8814                 cellObjects.push({
8815                     container : id,
8816                     cfg : value 
8817                 })
8818             }
8819             
8820             var rowcfg = {
8821                 record: d,
8822                 rowIndex : rowIndex,
8823                 colIndex : i,
8824                 rowClass : ''
8825             };
8826
8827             this.fireEvent('rowclass', this, rowcfg);
8828             
8829             var td = {
8830                 tag: 'td',
8831                 cls : rowcfg.rowClass + ' x-col-' + i,
8832                 style: '',
8833                 html: (typeof(value) === 'object') ? '' : value
8834             };
8835             
8836             if (id) {
8837                 td.id = id;
8838             }
8839             
8840             if(typeof(config.colspan) != 'undefined'){
8841                 td.colspan = config.colspan;
8842             }
8843             
8844             if(typeof(config.hidden) != 'undefined' && config.hidden){
8845                 td.style += ' display:none;';
8846             }
8847             
8848             if(typeof(config.align) != 'undefined' && config.align.length){
8849                 td.style += ' text-align:' + config.align + ';';
8850             }
8851             if(typeof(config.valign) != 'undefined' && config.valign.length){
8852                 td.style += ' vertical-align:' + config.valign + ';';
8853             }
8854             
8855             if(typeof(config.width) != 'undefined'){
8856                 td.style += ' width:' +  config.width + 'px;';
8857             }
8858             
8859             if(typeof(config.cursor) != 'undefined'){
8860                 td.style += ' cursor:' +  config.cursor + ';';
8861             }
8862             
8863             if(typeof(config.cls) != 'undefined'){
8864                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8865             }
8866             
8867             ['xs','sm','md','lg'].map(function(size){
8868                 
8869                 if(typeof(config[size]) == 'undefined'){
8870                     return;
8871                 }
8872                 
8873                 
8874                   
8875                 if (!config[size]) { // 0 = hidden
8876                     // BS 4 '0' is treated as hide that column and below.
8877                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8878                     return;
8879                 }
8880                 
8881                 td.cls += ' col-' + size + '-' + config[size] + (
8882                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8883                 );
8884                  
8885
8886             });
8887             
8888             row.cn.push(td);
8889            
8890         }
8891         
8892         row.cellObjects = cellObjects;
8893         
8894         return row;
8895           
8896     },
8897     
8898     
8899     
8900     onBeforeLoad : function()
8901     {
8902         
8903     },
8904      /**
8905      * Remove all rows
8906      */
8907     clear : function()
8908     {
8909         this.el.select('tbody', true).first().dom.innerHTML = '';
8910     },
8911     /**
8912      * Show or hide a row.
8913      * @param {Number} rowIndex to show or hide
8914      * @param {Boolean} state hide
8915      */
8916     setRowVisibility : function(rowIndex, state)
8917     {
8918         var bt = this.mainBody.dom;
8919         
8920         var rows = this.el.select('tbody > tr', true).elements;
8921         
8922         if(typeof(rows[rowIndex]) == 'undefined'){
8923             return;
8924         }
8925         rows[rowIndex].dom.style.display = state ? '' : 'none';
8926     },
8927     
8928     
8929     getSelectionModel : function(){
8930         if(!this.selModel){
8931             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8932         }
8933         return this.selModel;
8934     },
8935     /*
8936      * Render the Roo.bootstrap object from renderder
8937      */
8938     renderCellObject : function(r)
8939     {
8940         var _this = this;
8941         
8942         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8943         
8944         var t = r.cfg.render(r.container);
8945         
8946         if(r.cfg.cn){
8947             Roo.each(r.cfg.cn, function(c){
8948                 var child = {
8949                     container: t.getChildContainer(),
8950                     cfg: c
8951                 };
8952                 _this.renderCellObject(child);
8953             })
8954         }
8955     },
8956     
8957     getRowIndex : function(row)
8958     {
8959         var rowIndex = -1;
8960         
8961         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8962             if(el != row){
8963                 return;
8964             }
8965             
8966             rowIndex = index;
8967         });
8968         
8969         return rowIndex;
8970     },
8971      /**
8972      * Returns the grid's underlying element = used by panel.Grid
8973      * @return {Element} The element
8974      */
8975     getGridEl : function(){
8976         return this.el;
8977     },
8978      /**
8979      * Forces a resize - used by panel.Grid
8980      * @return {Element} The element
8981      */
8982     autoSize : function()
8983     {
8984         //var ctr = Roo.get(this.container.dom.parentElement);
8985         var ctr = Roo.get(this.el.dom);
8986         
8987         var thd = this.getGridEl().select('thead',true).first();
8988         var tbd = this.getGridEl().select('tbody', true).first();
8989         var tfd = this.getGridEl().select('tfoot', true).first();
8990         
8991         var cw = ctr.getWidth();
8992         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8993         
8994         if (tbd) {
8995             
8996             tbd.setWidth(ctr.getWidth());
8997             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8998             // this needs fixing for various usage - currently only hydra job advers I think..
8999             //tdb.setHeight(
9000             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9001             //); 
9002             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9003             cw -= barsize;
9004         }
9005         cw = Math.max(cw, this.totalWidth);
9006         this.getGridEl().select('tbody tr',true).setWidth(cw);
9007         
9008         // resize 'expandable coloumn?
9009         
9010         return; // we doe not have a view in this design..
9011         
9012     },
9013     onBodyScroll: function()
9014     {
9015         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9016         if(this.mainHead){
9017             this.mainHead.setStyle({
9018                 'position' : 'relative',
9019                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9020             });
9021         }
9022         
9023         if(this.lazyLoad){
9024             
9025             var scrollHeight = this.mainBody.dom.scrollHeight;
9026             
9027             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9028             
9029             var height = this.mainBody.getHeight();
9030             
9031             if(scrollHeight - height == scrollTop) {
9032                 
9033                 var total = this.ds.getTotalCount();
9034                 
9035                 if(this.footer.cursor + this.footer.pageSize < total){
9036                     
9037                     this.footer.ds.load({
9038                         params : {
9039                             start : this.footer.cursor + this.footer.pageSize,
9040                             limit : this.footer.pageSize
9041                         },
9042                         add : true
9043                     });
9044                 }
9045             }
9046             
9047         }
9048     },
9049     
9050     onHeaderChange : function()
9051     {
9052         var header = this.renderHeader();
9053         var table = this.el.select('table', true).first();
9054         
9055         this.mainHead.remove();
9056         this.mainHead = table.createChild(header, this.mainBody, false);
9057     },
9058     
9059     onHiddenChange : function(colModel, colIndex, hidden)
9060     {
9061         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9062         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9063         
9064         this.CSS.updateRule(thSelector, "display", "");
9065         this.CSS.updateRule(tdSelector, "display", "");
9066         
9067         if(hidden){
9068             this.CSS.updateRule(thSelector, "display", "none");
9069             this.CSS.updateRule(tdSelector, "display", "none");
9070         }
9071         
9072         this.onHeaderChange();
9073         this.onLoad();
9074     },
9075     
9076     setColumnWidth: function(col_index, width)
9077     {
9078         // width = "md-2 xs-2..."
9079         if(!this.colModel.config[col_index]) {
9080             return;
9081         }
9082         
9083         var w = width.split(" ");
9084         
9085         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9086         
9087         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9088         
9089         
9090         for(var j = 0; j < w.length; j++) {
9091             
9092             if(!w[j]) {
9093                 continue;
9094             }
9095             
9096             var size_cls = w[j].split("-");
9097             
9098             if(!Number.isInteger(size_cls[1] * 1)) {
9099                 continue;
9100             }
9101             
9102             if(!this.colModel.config[col_index][size_cls[0]]) {
9103                 continue;
9104             }
9105             
9106             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9107                 continue;
9108             }
9109             
9110             h_row[0].classList.replace(
9111                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9112                 "col-"+size_cls[0]+"-"+size_cls[1]
9113             );
9114             
9115             for(var i = 0; i < rows.length; i++) {
9116                 
9117                 var size_cls = w[j].split("-");
9118                 
9119                 if(!Number.isInteger(size_cls[1] * 1)) {
9120                     continue;
9121                 }
9122                 
9123                 if(!this.colModel.config[col_index][size_cls[0]]) {
9124                     continue;
9125                 }
9126                 
9127                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9128                     continue;
9129                 }
9130                 
9131                 rows[i].classList.replace(
9132                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9133                     "col-"+size_cls[0]+"-"+size_cls[1]
9134                 );
9135             }
9136             
9137             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9138         }
9139     }
9140 });
9141
9142  
9143
9144  /*
9145  * - LGPL
9146  *
9147  * table cell
9148  * 
9149  */
9150
9151 /**
9152  * @class Roo.bootstrap.TableCell
9153  * @extends Roo.bootstrap.Component
9154  * Bootstrap TableCell class
9155  * @cfg {String} html cell contain text
9156  * @cfg {String} cls cell class
9157  * @cfg {String} tag cell tag (td|th) default td
9158  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9159  * @cfg {String} align Aligns the content in a cell
9160  * @cfg {String} axis Categorizes cells
9161  * @cfg {String} bgcolor Specifies the background color of a cell
9162  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9163  * @cfg {Number} colspan Specifies the number of columns a cell should span
9164  * @cfg {String} headers Specifies one or more header cells a cell is related to
9165  * @cfg {Number} height Sets the height of a cell
9166  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9167  * @cfg {Number} rowspan Sets the number of rows a cell should span
9168  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9169  * @cfg {String} valign Vertical aligns the content in a cell
9170  * @cfg {Number} width Specifies the width of a cell
9171  * 
9172  * @constructor
9173  * Create a new TableCell
9174  * @param {Object} config The config object
9175  */
9176
9177 Roo.bootstrap.TableCell = function(config){
9178     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9179 };
9180
9181 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9182     
9183     html: false,
9184     cls: false,
9185     tag: false,
9186     abbr: false,
9187     align: false,
9188     axis: false,
9189     bgcolor: false,
9190     charoff: false,
9191     colspan: false,
9192     headers: false,
9193     height: false,
9194     nowrap: false,
9195     rowspan: false,
9196     scope: false,
9197     valign: false,
9198     width: false,
9199     
9200     
9201     getAutoCreate : function(){
9202         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9203         
9204         cfg = {
9205             tag: 'td'
9206         };
9207         
9208         if(this.tag){
9209             cfg.tag = this.tag;
9210         }
9211         
9212         if (this.html) {
9213             cfg.html=this.html
9214         }
9215         if (this.cls) {
9216             cfg.cls=this.cls
9217         }
9218         if (this.abbr) {
9219             cfg.abbr=this.abbr
9220         }
9221         if (this.align) {
9222             cfg.align=this.align
9223         }
9224         if (this.axis) {
9225             cfg.axis=this.axis
9226         }
9227         if (this.bgcolor) {
9228             cfg.bgcolor=this.bgcolor
9229         }
9230         if (this.charoff) {
9231             cfg.charoff=this.charoff
9232         }
9233         if (this.colspan) {
9234             cfg.colspan=this.colspan
9235         }
9236         if (this.headers) {
9237             cfg.headers=this.headers
9238         }
9239         if (this.height) {
9240             cfg.height=this.height
9241         }
9242         if (this.nowrap) {
9243             cfg.nowrap=this.nowrap
9244         }
9245         if (this.rowspan) {
9246             cfg.rowspan=this.rowspan
9247         }
9248         if (this.scope) {
9249             cfg.scope=this.scope
9250         }
9251         if (this.valign) {
9252             cfg.valign=this.valign
9253         }
9254         if (this.width) {
9255             cfg.width=this.width
9256         }
9257         
9258         
9259         return cfg;
9260     }
9261    
9262 });
9263
9264  
9265
9266  /*
9267  * - LGPL
9268  *
9269  * table row
9270  * 
9271  */
9272
9273 /**
9274  * @class Roo.bootstrap.TableRow
9275  * @extends Roo.bootstrap.Component
9276  * Bootstrap TableRow class
9277  * @cfg {String} cls row class
9278  * @cfg {String} align Aligns the content in a table row
9279  * @cfg {String} bgcolor Specifies a background color for a table row
9280  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9281  * @cfg {String} valign Vertical aligns the content in a table row
9282  * 
9283  * @constructor
9284  * Create a new TableRow
9285  * @param {Object} config The config object
9286  */
9287
9288 Roo.bootstrap.TableRow = function(config){
9289     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9290 };
9291
9292 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9293     
9294     cls: false,
9295     align: false,
9296     bgcolor: false,
9297     charoff: false,
9298     valign: false,
9299     
9300     getAutoCreate : function(){
9301         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9302         
9303         cfg = {
9304             tag: 'tr'
9305         };
9306             
9307         if(this.cls){
9308             cfg.cls = this.cls;
9309         }
9310         if(this.align){
9311             cfg.align = this.align;
9312         }
9313         if(this.bgcolor){
9314             cfg.bgcolor = this.bgcolor;
9315         }
9316         if(this.charoff){
9317             cfg.charoff = this.charoff;
9318         }
9319         if(this.valign){
9320             cfg.valign = this.valign;
9321         }
9322         
9323         return cfg;
9324     }
9325    
9326 });
9327
9328  
9329
9330  /*
9331  * - LGPL
9332  *
9333  * table body
9334  * 
9335  */
9336
9337 /**
9338  * @class Roo.bootstrap.TableBody
9339  * @extends Roo.bootstrap.Component
9340  * Bootstrap TableBody class
9341  * @cfg {String} cls element class
9342  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9343  * @cfg {String} align Aligns the content inside the element
9344  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9345  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9346  * 
9347  * @constructor
9348  * Create a new TableBody
9349  * @param {Object} config The config object
9350  */
9351
9352 Roo.bootstrap.TableBody = function(config){
9353     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9354 };
9355
9356 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9357     
9358     cls: false,
9359     tag: false,
9360     align: false,
9361     charoff: false,
9362     valign: false,
9363     
9364     getAutoCreate : function(){
9365         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9366         
9367         cfg = {
9368             tag: 'tbody'
9369         };
9370             
9371         if (this.cls) {
9372             cfg.cls=this.cls
9373         }
9374         if(this.tag){
9375             cfg.tag = this.tag;
9376         }
9377         
9378         if(this.align){
9379             cfg.align = this.align;
9380         }
9381         if(this.charoff){
9382             cfg.charoff = this.charoff;
9383         }
9384         if(this.valign){
9385             cfg.valign = this.valign;
9386         }
9387         
9388         return cfg;
9389     }
9390     
9391     
9392 //    initEvents : function()
9393 //    {
9394 //        
9395 //        if(!this.store){
9396 //            return;
9397 //        }
9398 //        
9399 //        this.store = Roo.factory(this.store, Roo.data);
9400 //        this.store.on('load', this.onLoad, this);
9401 //        
9402 //        this.store.load();
9403 //        
9404 //    },
9405 //    
9406 //    onLoad: function () 
9407 //    {   
9408 //        this.fireEvent('load', this);
9409 //    }
9410 //    
9411 //   
9412 });
9413
9414  
9415
9416  /*
9417  * Based on:
9418  * Ext JS Library 1.1.1
9419  * Copyright(c) 2006-2007, Ext JS, LLC.
9420  *
9421  * Originally Released Under LGPL - original licence link has changed is not relivant.
9422  *
9423  * Fork - LGPL
9424  * <script type="text/javascript">
9425  */
9426
9427 // as we use this in bootstrap.
9428 Roo.namespace('Roo.form');
9429  /**
9430  * @class Roo.form.Action
9431  * Internal Class used to handle form actions
9432  * @constructor
9433  * @param {Roo.form.BasicForm} el The form element or its id
9434  * @param {Object} config Configuration options
9435  */
9436
9437  
9438  
9439 // define the action interface
9440 Roo.form.Action = function(form, options){
9441     this.form = form;
9442     this.options = options || {};
9443 };
9444 /**
9445  * Client Validation Failed
9446  * @const 
9447  */
9448 Roo.form.Action.CLIENT_INVALID = 'client';
9449 /**
9450  * Server Validation Failed
9451  * @const 
9452  */
9453 Roo.form.Action.SERVER_INVALID = 'server';
9454  /**
9455  * Connect to Server Failed
9456  * @const 
9457  */
9458 Roo.form.Action.CONNECT_FAILURE = 'connect';
9459 /**
9460  * Reading Data from Server Failed
9461  * @const 
9462  */
9463 Roo.form.Action.LOAD_FAILURE = 'load';
9464
9465 Roo.form.Action.prototype = {
9466     type : 'default',
9467     failureType : undefined,
9468     response : undefined,
9469     result : undefined,
9470
9471     // interface method
9472     run : function(options){
9473
9474     },
9475
9476     // interface method
9477     success : function(response){
9478
9479     },
9480
9481     // interface method
9482     handleResponse : function(response){
9483
9484     },
9485
9486     // default connection failure
9487     failure : function(response){
9488         
9489         this.response = response;
9490         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9491         this.form.afterAction(this, false);
9492     },
9493
9494     processResponse : function(response){
9495         this.response = response;
9496         if(!response.responseText){
9497             return true;
9498         }
9499         this.result = this.handleResponse(response);
9500         return this.result;
9501     },
9502
9503     // utility functions used internally
9504     getUrl : function(appendParams){
9505         var url = this.options.url || this.form.url || this.form.el.dom.action;
9506         if(appendParams){
9507             var p = this.getParams();
9508             if(p){
9509                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9510             }
9511         }
9512         return url;
9513     },
9514
9515     getMethod : function(){
9516         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9517     },
9518
9519     getParams : function(){
9520         var bp = this.form.baseParams;
9521         var p = this.options.params;
9522         if(p){
9523             if(typeof p == "object"){
9524                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9525             }else if(typeof p == 'string' && bp){
9526                 p += '&' + Roo.urlEncode(bp);
9527             }
9528         }else if(bp){
9529             p = Roo.urlEncode(bp);
9530         }
9531         return p;
9532     },
9533
9534     createCallback : function(){
9535         return {
9536             success: this.success,
9537             failure: this.failure,
9538             scope: this,
9539             timeout: (this.form.timeout*1000),
9540             upload: this.form.fileUpload ? this.success : undefined
9541         };
9542     }
9543 };
9544
9545 Roo.form.Action.Submit = function(form, options){
9546     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9547 };
9548
9549 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9550     type : 'submit',
9551
9552     haveProgress : false,
9553     uploadComplete : false,
9554     
9555     // uploadProgress indicator.
9556     uploadProgress : function()
9557     {
9558         if (!this.form.progressUrl) {
9559             return;
9560         }
9561         
9562         if (!this.haveProgress) {
9563             Roo.MessageBox.progress("Uploading", "Uploading");
9564         }
9565         if (this.uploadComplete) {
9566            Roo.MessageBox.hide();
9567            return;
9568         }
9569         
9570         this.haveProgress = true;
9571    
9572         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9573         
9574         var c = new Roo.data.Connection();
9575         c.request({
9576             url : this.form.progressUrl,
9577             params: {
9578                 id : uid
9579             },
9580             method: 'GET',
9581             success : function(req){
9582                //console.log(data);
9583                 var rdata = false;
9584                 var edata;
9585                 try  {
9586                    rdata = Roo.decode(req.responseText)
9587                 } catch (e) {
9588                     Roo.log("Invalid data from server..");
9589                     Roo.log(edata);
9590                     return;
9591                 }
9592                 if (!rdata || !rdata.success) {
9593                     Roo.log(rdata);
9594                     Roo.MessageBox.alert(Roo.encode(rdata));
9595                     return;
9596                 }
9597                 var data = rdata.data;
9598                 
9599                 if (this.uploadComplete) {
9600                    Roo.MessageBox.hide();
9601                    return;
9602                 }
9603                    
9604                 if (data){
9605                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9606                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9607                     );
9608                 }
9609                 this.uploadProgress.defer(2000,this);
9610             },
9611        
9612             failure: function(data) {
9613                 Roo.log('progress url failed ');
9614                 Roo.log(data);
9615             },
9616             scope : this
9617         });
9618            
9619     },
9620     
9621     
9622     run : function()
9623     {
9624         // run get Values on the form, so it syncs any secondary forms.
9625         this.form.getValues();
9626         
9627         var o = this.options;
9628         var method = this.getMethod();
9629         var isPost = method == 'POST';
9630         if(o.clientValidation === false || this.form.isValid()){
9631             
9632             if (this.form.progressUrl) {
9633                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9634                     (new Date() * 1) + '' + Math.random());
9635                     
9636             } 
9637             
9638             
9639             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9640                 form:this.form.el.dom,
9641                 url:this.getUrl(!isPost),
9642                 method: method,
9643                 params:isPost ? this.getParams() : null,
9644                 isUpload: this.form.fileUpload,
9645                 formData : this.form.formData
9646             }));
9647             
9648             this.uploadProgress();
9649
9650         }else if (o.clientValidation !== false){ // client validation failed
9651             this.failureType = Roo.form.Action.CLIENT_INVALID;
9652             this.form.afterAction(this, false);
9653         }
9654     },
9655
9656     success : function(response)
9657     {
9658         this.uploadComplete= true;
9659         if (this.haveProgress) {
9660             Roo.MessageBox.hide();
9661         }
9662         
9663         
9664         var result = this.processResponse(response);
9665         if(result === true || result.success){
9666             this.form.afterAction(this, true);
9667             return;
9668         }
9669         if(result.errors){
9670             this.form.markInvalid(result.errors);
9671             this.failureType = Roo.form.Action.SERVER_INVALID;
9672         }
9673         this.form.afterAction(this, false);
9674     },
9675     failure : function(response)
9676     {
9677         this.uploadComplete= true;
9678         if (this.haveProgress) {
9679             Roo.MessageBox.hide();
9680         }
9681         
9682         this.response = response;
9683         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9684         this.form.afterAction(this, false);
9685     },
9686     
9687     handleResponse : function(response){
9688         if(this.form.errorReader){
9689             var rs = this.form.errorReader.read(response);
9690             var errors = [];
9691             if(rs.records){
9692                 for(var i = 0, len = rs.records.length; i < len; i++) {
9693                     var r = rs.records[i];
9694                     errors[i] = r.data;
9695                 }
9696             }
9697             if(errors.length < 1){
9698                 errors = null;
9699             }
9700             return {
9701                 success : rs.success,
9702                 errors : errors
9703             };
9704         }
9705         var ret = false;
9706         try {
9707             ret = Roo.decode(response.responseText);
9708         } catch (e) {
9709             ret = {
9710                 success: false,
9711                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9712                 errors : []
9713             };
9714         }
9715         return ret;
9716         
9717     }
9718 });
9719
9720
9721 Roo.form.Action.Load = function(form, options){
9722     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9723     this.reader = this.form.reader;
9724 };
9725
9726 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9727     type : 'load',
9728
9729     run : function(){
9730         
9731         Roo.Ajax.request(Roo.apply(
9732                 this.createCallback(), {
9733                     method:this.getMethod(),
9734                     url:this.getUrl(false),
9735                     params:this.getParams()
9736         }));
9737     },
9738
9739     success : function(response){
9740         
9741         var result = this.processResponse(response);
9742         if(result === true || !result.success || !result.data){
9743             this.failureType = Roo.form.Action.LOAD_FAILURE;
9744             this.form.afterAction(this, false);
9745             return;
9746         }
9747         this.form.clearInvalid();
9748         this.form.setValues(result.data);
9749         this.form.afterAction(this, true);
9750     },
9751
9752     handleResponse : function(response){
9753         if(this.form.reader){
9754             var rs = this.form.reader.read(response);
9755             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9756             return {
9757                 success : rs.success,
9758                 data : data
9759             };
9760         }
9761         return Roo.decode(response.responseText);
9762     }
9763 });
9764
9765 Roo.form.Action.ACTION_TYPES = {
9766     'load' : Roo.form.Action.Load,
9767     'submit' : Roo.form.Action.Submit
9768 };/*
9769  * - LGPL
9770  *
9771  * form
9772  *
9773  */
9774
9775 /**
9776  * @class Roo.bootstrap.Form
9777  * @extends Roo.bootstrap.Component
9778  * Bootstrap Form class
9779  * @cfg {String} method  GET | POST (default POST)
9780  * @cfg {String} labelAlign top | left (default top)
9781  * @cfg {String} align left  | right - for navbars
9782  * @cfg {Boolean} loadMask load mask when submit (default true)
9783
9784  *
9785  * @constructor
9786  * Create a new Form
9787  * @param {Object} config The config object
9788  */
9789
9790
9791 Roo.bootstrap.Form = function(config){
9792     
9793     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9794     
9795     Roo.bootstrap.Form.popover.apply();
9796     
9797     this.addEvents({
9798         /**
9799          * @event clientvalidation
9800          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9801          * @param {Form} this
9802          * @param {Boolean} valid true if the form has passed client-side validation
9803          */
9804         clientvalidation: true,
9805         /**
9806          * @event beforeaction
9807          * Fires before any action is performed. Return false to cancel the action.
9808          * @param {Form} this
9809          * @param {Action} action The action to be performed
9810          */
9811         beforeaction: true,
9812         /**
9813          * @event actionfailed
9814          * Fires when an action fails.
9815          * @param {Form} this
9816          * @param {Action} action The action that failed
9817          */
9818         actionfailed : true,
9819         /**
9820          * @event actioncomplete
9821          * Fires when an action is completed.
9822          * @param {Form} this
9823          * @param {Action} action The action that completed
9824          */
9825         actioncomplete : true
9826     });
9827 };
9828
9829 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9830
9831      /**
9832      * @cfg {String} method
9833      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9834      */
9835     method : 'POST',
9836     /**
9837      * @cfg {String} url
9838      * The URL to use for form actions if one isn't supplied in the action options.
9839      */
9840     /**
9841      * @cfg {Boolean} fileUpload
9842      * Set to true if this form is a file upload.
9843      */
9844
9845     /**
9846      * @cfg {Object} baseParams
9847      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9848      */
9849
9850     /**
9851      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9852      */
9853     timeout: 30,
9854     /**
9855      * @cfg {Sting} align (left|right) for navbar forms
9856      */
9857     align : 'left',
9858
9859     // private
9860     activeAction : null,
9861
9862     /**
9863      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9864      * element by passing it or its id or mask the form itself by passing in true.
9865      * @type Mixed
9866      */
9867     waitMsgTarget : false,
9868
9869     loadMask : true,
9870     
9871     /**
9872      * @cfg {Boolean} errorMask (true|false) default false
9873      */
9874     errorMask : false,
9875     
9876     /**
9877      * @cfg {Number} maskOffset Default 100
9878      */
9879     maskOffset : 100,
9880     
9881     /**
9882      * @cfg {Boolean} maskBody
9883      */
9884     maskBody : false,
9885
9886     getAutoCreate : function(){
9887
9888         var cfg = {
9889             tag: 'form',
9890             method : this.method || 'POST',
9891             id : this.id || Roo.id(),
9892             cls : ''
9893         };
9894         if (this.parent().xtype.match(/^Nav/)) {
9895             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9896
9897         }
9898
9899         if (this.labelAlign == 'left' ) {
9900             cfg.cls += ' form-horizontal';
9901         }
9902
9903
9904         return cfg;
9905     },
9906     initEvents : function()
9907     {
9908         this.el.on('submit', this.onSubmit, this);
9909         // this was added as random key presses on the form where triggering form submit.
9910         this.el.on('keypress', function(e) {
9911             if (e.getCharCode() != 13) {
9912                 return true;
9913             }
9914             // we might need to allow it for textareas.. and some other items.
9915             // check e.getTarget().
9916
9917             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9918                 return true;
9919             }
9920
9921             Roo.log("keypress blocked");
9922
9923             e.preventDefault();
9924             return false;
9925         });
9926         
9927     },
9928     // private
9929     onSubmit : function(e){
9930         e.stopEvent();
9931     },
9932
9933      /**
9934      * Returns true if client-side validation on the form is successful.
9935      * @return Boolean
9936      */
9937     isValid : function(){
9938         var items = this.getItems();
9939         var valid = true;
9940         var target = false;
9941         
9942         items.each(function(f){
9943             
9944             if(f.validate()){
9945                 return;
9946             }
9947             
9948             Roo.log('invalid field: ' + f.name);
9949             
9950             valid = false;
9951
9952             if(!target && f.el.isVisible(true)){
9953                 target = f;
9954             }
9955            
9956         });
9957         
9958         if(this.errorMask && !valid){
9959             Roo.bootstrap.Form.popover.mask(this, target);
9960         }
9961         
9962         return valid;
9963     },
9964     
9965     /**
9966      * Returns true if any fields in this form have changed since their original load.
9967      * @return Boolean
9968      */
9969     isDirty : function(){
9970         var dirty = false;
9971         var items = this.getItems();
9972         items.each(function(f){
9973            if(f.isDirty()){
9974                dirty = true;
9975                return false;
9976            }
9977            return true;
9978         });
9979         return dirty;
9980     },
9981      /**
9982      * Performs a predefined action (submit or load) or custom actions you define on this form.
9983      * @param {String} actionName The name of the action type
9984      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9985      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9986      * accept other config options):
9987      * <pre>
9988 Property          Type             Description
9989 ----------------  ---------------  ----------------------------------------------------------------------------------
9990 url               String           The url for the action (defaults to the form's url)
9991 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9992 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9993 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9994                                    validate the form on the client (defaults to false)
9995      * </pre>
9996      * @return {BasicForm} this
9997      */
9998     doAction : function(action, options){
9999         if(typeof action == 'string'){
10000             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10001         }
10002         if(this.fireEvent('beforeaction', this, action) !== false){
10003             this.beforeAction(action);
10004             action.run.defer(100, action);
10005         }
10006         return this;
10007     },
10008
10009     // private
10010     beforeAction : function(action){
10011         var o = action.options;
10012         
10013         if(this.loadMask){
10014             
10015             if(this.maskBody){
10016                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10017             } else {
10018                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10019             }
10020         }
10021         // not really supported yet.. ??
10022
10023         //if(this.waitMsgTarget === true){
10024         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10025         //}else if(this.waitMsgTarget){
10026         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10027         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10028         //}else {
10029         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10030        // }
10031
10032     },
10033
10034     // private
10035     afterAction : function(action, success){
10036         this.activeAction = null;
10037         var o = action.options;
10038
10039         if(this.loadMask){
10040             
10041             if(this.maskBody){
10042                 Roo.get(document.body).unmask();
10043             } else {
10044                 this.el.unmask();
10045             }
10046         }
10047         
10048         //if(this.waitMsgTarget === true){
10049 //            this.el.unmask();
10050         //}else if(this.waitMsgTarget){
10051         //    this.waitMsgTarget.unmask();
10052         //}else{
10053         //    Roo.MessageBox.updateProgress(1);
10054         //    Roo.MessageBox.hide();
10055        // }
10056         //
10057         if(success){
10058             if(o.reset){
10059                 this.reset();
10060             }
10061             Roo.callback(o.success, o.scope, [this, action]);
10062             this.fireEvent('actioncomplete', this, action);
10063
10064         }else{
10065
10066             // failure condition..
10067             // we have a scenario where updates need confirming.
10068             // eg. if a locking scenario exists..
10069             // we look for { errors : { needs_confirm : true }} in the response.
10070             if (
10071                 (typeof(action.result) != 'undefined')  &&
10072                 (typeof(action.result.errors) != 'undefined')  &&
10073                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10074            ){
10075                 var _t = this;
10076                 Roo.log("not supported yet");
10077                  /*
10078
10079                 Roo.MessageBox.confirm(
10080                     "Change requires confirmation",
10081                     action.result.errorMsg,
10082                     function(r) {
10083                         if (r != 'yes') {
10084                             return;
10085                         }
10086                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10087                     }
10088
10089                 );
10090                 */
10091
10092
10093                 return;
10094             }
10095
10096             Roo.callback(o.failure, o.scope, [this, action]);
10097             // show an error message if no failed handler is set..
10098             if (!this.hasListener('actionfailed')) {
10099                 Roo.log("need to add dialog support");
10100                 /*
10101                 Roo.MessageBox.alert("Error",
10102                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10103                         action.result.errorMsg :
10104                         "Saving Failed, please check your entries or try again"
10105                 );
10106                 */
10107             }
10108
10109             this.fireEvent('actionfailed', this, action);
10110         }
10111
10112     },
10113     /**
10114      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10115      * @param {String} id The value to search for
10116      * @return Field
10117      */
10118     findField : function(id){
10119         var items = this.getItems();
10120         var field = items.get(id);
10121         if(!field){
10122              items.each(function(f){
10123                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10124                     field = f;
10125                     return false;
10126                 }
10127                 return true;
10128             });
10129         }
10130         return field || null;
10131     },
10132      /**
10133      * Mark fields in this form invalid in bulk.
10134      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10135      * @return {BasicForm} this
10136      */
10137     markInvalid : function(errors){
10138         if(errors instanceof Array){
10139             for(var i = 0, len = errors.length; i < len; i++){
10140                 var fieldError = errors[i];
10141                 var f = this.findField(fieldError.id);
10142                 if(f){
10143                     f.markInvalid(fieldError.msg);
10144                 }
10145             }
10146         }else{
10147             var field, id;
10148             for(id in errors){
10149                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10150                     field.markInvalid(errors[id]);
10151                 }
10152             }
10153         }
10154         //Roo.each(this.childForms || [], function (f) {
10155         //    f.markInvalid(errors);
10156         //});
10157
10158         return this;
10159     },
10160
10161     /**
10162      * Set values for fields in this form in bulk.
10163      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10164      * @return {BasicForm} this
10165      */
10166     setValues : function(values){
10167         if(values instanceof Array){ // array of objects
10168             for(var i = 0, len = values.length; i < len; i++){
10169                 var v = values[i];
10170                 var f = this.findField(v.id);
10171                 if(f){
10172                     f.setValue(v.value);
10173                     if(this.trackResetOnLoad){
10174                         f.originalValue = f.getValue();
10175                     }
10176                 }
10177             }
10178         }else{ // object hash
10179             var field, id;
10180             for(id in values){
10181                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10182
10183                     if (field.setFromData &&
10184                         field.valueField &&
10185                         field.displayField &&
10186                         // combos' with local stores can
10187                         // be queried via setValue()
10188                         // to set their value..
10189                         (field.store && !field.store.isLocal)
10190                         ) {
10191                         // it's a combo
10192                         var sd = { };
10193                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10194                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10195                         field.setFromData(sd);
10196
10197                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10198                         
10199                         field.setFromData(values);
10200                         
10201                     } else {
10202                         field.setValue(values[id]);
10203                     }
10204
10205
10206                     if(this.trackResetOnLoad){
10207                         field.originalValue = field.getValue();
10208                     }
10209                 }
10210             }
10211         }
10212
10213         //Roo.each(this.childForms || [], function (f) {
10214         //    f.setValues(values);
10215         //});
10216
10217         return this;
10218     },
10219
10220     /**
10221      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10222      * they are returned as an array.
10223      * @param {Boolean} asString
10224      * @return {Object}
10225      */
10226     getValues : function(asString){
10227         //if (this.childForms) {
10228             // copy values from the child forms
10229         //    Roo.each(this.childForms, function (f) {
10230         //        this.setValues(f.getValues());
10231         //    }, this);
10232         //}
10233
10234
10235
10236         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10237         if(asString === true){
10238             return fs;
10239         }
10240         return Roo.urlDecode(fs);
10241     },
10242
10243     /**
10244      * Returns the fields in this form as an object with key/value pairs.
10245      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10246      * @return {Object}
10247      */
10248     getFieldValues : function(with_hidden)
10249     {
10250         var items = this.getItems();
10251         var ret = {};
10252         items.each(function(f){
10253             
10254             if (!f.getName()) {
10255                 return;
10256             }
10257             
10258             var v = f.getValue();
10259             
10260             if (f.inputType =='radio') {
10261                 if (typeof(ret[f.getName()]) == 'undefined') {
10262                     ret[f.getName()] = ''; // empty..
10263                 }
10264
10265                 if (!f.el.dom.checked) {
10266                     return;
10267
10268                 }
10269                 v = f.el.dom.value;
10270
10271             }
10272             
10273             if(f.xtype == 'MoneyField'){
10274                 ret[f.currencyName] = f.getCurrency();
10275             }
10276
10277             // not sure if this supported any more..
10278             if ((typeof(v) == 'object') && f.getRawValue) {
10279                 v = f.getRawValue() ; // dates..
10280             }
10281             // combo boxes where name != hiddenName...
10282             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10283                 ret[f.name] = f.getRawValue();
10284             }
10285             ret[f.getName()] = v;
10286         });
10287
10288         return ret;
10289     },
10290
10291     /**
10292      * Clears all invalid messages in this form.
10293      * @return {BasicForm} this
10294      */
10295     clearInvalid : function(){
10296         var items = this.getItems();
10297
10298         items.each(function(f){
10299            f.clearInvalid();
10300         });
10301
10302         return this;
10303     },
10304
10305     /**
10306      * Resets this form.
10307      * @return {BasicForm} this
10308      */
10309     reset : function(){
10310         var items = this.getItems();
10311         items.each(function(f){
10312             f.reset();
10313         });
10314
10315         Roo.each(this.childForms || [], function (f) {
10316             f.reset();
10317         });
10318
10319
10320         return this;
10321     },
10322     
10323     getItems : function()
10324     {
10325         var r=new Roo.util.MixedCollection(false, function(o){
10326             return o.id || (o.id = Roo.id());
10327         });
10328         var iter = function(el) {
10329             if (el.inputEl) {
10330                 r.add(el);
10331             }
10332             if (!el.items) {
10333                 return;
10334             }
10335             Roo.each(el.items,function(e) {
10336                 iter(e);
10337             });
10338         };
10339
10340         iter(this);
10341         return r;
10342     },
10343     
10344     hideFields : function(items)
10345     {
10346         Roo.each(items, function(i){
10347             
10348             var f = this.findField(i);
10349             
10350             if(!f){
10351                 return;
10352             }
10353             
10354             f.hide();
10355             
10356         }, this);
10357     },
10358     
10359     showFields : function(items)
10360     {
10361         Roo.each(items, function(i){
10362             
10363             var f = this.findField(i);
10364             
10365             if(!f){
10366                 return;
10367             }
10368             
10369             f.show();
10370             
10371         }, this);
10372     }
10373
10374 });
10375
10376 Roo.apply(Roo.bootstrap.Form, {
10377     
10378     popover : {
10379         
10380         padding : 5,
10381         
10382         isApplied : false,
10383         
10384         isMasked : false,
10385         
10386         form : false,
10387         
10388         target : false,
10389         
10390         toolTip : false,
10391         
10392         intervalID : false,
10393         
10394         maskEl : false,
10395         
10396         apply : function()
10397         {
10398             if(this.isApplied){
10399                 return;
10400             }
10401             
10402             this.maskEl = {
10403                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10404                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10405                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10406                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10407             };
10408             
10409             this.maskEl.top.enableDisplayMode("block");
10410             this.maskEl.left.enableDisplayMode("block");
10411             this.maskEl.bottom.enableDisplayMode("block");
10412             this.maskEl.right.enableDisplayMode("block");
10413             
10414             this.toolTip = new Roo.bootstrap.Tooltip({
10415                 cls : 'roo-form-error-popover',
10416                 alignment : {
10417                     'left' : ['r-l', [-2,0], 'right'],
10418                     'right' : ['l-r', [2,0], 'left'],
10419                     'bottom' : ['tl-bl', [0,2], 'top'],
10420                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10421                 }
10422             });
10423             
10424             this.toolTip.render(Roo.get(document.body));
10425
10426             this.toolTip.el.enableDisplayMode("block");
10427             
10428             Roo.get(document.body).on('click', function(){
10429                 this.unmask();
10430             }, this);
10431             
10432             Roo.get(document.body).on('touchstart', function(){
10433                 this.unmask();
10434             }, this);
10435             
10436             this.isApplied = true
10437         },
10438         
10439         mask : function(form, target)
10440         {
10441             this.form = form;
10442             
10443             this.target = target;
10444             
10445             if(!this.form.errorMask || !target.el){
10446                 return;
10447             }
10448             
10449             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10450             
10451             Roo.log(scrollable);
10452             
10453             var ot = this.target.el.calcOffsetsTo(scrollable);
10454             
10455             var scrollTo = ot[1] - this.form.maskOffset;
10456             
10457             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10458             
10459             scrollable.scrollTo('top', scrollTo);
10460             
10461             var box = this.target.el.getBox();
10462             Roo.log(box);
10463             var zIndex = Roo.bootstrap.Modal.zIndex++;
10464
10465             
10466             this.maskEl.top.setStyle('position', 'absolute');
10467             this.maskEl.top.setStyle('z-index', zIndex);
10468             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10469             this.maskEl.top.setLeft(0);
10470             this.maskEl.top.setTop(0);
10471             this.maskEl.top.show();
10472             
10473             this.maskEl.left.setStyle('position', 'absolute');
10474             this.maskEl.left.setStyle('z-index', zIndex);
10475             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10476             this.maskEl.left.setLeft(0);
10477             this.maskEl.left.setTop(box.y - this.padding);
10478             this.maskEl.left.show();
10479
10480             this.maskEl.bottom.setStyle('position', 'absolute');
10481             this.maskEl.bottom.setStyle('z-index', zIndex);
10482             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10483             this.maskEl.bottom.setLeft(0);
10484             this.maskEl.bottom.setTop(box.bottom + this.padding);
10485             this.maskEl.bottom.show();
10486
10487             this.maskEl.right.setStyle('position', 'absolute');
10488             this.maskEl.right.setStyle('z-index', zIndex);
10489             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10490             this.maskEl.right.setLeft(box.right + this.padding);
10491             this.maskEl.right.setTop(box.y - this.padding);
10492             this.maskEl.right.show();
10493
10494             this.toolTip.bindEl = this.target.el;
10495
10496             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10497
10498             var tip = this.target.blankText;
10499
10500             if(this.target.getValue() !== '' ) {
10501                 
10502                 if (this.target.invalidText.length) {
10503                     tip = this.target.invalidText;
10504                 } else if (this.target.regexText.length){
10505                     tip = this.target.regexText;
10506                 }
10507             }
10508
10509             this.toolTip.show(tip);
10510
10511             this.intervalID = window.setInterval(function() {
10512                 Roo.bootstrap.Form.popover.unmask();
10513             }, 10000);
10514
10515             window.onwheel = function(){ return false;};
10516             
10517             (function(){ this.isMasked = true; }).defer(500, this);
10518             
10519         },
10520         
10521         unmask : function()
10522         {
10523             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10524                 return;
10525             }
10526             
10527             this.maskEl.top.setStyle('position', 'absolute');
10528             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10529             this.maskEl.top.hide();
10530
10531             this.maskEl.left.setStyle('position', 'absolute');
10532             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10533             this.maskEl.left.hide();
10534
10535             this.maskEl.bottom.setStyle('position', 'absolute');
10536             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10537             this.maskEl.bottom.hide();
10538
10539             this.maskEl.right.setStyle('position', 'absolute');
10540             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10541             this.maskEl.right.hide();
10542             
10543             this.toolTip.hide();
10544             
10545             this.toolTip.el.hide();
10546             
10547             window.onwheel = function(){ return true;};
10548             
10549             if(this.intervalID){
10550                 window.clearInterval(this.intervalID);
10551                 this.intervalID = false;
10552             }
10553             
10554             this.isMasked = false;
10555             
10556         }
10557         
10558     }
10559     
10560 });
10561
10562 /*
10563  * Based on:
10564  * Ext JS Library 1.1.1
10565  * Copyright(c) 2006-2007, Ext JS, LLC.
10566  *
10567  * Originally Released Under LGPL - original licence link has changed is not relivant.
10568  *
10569  * Fork - LGPL
10570  * <script type="text/javascript">
10571  */
10572 /**
10573  * @class Roo.form.VTypes
10574  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10575  * @singleton
10576  */
10577 Roo.form.VTypes = function(){
10578     // closure these in so they are only created once.
10579     var alpha = /^[a-zA-Z_]+$/;
10580     var alphanum = /^[a-zA-Z0-9_]+$/;
10581     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10582     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10583
10584     // All these messages and functions are configurable
10585     return {
10586         /**
10587          * The function used to validate email addresses
10588          * @param {String} value The email address
10589          */
10590         'email' : function(v){
10591             return email.test(v);
10592         },
10593         /**
10594          * The error text to display when the email validation function returns false
10595          * @type String
10596          */
10597         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10598         /**
10599          * The keystroke filter mask to be applied on email input
10600          * @type RegExp
10601          */
10602         'emailMask' : /[a-z0-9_\.\-@]/i,
10603
10604         /**
10605          * The function used to validate URLs
10606          * @param {String} value The URL
10607          */
10608         'url' : function(v){
10609             return url.test(v);
10610         },
10611         /**
10612          * The error text to display when the url validation function returns false
10613          * @type String
10614          */
10615         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10616         
10617         /**
10618          * The function used to validate alpha values
10619          * @param {String} value The value
10620          */
10621         'alpha' : function(v){
10622             return alpha.test(v);
10623         },
10624         /**
10625          * The error text to display when the alpha validation function returns false
10626          * @type String
10627          */
10628         'alphaText' : 'This field should only contain letters and _',
10629         /**
10630          * The keystroke filter mask to be applied on alpha input
10631          * @type RegExp
10632          */
10633         'alphaMask' : /[a-z_]/i,
10634
10635         /**
10636          * The function used to validate alphanumeric values
10637          * @param {String} value The value
10638          */
10639         'alphanum' : function(v){
10640             return alphanum.test(v);
10641         },
10642         /**
10643          * The error text to display when the alphanumeric validation function returns false
10644          * @type String
10645          */
10646         'alphanumText' : 'This field should only contain letters, numbers and _',
10647         /**
10648          * The keystroke filter mask to be applied on alphanumeric input
10649          * @type RegExp
10650          */
10651         'alphanumMask' : /[a-z0-9_]/i
10652     };
10653 }();/*
10654  * - LGPL
10655  *
10656  * Input
10657  * 
10658  */
10659
10660 /**
10661  * @class Roo.bootstrap.Input
10662  * @extends Roo.bootstrap.Component
10663  * Bootstrap Input class
10664  * @cfg {Boolean} disabled is it disabled
10665  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10666  * @cfg {String} name name of the input
10667  * @cfg {string} fieldLabel - the label associated
10668  * @cfg {string} placeholder - placeholder to put in text.
10669  * @cfg {string}  before - input group add on before
10670  * @cfg {string} after - input group add on after
10671  * @cfg {string} size - (lg|sm) or leave empty..
10672  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10673  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10674  * @cfg {Number} md colspan out of 12 for computer-sized screens
10675  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10676  * @cfg {string} value default value of the input
10677  * @cfg {Number} labelWidth set the width of label 
10678  * @cfg {Number} labellg set the width of label (1-12)
10679  * @cfg {Number} labelmd set the width of label (1-12)
10680  * @cfg {Number} labelsm set the width of label (1-12)
10681  * @cfg {Number} labelxs set the width of label (1-12)
10682  * @cfg {String} labelAlign (top|left)
10683  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10684  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10685  * @cfg {String} indicatorpos (left|right) default left
10686  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10687  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10688  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10689
10690  * @cfg {String} align (left|center|right) Default left
10691  * @cfg {Boolean} forceFeedback (true|false) Default false
10692  * 
10693  * @constructor
10694  * Create a new Input
10695  * @param {Object} config The config object
10696  */
10697
10698 Roo.bootstrap.Input = function(config){
10699     
10700     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10701     
10702     this.addEvents({
10703         /**
10704          * @event focus
10705          * Fires when this field receives input focus.
10706          * @param {Roo.form.Field} this
10707          */
10708         focus : true,
10709         /**
10710          * @event blur
10711          * Fires when this field loses input focus.
10712          * @param {Roo.form.Field} this
10713          */
10714         blur : true,
10715         /**
10716          * @event specialkey
10717          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10718          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10719          * @param {Roo.form.Field} this
10720          * @param {Roo.EventObject} e The event object
10721          */
10722         specialkey : true,
10723         /**
10724          * @event change
10725          * Fires just before the field blurs if the field value has changed.
10726          * @param {Roo.form.Field} this
10727          * @param {Mixed} newValue The new value
10728          * @param {Mixed} oldValue The original value
10729          */
10730         change : true,
10731         /**
10732          * @event invalid
10733          * Fires after the field has been marked as invalid.
10734          * @param {Roo.form.Field} this
10735          * @param {String} msg The validation message
10736          */
10737         invalid : true,
10738         /**
10739          * @event valid
10740          * Fires after the field has been validated with no errors.
10741          * @param {Roo.form.Field} this
10742          */
10743         valid : true,
10744          /**
10745          * @event keyup
10746          * Fires after the key up
10747          * @param {Roo.form.Field} this
10748          * @param {Roo.EventObject}  e The event Object
10749          */
10750         keyup : true,
10751         /**
10752          * @event paste
10753          * Fires after the user pastes into input
10754          * @param {Roo.form.Field} this
10755          * @param {Roo.EventObject}  e The event Object
10756          */
10757         paste : true
10758     });
10759 };
10760
10761 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10762      /**
10763      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10764       automatic validation (defaults to "keyup").
10765      */
10766     validationEvent : "keyup",
10767      /**
10768      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10769      */
10770     validateOnBlur : true,
10771     /**
10772      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10773      */
10774     validationDelay : 250,
10775      /**
10776      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10777      */
10778     focusClass : "x-form-focus",  // not needed???
10779     
10780        
10781     /**
10782      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10783      */
10784     invalidClass : "has-warning",
10785     
10786     /**
10787      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10788      */
10789     validClass : "has-success",
10790     
10791     /**
10792      * @cfg {Boolean} hasFeedback (true|false) default true
10793      */
10794     hasFeedback : true,
10795     
10796     /**
10797      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10798      */
10799     invalidFeedbackClass : "glyphicon-warning-sign",
10800     
10801     /**
10802      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10803      */
10804     validFeedbackClass : "glyphicon-ok",
10805     
10806     /**
10807      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10808      */
10809     selectOnFocus : false,
10810     
10811      /**
10812      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10813      */
10814     maskRe : null,
10815        /**
10816      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10817      */
10818     vtype : null,
10819     
10820       /**
10821      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10822      */
10823     disableKeyFilter : false,
10824     
10825        /**
10826      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10827      */
10828     disabled : false,
10829      /**
10830      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10831      */
10832     allowBlank : true,
10833     /**
10834      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10835      */
10836     blankText : "Please complete this mandatory field",
10837     
10838      /**
10839      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10840      */
10841     minLength : 0,
10842     /**
10843      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10844      */
10845     maxLength : Number.MAX_VALUE,
10846     /**
10847      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10848      */
10849     minLengthText : "The minimum length for this field is {0}",
10850     /**
10851      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10852      */
10853     maxLengthText : "The maximum length for this field is {0}",
10854   
10855     
10856     /**
10857      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10858      * If available, this function will be called only after the basic validators all return true, and will be passed the
10859      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10860      */
10861     validator : null,
10862     /**
10863      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10864      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10865      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10866      */
10867     regex : null,
10868     /**
10869      * @cfg {String} regexText -- Depricated - use Invalid Text
10870      */
10871     regexText : "",
10872     
10873     /**
10874      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10875      */
10876     invalidText : "",
10877     
10878     
10879     
10880     autocomplete: false,
10881     
10882     
10883     fieldLabel : '',
10884     inputType : 'text',
10885     
10886     name : false,
10887     placeholder: false,
10888     before : false,
10889     after : false,
10890     size : false,
10891     hasFocus : false,
10892     preventMark: false,
10893     isFormField : true,
10894     value : '',
10895     labelWidth : 2,
10896     labelAlign : false,
10897     readOnly : false,
10898     align : false,
10899     formatedValue : false,
10900     forceFeedback : false,
10901     
10902     indicatorpos : 'left',
10903     
10904     labellg : 0,
10905     labelmd : 0,
10906     labelsm : 0,
10907     labelxs : 0,
10908     
10909     capture : '',
10910     accept : '',
10911     
10912     parentLabelAlign : function()
10913     {
10914         var parent = this;
10915         while (parent.parent()) {
10916             parent = parent.parent();
10917             if (typeof(parent.labelAlign) !='undefined') {
10918                 return parent.labelAlign;
10919             }
10920         }
10921         return 'left';
10922         
10923     },
10924     
10925     getAutoCreate : function()
10926     {
10927         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10928         
10929         var id = Roo.id();
10930         
10931         var cfg = {};
10932         
10933         if(this.inputType != 'hidden'){
10934             cfg.cls = 'form-group' //input-group
10935         }
10936         
10937         var input =  {
10938             tag: 'input',
10939             id : id,
10940             type : this.inputType,
10941             value : this.value,
10942             cls : 'form-control',
10943             placeholder : this.placeholder || '',
10944             autocomplete : this.autocomplete || 'new-password'
10945         };
10946         if (this.inputType == 'file') {
10947             input.style = 'overflow:hidden'; // why not in CSS?
10948         }
10949         
10950         if(this.capture.length){
10951             input.capture = this.capture;
10952         }
10953         
10954         if(this.accept.length){
10955             input.accept = this.accept + "/*";
10956         }
10957         
10958         if(this.align){
10959             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10960         }
10961         
10962         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10963             input.maxLength = this.maxLength;
10964         }
10965         
10966         if (this.disabled) {
10967             input.disabled=true;
10968         }
10969         
10970         if (this.readOnly) {
10971             input.readonly=true;
10972         }
10973         
10974         if (this.name) {
10975             input.name = this.name;
10976         }
10977         
10978         if (this.size) {
10979             input.cls += ' input-' + this.size;
10980         }
10981         
10982         var settings=this;
10983         ['xs','sm','md','lg'].map(function(size){
10984             if (settings[size]) {
10985                 cfg.cls += ' col-' + size + '-' + settings[size];
10986             }
10987         });
10988         
10989         var inputblock = input;
10990         
10991         var feedback = {
10992             tag: 'span',
10993             cls: 'glyphicon form-control-feedback'
10994         };
10995             
10996         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10997             
10998             inputblock = {
10999                 cls : 'has-feedback',
11000                 cn :  [
11001                     input,
11002                     feedback
11003                 ] 
11004             };  
11005         }
11006         
11007         if (this.before || this.after) {
11008             
11009             inputblock = {
11010                 cls : 'input-group',
11011                 cn :  [] 
11012             };
11013             
11014             if (this.before && typeof(this.before) == 'string') {
11015                 
11016                 inputblock.cn.push({
11017                     tag :'span',
11018                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11019                     html : this.before
11020                 });
11021             }
11022             if (this.before && typeof(this.before) == 'object') {
11023                 this.before = Roo.factory(this.before);
11024                 
11025                 inputblock.cn.push({
11026                     tag :'span',
11027                     cls : 'roo-input-before input-group-prepend   input-group-' +
11028                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11029                 });
11030             }
11031             
11032             inputblock.cn.push(input);
11033             
11034             if (this.after && typeof(this.after) == 'string') {
11035                 inputblock.cn.push({
11036                     tag :'span',
11037                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11038                     html : this.after
11039                 });
11040             }
11041             if (this.after && typeof(this.after) == 'object') {
11042                 this.after = Roo.factory(this.after);
11043                 
11044                 inputblock.cn.push({
11045                     tag :'span',
11046                     cls : 'roo-input-after input-group-append  input-group-' +
11047                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11048                 });
11049             }
11050             
11051             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11052                 inputblock.cls += ' has-feedback';
11053                 inputblock.cn.push(feedback);
11054             }
11055         };
11056         var indicator = {
11057             tag : 'i',
11058             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11059             tooltip : 'This field is required'
11060         };
11061         if (this.allowBlank ) {
11062             indicator.style = this.allowBlank ? ' display:none' : '';
11063         }
11064         if (align ==='left' && this.fieldLabel.length) {
11065             
11066             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11067             
11068             cfg.cn = [
11069                 indicator,
11070                 {
11071                     tag: 'label',
11072                     'for' :  id,
11073                     cls : 'control-label col-form-label',
11074                     html : this.fieldLabel
11075
11076                 },
11077                 {
11078                     cls : "", 
11079                     cn: [
11080                         inputblock
11081                     ]
11082                 }
11083             ];
11084             
11085             var labelCfg = cfg.cn[1];
11086             var contentCfg = cfg.cn[2];
11087             
11088             if(this.indicatorpos == 'right'){
11089                 cfg.cn = [
11090                     {
11091                         tag: 'label',
11092                         'for' :  id,
11093                         cls : 'control-label col-form-label',
11094                         cn : [
11095                             {
11096                                 tag : 'span',
11097                                 html : this.fieldLabel
11098                             },
11099                             indicator
11100                         ]
11101                     },
11102                     {
11103                         cls : "",
11104                         cn: [
11105                             inputblock
11106                         ]
11107                     }
11108
11109                 ];
11110                 
11111                 labelCfg = cfg.cn[0];
11112                 contentCfg = cfg.cn[1];
11113             
11114             }
11115             
11116             if(this.labelWidth > 12){
11117                 labelCfg.style = "width: " + this.labelWidth + 'px';
11118             }
11119             
11120             if(this.labelWidth < 13 && this.labelmd == 0){
11121                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11122             }
11123             
11124             if(this.labellg > 0){
11125                 labelCfg.cls += ' col-lg-' + this.labellg;
11126                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11127             }
11128             
11129             if(this.labelmd > 0){
11130                 labelCfg.cls += ' col-md-' + this.labelmd;
11131                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11132             }
11133             
11134             if(this.labelsm > 0){
11135                 labelCfg.cls += ' col-sm-' + this.labelsm;
11136                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11137             }
11138             
11139             if(this.labelxs > 0){
11140                 labelCfg.cls += ' col-xs-' + this.labelxs;
11141                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11142             }
11143             
11144             
11145         } else if ( this.fieldLabel.length) {
11146                 
11147             
11148             
11149             cfg.cn = [
11150                 {
11151                     tag : 'i',
11152                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11153                     tooltip : 'This field is required',
11154                     style : this.allowBlank ? ' display:none' : '' 
11155                 },
11156                 {
11157                     tag: 'label',
11158                    //cls : 'input-group-addon',
11159                     html : this.fieldLabel
11160
11161                 },
11162
11163                inputblock
11164
11165            ];
11166            
11167            if(this.indicatorpos == 'right'){
11168        
11169                 cfg.cn = [
11170                     {
11171                         tag: 'label',
11172                        //cls : 'input-group-addon',
11173                         html : this.fieldLabel
11174
11175                     },
11176                     {
11177                         tag : 'i',
11178                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11179                         tooltip : 'This field is required',
11180                         style : this.allowBlank ? ' display:none' : '' 
11181                     },
11182
11183                    inputblock
11184
11185                ];
11186
11187             }
11188
11189         } else {
11190             
11191             cfg.cn = [
11192
11193                     inputblock
11194
11195             ];
11196                 
11197                 
11198         };
11199         
11200         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11201            cfg.cls += ' navbar-form';
11202         }
11203         
11204         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11205             // on BS4 we do this only if not form 
11206             cfg.cls += ' navbar-form';
11207             cfg.tag = 'li';
11208         }
11209         
11210         return cfg;
11211         
11212     },
11213     /**
11214      * return the real input element.
11215      */
11216     inputEl: function ()
11217     {
11218         return this.el.select('input.form-control',true).first();
11219     },
11220     
11221     tooltipEl : function()
11222     {
11223         return this.inputEl();
11224     },
11225     
11226     indicatorEl : function()
11227     {
11228         if (Roo.bootstrap.version == 4) {
11229             return false; // not enabled in v4 yet.
11230         }
11231         
11232         var indicator = this.el.select('i.roo-required-indicator',true).first();
11233         
11234         if(!indicator){
11235             return false;
11236         }
11237         
11238         return indicator;
11239         
11240     },
11241     
11242     setDisabled : function(v)
11243     {
11244         var i  = this.inputEl().dom;
11245         if (!v) {
11246             i.removeAttribute('disabled');
11247             return;
11248             
11249         }
11250         i.setAttribute('disabled','true');
11251     },
11252     initEvents : function()
11253     {
11254           
11255         this.inputEl().on("keydown" , this.fireKey,  this);
11256         this.inputEl().on("focus", this.onFocus,  this);
11257         this.inputEl().on("blur", this.onBlur,  this);
11258         
11259         this.inputEl().relayEvent('keyup', this);
11260         this.inputEl().relayEvent('paste', this);
11261         
11262         this.indicator = this.indicatorEl();
11263         
11264         if(this.indicator){
11265             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11266         }
11267  
11268         // reference to original value for reset
11269         this.originalValue = this.getValue();
11270         //Roo.form.TextField.superclass.initEvents.call(this);
11271         if(this.validationEvent == 'keyup'){
11272             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11273             this.inputEl().on('keyup', this.filterValidation, this);
11274         }
11275         else if(this.validationEvent !== false){
11276             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11277         }
11278         
11279         if(this.selectOnFocus){
11280             this.on("focus", this.preFocus, this);
11281             
11282         }
11283         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11284             this.inputEl().on("keypress", this.filterKeys, this);
11285         } else {
11286             this.inputEl().relayEvent('keypress', this);
11287         }
11288        /* if(this.grow){
11289             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11290             this.el.on("click", this.autoSize,  this);
11291         }
11292         */
11293         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11294             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11295         }
11296         
11297         if (typeof(this.before) == 'object') {
11298             this.before.render(this.el.select('.roo-input-before',true).first());
11299         }
11300         if (typeof(this.after) == 'object') {
11301             this.after.render(this.el.select('.roo-input-after',true).first());
11302         }
11303         
11304         this.inputEl().on('change', this.onChange, this);
11305         
11306     },
11307     filterValidation : function(e){
11308         if(!e.isNavKeyPress()){
11309             this.validationTask.delay(this.validationDelay);
11310         }
11311     },
11312      /**
11313      * Validates the field value
11314      * @return {Boolean} True if the value is valid, else false
11315      */
11316     validate : function(){
11317         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11318         if(this.disabled || this.validateValue(this.getRawValue())){
11319             this.markValid();
11320             return true;
11321         }
11322         
11323         this.markInvalid();
11324         return false;
11325     },
11326     
11327     
11328     /**
11329      * Validates a value according to the field's validation rules and marks the field as invalid
11330      * if the validation fails
11331      * @param {Mixed} value The value to validate
11332      * @return {Boolean} True if the value is valid, else false
11333      */
11334     validateValue : function(value)
11335     {
11336         if(this.getVisibilityEl().hasClass('hidden')){
11337             return true;
11338         }
11339         
11340         if(value.length < 1)  { // if it's blank
11341             if(this.allowBlank){
11342                 return true;
11343             }
11344             return false;
11345         }
11346         
11347         if(value.length < this.minLength){
11348             return false;
11349         }
11350         if(value.length > this.maxLength){
11351             return false;
11352         }
11353         if(this.vtype){
11354             var vt = Roo.form.VTypes;
11355             if(!vt[this.vtype](value, this)){
11356                 return false;
11357             }
11358         }
11359         if(typeof this.validator == "function"){
11360             var msg = this.validator(value);
11361             if(msg !== true){
11362                 return false;
11363             }
11364             if (typeof(msg) == 'string') {
11365                 this.invalidText = msg;
11366             }
11367         }
11368         
11369         if(this.regex && !this.regex.test(value)){
11370             return false;
11371         }
11372         
11373         return true;
11374     },
11375     
11376      // private
11377     fireKey : function(e){
11378         //Roo.log('field ' + e.getKey());
11379         if(e.isNavKeyPress()){
11380             this.fireEvent("specialkey", this, e);
11381         }
11382     },
11383     focus : function (selectText){
11384         if(this.rendered){
11385             this.inputEl().focus();
11386             if(selectText === true){
11387                 this.inputEl().dom.select();
11388             }
11389         }
11390         return this;
11391     } ,
11392     
11393     onFocus : function(){
11394         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11395            // this.el.addClass(this.focusClass);
11396         }
11397         if(!this.hasFocus){
11398             this.hasFocus = true;
11399             this.startValue = this.getValue();
11400             this.fireEvent("focus", this);
11401         }
11402     },
11403     
11404     beforeBlur : Roo.emptyFn,
11405
11406     
11407     // private
11408     onBlur : function(){
11409         this.beforeBlur();
11410         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11411             //this.el.removeClass(this.focusClass);
11412         }
11413         this.hasFocus = false;
11414         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11415             this.validate();
11416         }
11417         var v = this.getValue();
11418         if(String(v) !== String(this.startValue)){
11419             this.fireEvent('change', this, v, this.startValue);
11420         }
11421         this.fireEvent("blur", this);
11422     },
11423     
11424     onChange : function(e)
11425     {
11426         var v = this.getValue();
11427         if(String(v) !== String(this.startValue)){
11428             this.fireEvent('change', this, v, this.startValue);
11429         }
11430         
11431     },
11432     
11433     /**
11434      * Resets the current field value to the originally loaded value and clears any validation messages
11435      */
11436     reset : function(){
11437         this.setValue(this.originalValue);
11438         this.validate();
11439     },
11440      /**
11441      * Returns the name of the field
11442      * @return {Mixed} name The name field
11443      */
11444     getName: function(){
11445         return this.name;
11446     },
11447      /**
11448      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11449      * @return {Mixed} value The field value
11450      */
11451     getValue : function(){
11452         
11453         var v = this.inputEl().getValue();
11454         
11455         return v;
11456     },
11457     /**
11458      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11459      * @return {Mixed} value The field value
11460      */
11461     getRawValue : function(){
11462         var v = this.inputEl().getValue();
11463         
11464         return v;
11465     },
11466     
11467     /**
11468      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11469      * @param {Mixed} value The value to set
11470      */
11471     setRawValue : function(v){
11472         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11473     },
11474     
11475     selectText : function(start, end){
11476         var v = this.getRawValue();
11477         if(v.length > 0){
11478             start = start === undefined ? 0 : start;
11479             end = end === undefined ? v.length : end;
11480             var d = this.inputEl().dom;
11481             if(d.setSelectionRange){
11482                 d.setSelectionRange(start, end);
11483             }else if(d.createTextRange){
11484                 var range = d.createTextRange();
11485                 range.moveStart("character", start);
11486                 range.moveEnd("character", v.length-end);
11487                 range.select();
11488             }
11489         }
11490     },
11491     
11492     /**
11493      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11494      * @param {Mixed} value The value to set
11495      */
11496     setValue : function(v){
11497         this.value = v;
11498         if(this.rendered){
11499             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11500             this.validate();
11501         }
11502     },
11503     
11504     /*
11505     processValue : function(value){
11506         if(this.stripCharsRe){
11507             var newValue = value.replace(this.stripCharsRe, '');
11508             if(newValue !== value){
11509                 this.setRawValue(newValue);
11510                 return newValue;
11511             }
11512         }
11513         return value;
11514     },
11515   */
11516     preFocus : function(){
11517         
11518         if(this.selectOnFocus){
11519             this.inputEl().dom.select();
11520         }
11521     },
11522     filterKeys : function(e){
11523         var k = e.getKey();
11524         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11525             return;
11526         }
11527         var c = e.getCharCode(), cc = String.fromCharCode(c);
11528         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11529             return;
11530         }
11531         if(!this.maskRe.test(cc)){
11532             e.stopEvent();
11533         }
11534     },
11535      /**
11536      * Clear any invalid styles/messages for this field
11537      */
11538     clearInvalid : function(){
11539         
11540         if(!this.el || this.preventMark){ // not rendered
11541             return;
11542         }
11543         
11544         
11545         this.el.removeClass([this.invalidClass, 'is-invalid']);
11546         
11547         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11548             
11549             var feedback = this.el.select('.form-control-feedback', true).first();
11550             
11551             if(feedback){
11552                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11553             }
11554             
11555         }
11556         
11557         if(this.indicator){
11558             this.indicator.removeClass('visible');
11559             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11560         }
11561         
11562         this.fireEvent('valid', this);
11563     },
11564     
11565      /**
11566      * Mark this field as valid
11567      */
11568     markValid : function()
11569     {
11570         if(!this.el  || this.preventMark){ // not rendered...
11571             return;
11572         }
11573         
11574         this.el.removeClass([this.invalidClass, this.validClass]);
11575         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11576
11577         var feedback = this.el.select('.form-control-feedback', true).first();
11578             
11579         if(feedback){
11580             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11581         }
11582         
11583         if(this.indicator){
11584             this.indicator.removeClass('visible');
11585             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11586         }
11587         
11588         if(this.disabled){
11589             return;
11590         }
11591         
11592            
11593         if(this.allowBlank && !this.getRawValue().length){
11594             return;
11595         }
11596         if (Roo.bootstrap.version == 3) {
11597             this.el.addClass(this.validClass);
11598         } else {
11599             this.inputEl().addClass('is-valid');
11600         }
11601
11602         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11603             
11604             var feedback = this.el.select('.form-control-feedback', true).first();
11605             
11606             if(feedback){
11607                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11608                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11609             }
11610             
11611         }
11612         
11613         this.fireEvent('valid', this);
11614     },
11615     
11616      /**
11617      * Mark this field as invalid
11618      * @param {String} msg The validation message
11619      */
11620     markInvalid : function(msg)
11621     {
11622         if(!this.el  || this.preventMark){ // not rendered
11623             return;
11624         }
11625         
11626         this.el.removeClass([this.invalidClass, this.validClass]);
11627         this.inputEl().removeClass(['is-valid', 'is-invalid']);
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(
11633                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11634         }
11635
11636         if(this.disabled){
11637             return;
11638         }
11639         
11640         if(this.allowBlank && !this.getRawValue().length){
11641             return;
11642         }
11643         
11644         if(this.indicator){
11645             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11646             this.indicator.addClass('visible');
11647         }
11648         if (Roo.bootstrap.version == 3) {
11649             this.el.addClass(this.invalidClass);
11650         } else {
11651             this.inputEl().addClass('is-invalid');
11652         }
11653         
11654         
11655         
11656         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11657             
11658             var feedback = this.el.select('.form-control-feedback', true).first();
11659             
11660             if(feedback){
11661                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11662                 
11663                 if(this.getValue().length || this.forceFeedback){
11664                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11665                 }
11666                 
11667             }
11668             
11669         }
11670         
11671         this.fireEvent('invalid', this, msg);
11672     },
11673     // private
11674     SafariOnKeyDown : function(event)
11675     {
11676         // this is a workaround for a password hang bug on chrome/ webkit.
11677         if (this.inputEl().dom.type != 'password') {
11678             return;
11679         }
11680         
11681         var isSelectAll = false;
11682         
11683         if(this.inputEl().dom.selectionEnd > 0){
11684             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11685         }
11686         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11687             event.preventDefault();
11688             this.setValue('');
11689             return;
11690         }
11691         
11692         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11693             
11694             event.preventDefault();
11695             // this is very hacky as keydown always get's upper case.
11696             //
11697             var cc = String.fromCharCode(event.getCharCode());
11698             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11699             
11700         }
11701     },
11702     adjustWidth : function(tag, w){
11703         tag = tag.toLowerCase();
11704         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11705             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11706                 if(tag == 'input'){
11707                     return w + 2;
11708                 }
11709                 if(tag == 'textarea'){
11710                     return w-2;
11711                 }
11712             }else if(Roo.isOpera){
11713                 if(tag == 'input'){
11714                     return w + 2;
11715                 }
11716                 if(tag == 'textarea'){
11717                     return w-2;
11718                 }
11719             }
11720         }
11721         return w;
11722     },
11723     
11724     setFieldLabel : function(v)
11725     {
11726         if(!this.rendered){
11727             return;
11728         }
11729         
11730         if(this.indicatorEl()){
11731             var ar = this.el.select('label > span',true);
11732             
11733             if (ar.elements.length) {
11734                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11735                 this.fieldLabel = v;
11736                 return;
11737             }
11738             
11739             var br = this.el.select('label',true);
11740             
11741             if(br.elements.length) {
11742                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11743                 this.fieldLabel = v;
11744                 return;
11745             }
11746             
11747             Roo.log('Cannot Found any of label > span || label in input');
11748             return;
11749         }
11750         
11751         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11752         this.fieldLabel = v;
11753         
11754         
11755     }
11756 });
11757
11758  
11759 /*
11760  * - LGPL
11761  *
11762  * Input
11763  * 
11764  */
11765
11766 /**
11767  * @class Roo.bootstrap.TextArea
11768  * @extends Roo.bootstrap.Input
11769  * Bootstrap TextArea class
11770  * @cfg {Number} cols Specifies the visible width of a text area
11771  * @cfg {Number} rows Specifies the visible number of lines in a text area
11772  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11773  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11774  * @cfg {string} html text
11775  * 
11776  * @constructor
11777  * Create a new TextArea
11778  * @param {Object} config The config object
11779  */
11780
11781 Roo.bootstrap.TextArea = function(config){
11782     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11783    
11784 };
11785
11786 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11787      
11788     cols : false,
11789     rows : 5,
11790     readOnly : false,
11791     warp : 'soft',
11792     resize : false,
11793     value: false,
11794     html: false,
11795     
11796     getAutoCreate : function(){
11797         
11798         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11799         
11800         var id = Roo.id();
11801         
11802         var cfg = {};
11803         
11804         if(this.inputType != 'hidden'){
11805             cfg.cls = 'form-group' //input-group
11806         }
11807         
11808         var input =  {
11809             tag: 'textarea',
11810             id : id,
11811             warp : this.warp,
11812             rows : this.rows,
11813             value : this.value || '',
11814             html: this.html || '',
11815             cls : 'form-control',
11816             placeholder : this.placeholder || '' 
11817             
11818         };
11819         
11820         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11821             input.maxLength = this.maxLength;
11822         }
11823         
11824         if(this.resize){
11825             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11826         }
11827         
11828         if(this.cols){
11829             input.cols = this.cols;
11830         }
11831         
11832         if (this.readOnly) {
11833             input.readonly = true;
11834         }
11835         
11836         if (this.name) {
11837             input.name = this.name;
11838         }
11839         
11840         if (this.size) {
11841             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11842         }
11843         
11844         var settings=this;
11845         ['xs','sm','md','lg'].map(function(size){
11846             if (settings[size]) {
11847                 cfg.cls += ' col-' + size + '-' + settings[size];
11848             }
11849         });
11850         
11851         var inputblock = input;
11852         
11853         if(this.hasFeedback && !this.allowBlank){
11854             
11855             var feedback = {
11856                 tag: 'span',
11857                 cls: 'glyphicon form-control-feedback'
11858             };
11859
11860             inputblock = {
11861                 cls : 'has-feedback',
11862                 cn :  [
11863                     input,
11864                     feedback
11865                 ] 
11866             };  
11867         }
11868         
11869         
11870         if (this.before || this.after) {
11871             
11872             inputblock = {
11873                 cls : 'input-group',
11874                 cn :  [] 
11875             };
11876             if (this.before) {
11877                 inputblock.cn.push({
11878                     tag :'span',
11879                     cls : 'input-group-addon',
11880                     html : this.before
11881                 });
11882             }
11883             
11884             inputblock.cn.push(input);
11885             
11886             if(this.hasFeedback && !this.allowBlank){
11887                 inputblock.cls += ' has-feedback';
11888                 inputblock.cn.push(feedback);
11889             }
11890             
11891             if (this.after) {
11892                 inputblock.cn.push({
11893                     tag :'span',
11894                     cls : 'input-group-addon',
11895                     html : this.after
11896                 });
11897             }
11898             
11899         }
11900         
11901         if (align ==='left' && this.fieldLabel.length) {
11902             cfg.cn = [
11903                 {
11904                     tag: 'label',
11905                     'for' :  id,
11906                     cls : 'control-label',
11907                     html : this.fieldLabel
11908                 },
11909                 {
11910                     cls : "",
11911                     cn: [
11912                         inputblock
11913                     ]
11914                 }
11915
11916             ];
11917             
11918             if(this.labelWidth > 12){
11919                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11920             }
11921
11922             if(this.labelWidth < 13 && this.labelmd == 0){
11923                 this.labelmd = this.labelWidth;
11924             }
11925
11926             if(this.labellg > 0){
11927                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11928                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11929             }
11930
11931             if(this.labelmd > 0){
11932                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11933                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11934             }
11935
11936             if(this.labelsm > 0){
11937                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11938                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11939             }
11940
11941             if(this.labelxs > 0){
11942                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11943                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11944             }
11945             
11946         } else if ( this.fieldLabel.length) {
11947             cfg.cn = [
11948
11949                {
11950                    tag: 'label',
11951                    //cls : 'input-group-addon',
11952                    html : this.fieldLabel
11953
11954                },
11955
11956                inputblock
11957
11958            ];
11959
11960         } else {
11961
11962             cfg.cn = [
11963
11964                 inputblock
11965
11966             ];
11967                 
11968         }
11969         
11970         if (this.disabled) {
11971             input.disabled=true;
11972         }
11973         
11974         return cfg;
11975         
11976     },
11977     /**
11978      * return the real textarea element.
11979      */
11980     inputEl: function ()
11981     {
11982         return this.el.select('textarea.form-control',true).first();
11983     },
11984     
11985     /**
11986      * Clear any invalid styles/messages for this field
11987      */
11988     clearInvalid : function()
11989     {
11990         
11991         if(!this.el || this.preventMark){ // not rendered
11992             return;
11993         }
11994         
11995         var label = this.el.select('label', true).first();
11996         var icon = this.el.select('i.fa-star', true).first();
11997         
11998         if(label && icon){
11999             icon.remove();
12000         }
12001         this.el.removeClass( this.validClass);
12002         this.inputEl().removeClass('is-invalid');
12003          
12004         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12005             
12006             var feedback = this.el.select('.form-control-feedback', true).first();
12007             
12008             if(feedback){
12009                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12010             }
12011             
12012         }
12013         
12014         this.fireEvent('valid', this);
12015     },
12016     
12017      /**
12018      * Mark this field as valid
12019      */
12020     markValid : function()
12021     {
12022         if(!this.el  || this.preventMark){ // not rendered
12023             return;
12024         }
12025         
12026         this.el.removeClass([this.invalidClass, this.validClass]);
12027         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12028         
12029         var feedback = this.el.select('.form-control-feedback', true).first();
12030             
12031         if(feedback){
12032             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12033         }
12034
12035         if(this.disabled || this.allowBlank){
12036             return;
12037         }
12038         
12039         var label = this.el.select('label', true).first();
12040         var icon = this.el.select('i.fa-star', true).first();
12041         
12042         if(label && icon){
12043             icon.remove();
12044         }
12045         if (Roo.bootstrap.version == 3) {
12046             this.el.addClass(this.validClass);
12047         } else {
12048             this.inputEl().addClass('is-valid');
12049         }
12050         
12051         
12052         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
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                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12059             }
12060             
12061         }
12062         
12063         this.fireEvent('valid', this);
12064     },
12065     
12066      /**
12067      * Mark this field as invalid
12068      * @param {String} msg The validation message
12069      */
12070     markInvalid : function(msg)
12071     {
12072         if(!this.el  || this.preventMark){ // not rendered
12073             return;
12074         }
12075         
12076         this.el.removeClass([this.invalidClass, this.validClass]);
12077         this.inputEl().removeClass(['is-valid', 'is-invalid']);
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         }
12084
12085         if(this.disabled || this.allowBlank){
12086             return;
12087         }
12088         
12089         var label = this.el.select('label', true).first();
12090         var icon = this.el.select('i.fa-star', true).first();
12091         
12092         if(!this.getValue().length && label && !icon){
12093             this.el.createChild({
12094                 tag : 'i',
12095                 cls : 'text-danger fa fa-lg fa-star',
12096                 tooltip : 'This field is required',
12097                 style : 'margin-right:5px;'
12098             }, label, true);
12099         }
12100         
12101         if (Roo.bootstrap.version == 3) {
12102             this.el.addClass(this.invalidClass);
12103         } else {
12104             this.inputEl().addClass('is-invalid');
12105         }
12106         
12107         // fixme ... this may be depricated need to test..
12108         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12109             
12110             var feedback = this.el.select('.form-control-feedback', true).first();
12111             
12112             if(feedback){
12113                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12114                 
12115                 if(this.getValue().length || this.forceFeedback){
12116                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12117                 }
12118                 
12119             }
12120             
12121         }
12122         
12123         this.fireEvent('invalid', this, msg);
12124     }
12125 });
12126
12127  
12128 /*
12129  * - LGPL
12130  *
12131  * trigger field - base class for combo..
12132  * 
12133  */
12134  
12135 /**
12136  * @class Roo.bootstrap.TriggerField
12137  * @extends Roo.bootstrap.Input
12138  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12139  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12140  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12141  * for which you can provide a custom implementation.  For example:
12142  * <pre><code>
12143 var trigger = new Roo.bootstrap.TriggerField();
12144 trigger.onTriggerClick = myTriggerFn;
12145 trigger.applyTo('my-field');
12146 </code></pre>
12147  *
12148  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12149  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12150  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12151  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12152  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12153
12154  * @constructor
12155  * Create a new TriggerField.
12156  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12157  * to the base TextField)
12158  */
12159 Roo.bootstrap.TriggerField = function(config){
12160     this.mimicing = false;
12161     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12162 };
12163
12164 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12165     /**
12166      * @cfg {String} triggerClass A CSS class to apply to the trigger
12167      */
12168      /**
12169      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12170      */
12171     hideTrigger:false,
12172
12173     /**
12174      * @cfg {Boolean} removable (true|false) special filter default false
12175      */
12176     removable : false,
12177     
12178     /** @cfg {Boolean} grow @hide */
12179     /** @cfg {Number} growMin @hide */
12180     /** @cfg {Number} growMax @hide */
12181
12182     /**
12183      * @hide 
12184      * @method
12185      */
12186     autoSize: Roo.emptyFn,
12187     // private
12188     monitorTab : true,
12189     // private
12190     deferHeight : true,
12191
12192     
12193     actionMode : 'wrap',
12194     
12195     caret : false,
12196     
12197     
12198     getAutoCreate : function(){
12199        
12200         var align = this.labelAlign || this.parentLabelAlign();
12201         
12202         var id = Roo.id();
12203         
12204         var cfg = {
12205             cls: 'form-group' //input-group
12206         };
12207         
12208         
12209         var input =  {
12210             tag: 'input',
12211             id : id,
12212             type : this.inputType,
12213             cls : 'form-control',
12214             autocomplete: 'new-password',
12215             placeholder : this.placeholder || '' 
12216             
12217         };
12218         if (this.name) {
12219             input.name = this.name;
12220         }
12221         if (this.size) {
12222             input.cls += ' input-' + this.size;
12223         }
12224         
12225         if (this.disabled) {
12226             input.disabled=true;
12227         }
12228         
12229         var inputblock = input;
12230         
12231         if(this.hasFeedback && !this.allowBlank){
12232             
12233             var feedback = {
12234                 tag: 'span',
12235                 cls: 'glyphicon form-control-feedback'
12236             };
12237             
12238             if(this.removable && !this.editable  ){
12239                 inputblock = {
12240                     cls : 'has-feedback',
12241                     cn :  [
12242                         inputblock,
12243                         {
12244                             tag: 'button',
12245                             html : 'x',
12246                             cls : 'roo-combo-removable-btn close'
12247                         },
12248                         feedback
12249                     ] 
12250                 };
12251             } else {
12252                 inputblock = {
12253                     cls : 'has-feedback',
12254                     cn :  [
12255                         inputblock,
12256                         feedback
12257                     ] 
12258                 };
12259             }
12260
12261         } else {
12262             if(this.removable && !this.editable ){
12263                 inputblock = {
12264                     cls : 'roo-removable',
12265                     cn :  [
12266                         inputblock,
12267                         {
12268                             tag: 'button',
12269                             html : 'x',
12270                             cls : 'roo-combo-removable-btn close'
12271                         }
12272                     ] 
12273                 };
12274             }
12275         }
12276         
12277         if (this.before || this.after) {
12278             
12279             inputblock = {
12280                 cls : 'input-group',
12281                 cn :  [] 
12282             };
12283             if (this.before) {
12284                 inputblock.cn.push({
12285                     tag :'span',
12286                     cls : 'input-group-addon input-group-prepend input-group-text',
12287                     html : this.before
12288                 });
12289             }
12290             
12291             inputblock.cn.push(input);
12292             
12293             if(this.hasFeedback && !this.allowBlank){
12294                 inputblock.cls += ' has-feedback';
12295                 inputblock.cn.push(feedback);
12296             }
12297             
12298             if (this.after) {
12299                 inputblock.cn.push({
12300                     tag :'span',
12301                     cls : 'input-group-addon input-group-append input-group-text',
12302                     html : this.after
12303                 });
12304             }
12305             
12306         };
12307         
12308       
12309         
12310         var ibwrap = inputblock;
12311         
12312         if(this.multiple){
12313             ibwrap = {
12314                 tag: 'ul',
12315                 cls: 'roo-select2-choices',
12316                 cn:[
12317                     {
12318                         tag: 'li',
12319                         cls: 'roo-select2-search-field',
12320                         cn: [
12321
12322                             inputblock
12323                         ]
12324                     }
12325                 ]
12326             };
12327                 
12328         }
12329         
12330         var combobox = {
12331             cls: 'roo-select2-container input-group',
12332             cn: [
12333                  {
12334                     tag: 'input',
12335                     type : 'hidden',
12336                     cls: 'form-hidden-field'
12337                 },
12338                 ibwrap
12339             ]
12340         };
12341         
12342         if(!this.multiple && this.showToggleBtn){
12343             
12344             var caret = {
12345                         tag: 'span',
12346                         cls: 'caret'
12347              };
12348             if (this.caret != false) {
12349                 caret = {
12350                      tag: 'i',
12351                      cls: 'fa fa-' + this.caret
12352                 };
12353                 
12354             }
12355             
12356             combobox.cn.push({
12357                 tag :'span',
12358                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12359                 cn : [
12360                     Roo.bootstrap.version == 3 ? caret : '',
12361                     {
12362                         tag: 'span',
12363                         cls: 'combobox-clear',
12364                         cn  : [
12365                             {
12366                                 tag : 'i',
12367                                 cls: 'icon-remove'
12368                             }
12369                         ]
12370                     }
12371                 ]
12372
12373             })
12374         }
12375         
12376         if(this.multiple){
12377             combobox.cls += ' roo-select2-container-multi';
12378         }
12379          var indicator = {
12380             tag : 'i',
12381             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12382             tooltip : 'This field is required'
12383         };
12384         if (Roo.bootstrap.version == 4) {
12385             indicator = {
12386                 tag : 'i',
12387                 style : 'display:none'
12388             };
12389         }
12390         
12391         
12392         if (align ==='left' && this.fieldLabel.length) {
12393             
12394             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12395
12396             cfg.cn = [
12397                 indicator,
12398                 {
12399                     tag: 'label',
12400                     'for' :  id,
12401                     cls : 'control-label',
12402                     html : this.fieldLabel
12403
12404                 },
12405                 {
12406                     cls : "", 
12407                     cn: [
12408                         combobox
12409                     ]
12410                 }
12411
12412             ];
12413             
12414             var labelCfg = cfg.cn[1];
12415             var contentCfg = cfg.cn[2];
12416             
12417             if(this.indicatorpos == 'right'){
12418                 cfg.cn = [
12419                     {
12420                         tag: 'label',
12421                         'for' :  id,
12422                         cls : 'control-label',
12423                         cn : [
12424                             {
12425                                 tag : 'span',
12426                                 html : this.fieldLabel
12427                             },
12428                             indicator
12429                         ]
12430                     },
12431                     {
12432                         cls : "", 
12433                         cn: [
12434                             combobox
12435                         ]
12436                     }
12437
12438                 ];
12439                 
12440                 labelCfg = cfg.cn[0];
12441                 contentCfg = cfg.cn[1];
12442             }
12443             
12444             if(this.labelWidth > 12){
12445                 labelCfg.style = "width: " + this.labelWidth + 'px';
12446             }
12447             
12448             if(this.labelWidth < 13 && this.labelmd == 0){
12449                 this.labelmd = this.labelWidth;
12450             }
12451             
12452             if(this.labellg > 0){
12453                 labelCfg.cls += ' col-lg-' + this.labellg;
12454                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12455             }
12456             
12457             if(this.labelmd > 0){
12458                 labelCfg.cls += ' col-md-' + this.labelmd;
12459                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12460             }
12461             
12462             if(this.labelsm > 0){
12463                 labelCfg.cls += ' col-sm-' + this.labelsm;
12464                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12465             }
12466             
12467             if(this.labelxs > 0){
12468                 labelCfg.cls += ' col-xs-' + this.labelxs;
12469                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12470             }
12471             
12472         } else if ( this.fieldLabel.length) {
12473 //                Roo.log(" label");
12474             cfg.cn = [
12475                 indicator,
12476                {
12477                    tag: 'label',
12478                    //cls : 'input-group-addon',
12479                    html : this.fieldLabel
12480
12481                },
12482
12483                combobox
12484
12485             ];
12486             
12487             if(this.indicatorpos == 'right'){
12488                 
12489                 cfg.cn = [
12490                     {
12491                        tag: 'label',
12492                        cn : [
12493                            {
12494                                tag : 'span',
12495                                html : this.fieldLabel
12496                            },
12497                            indicator
12498                        ]
12499
12500                     },
12501                     combobox
12502
12503                 ];
12504
12505             }
12506
12507         } else {
12508             
12509 //                Roo.log(" no label && no align");
12510                 cfg = combobox
12511                      
12512                 
12513         }
12514         
12515         var settings=this;
12516         ['xs','sm','md','lg'].map(function(size){
12517             if (settings[size]) {
12518                 cfg.cls += ' col-' + size + '-' + settings[size];
12519             }
12520         });
12521         
12522         return cfg;
12523         
12524     },
12525     
12526     
12527     
12528     // private
12529     onResize : function(w, h){
12530 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12531 //        if(typeof w == 'number'){
12532 //            var x = w - this.trigger.getWidth();
12533 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12534 //            this.trigger.setStyle('left', x+'px');
12535 //        }
12536     },
12537
12538     // private
12539     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12540
12541     // private
12542     getResizeEl : function(){
12543         return this.inputEl();
12544     },
12545
12546     // private
12547     getPositionEl : function(){
12548         return this.inputEl();
12549     },
12550
12551     // private
12552     alignErrorIcon : function(){
12553         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12554     },
12555
12556     // private
12557     initEvents : function(){
12558         
12559         this.createList();
12560         
12561         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12562         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12563         if(!this.multiple && this.showToggleBtn){
12564             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12565             if(this.hideTrigger){
12566                 this.trigger.setDisplayed(false);
12567             }
12568             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12569         }
12570         
12571         if(this.multiple){
12572             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12573         }
12574         
12575         if(this.removable && !this.editable && !this.tickable){
12576             var close = this.closeTriggerEl();
12577             
12578             if(close){
12579                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12580                 close.on('click', this.removeBtnClick, this, close);
12581             }
12582         }
12583         
12584         //this.trigger.addClassOnOver('x-form-trigger-over');
12585         //this.trigger.addClassOnClick('x-form-trigger-click');
12586         
12587         //if(!this.width){
12588         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12589         //}
12590     },
12591     
12592     closeTriggerEl : function()
12593     {
12594         var close = this.el.select('.roo-combo-removable-btn', true).first();
12595         return close ? close : false;
12596     },
12597     
12598     removeBtnClick : function(e, h, el)
12599     {
12600         e.preventDefault();
12601         
12602         if(this.fireEvent("remove", this) !== false){
12603             this.reset();
12604             this.fireEvent("afterremove", this)
12605         }
12606     },
12607     
12608     createList : function()
12609     {
12610         this.list = Roo.get(document.body).createChild({
12611             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12612             cls: 'typeahead typeahead-long dropdown-menu shadow',
12613             style: 'display:none'
12614         });
12615         
12616         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12617         
12618     },
12619
12620     // private
12621     initTrigger : function(){
12622        
12623     },
12624
12625     // private
12626     onDestroy : function(){
12627         if(this.trigger){
12628             this.trigger.removeAllListeners();
12629           //  this.trigger.remove();
12630         }
12631         //if(this.wrap){
12632         //    this.wrap.remove();
12633         //}
12634         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12635     },
12636
12637     // private
12638     onFocus : function(){
12639         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12640         /*
12641         if(!this.mimicing){
12642             this.wrap.addClass('x-trigger-wrap-focus');
12643             this.mimicing = true;
12644             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12645             if(this.monitorTab){
12646                 this.el.on("keydown", this.checkTab, this);
12647             }
12648         }
12649         */
12650     },
12651
12652     // private
12653     checkTab : function(e){
12654         if(e.getKey() == e.TAB){
12655             this.triggerBlur();
12656         }
12657     },
12658
12659     // private
12660     onBlur : function(){
12661         // do nothing
12662     },
12663
12664     // private
12665     mimicBlur : function(e, t){
12666         /*
12667         if(!this.wrap.contains(t) && this.validateBlur()){
12668             this.triggerBlur();
12669         }
12670         */
12671     },
12672
12673     // private
12674     triggerBlur : function(){
12675         this.mimicing = false;
12676         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12677         if(this.monitorTab){
12678             this.el.un("keydown", this.checkTab, this);
12679         }
12680         //this.wrap.removeClass('x-trigger-wrap-focus');
12681         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12682     },
12683
12684     // private
12685     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12686     validateBlur : function(e, t){
12687         return true;
12688     },
12689
12690     // private
12691     onDisable : function(){
12692         this.inputEl().dom.disabled = true;
12693         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12694         //if(this.wrap){
12695         //    this.wrap.addClass('x-item-disabled');
12696         //}
12697     },
12698
12699     // private
12700     onEnable : function(){
12701         this.inputEl().dom.disabled = false;
12702         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12703         //if(this.wrap){
12704         //    this.el.removeClass('x-item-disabled');
12705         //}
12706     },
12707
12708     // private
12709     onShow : function(){
12710         var ae = this.getActionEl();
12711         
12712         if(ae){
12713             ae.dom.style.display = '';
12714             ae.dom.style.visibility = 'visible';
12715         }
12716     },
12717
12718     // private
12719     
12720     onHide : function(){
12721         var ae = this.getActionEl();
12722         ae.dom.style.display = 'none';
12723     },
12724
12725     /**
12726      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12727      * by an implementing function.
12728      * @method
12729      * @param {EventObject} e
12730      */
12731     onTriggerClick : Roo.emptyFn
12732 });
12733  
12734 /*
12735 * Licence: LGPL
12736 */
12737
12738 /**
12739  * @class Roo.bootstrap.CardUploader
12740  * @extends Roo.bootstrap.Button
12741  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12742  * @cfg {Number} errorTimeout default 3000
12743  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12744  * @cfg {Array}  html The button text.
12745
12746  *
12747  * @constructor
12748  * Create a new CardUploader
12749  * @param {Object} config The config object
12750  */
12751
12752 Roo.bootstrap.CardUploader = function(config){
12753     
12754  
12755     
12756     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12757     
12758     
12759     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12760         return r.data.id
12761      });
12762     
12763      this.addEvents({
12764          // raw events
12765         /**
12766          * @event preview
12767          * When a image is clicked on - and needs to display a slideshow or similar..
12768          * @param {Roo.bootstrap.Card} this
12769          * @param {Object} The image information data 
12770          *
12771          */
12772         'preview' : true,
12773          /**
12774          * @event download
12775          * When a the download link is clicked
12776          * @param {Roo.bootstrap.Card} this
12777          * @param {Object} The image information data  contains 
12778          */
12779         'download' : true
12780         
12781     });
12782 };
12783  
12784 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12785     
12786      
12787     errorTimeout : 3000,
12788      
12789     images : false,
12790    
12791     fileCollection : false,
12792     allowBlank : true,
12793     
12794     getAutoCreate : function()
12795     {
12796         
12797         var cfg =  {
12798             cls :'form-group' ,
12799             cn : [
12800                
12801                 {
12802                     tag: 'label',
12803                    //cls : 'input-group-addon',
12804                     html : this.fieldLabel
12805
12806                 },
12807
12808                 {
12809                     tag: 'input',
12810                     type : 'hidden',
12811                     name : this.name,
12812                     value : this.value,
12813                     cls : 'd-none  form-control'
12814                 },
12815                 
12816                 {
12817                     tag: 'input',
12818                     multiple : 'multiple',
12819                     type : 'file',
12820                     cls : 'd-none  roo-card-upload-selector'
12821                 },
12822                 
12823                 {
12824                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12825                 },
12826                 {
12827                     cls : 'card-columns roo-card-uploader-container'
12828                 }
12829
12830             ]
12831         };
12832            
12833          
12834         return cfg;
12835     },
12836     
12837     getChildContainer : function() /// what children are added to.
12838     {
12839         return this.containerEl;
12840     },
12841    
12842     getButtonContainer : function() /// what children are added to.
12843     {
12844         return this.el.select(".roo-card-uploader-button-container").first();
12845     },
12846    
12847     initEvents : function()
12848     {
12849         
12850         Roo.bootstrap.Input.prototype.initEvents.call(this);
12851         
12852         var t = this;
12853         this.addxtype({
12854             xns: Roo.bootstrap,
12855
12856             xtype : 'Button',
12857             container_method : 'getButtonContainer' ,            
12858             html :  this.html, // fix changable?
12859             cls : 'w-100 ',
12860             listeners : {
12861                 'click' : function(btn, e) {
12862                     t.onClick(e);
12863                 }
12864             }
12865         });
12866         
12867         
12868         
12869         
12870         this.urlAPI = (window.createObjectURL && window) || 
12871                                 (window.URL && URL.revokeObjectURL && URL) || 
12872                                 (window.webkitURL && webkitURL);
12873                         
12874          
12875          
12876          
12877         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12878         
12879         this.selectorEl.on('change', this.onFileSelected, this);
12880         if (this.images) {
12881             var t = this;
12882             this.images.forEach(function(img) {
12883                 t.addCard(img)
12884             });
12885             this.images = false;
12886         }
12887         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12888          
12889        
12890     },
12891     
12892    
12893     onClick : function(e)
12894     {
12895         e.preventDefault();
12896          
12897         this.selectorEl.dom.click();
12898          
12899     },
12900     
12901     onFileSelected : function(e)
12902     {
12903         e.preventDefault();
12904         
12905         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12906             return;
12907         }
12908         
12909         Roo.each(this.selectorEl.dom.files, function(file){    
12910             this.addFile(file);
12911         }, this);
12912          
12913     },
12914     
12915       
12916     
12917       
12918     
12919     addFile : function(file)
12920     {
12921            
12922         if(typeof(file) === 'string'){
12923             throw "Add file by name?"; // should not happen
12924             return;
12925         }
12926         
12927         if(!file || !this.urlAPI){
12928             return;
12929         }
12930         
12931         // file;
12932         // file.type;
12933         
12934         var _this = this;
12935         
12936         
12937         var url = _this.urlAPI.createObjectURL( file);
12938            
12939         this.addCard({
12940             id : Roo.bootstrap.CardUploader.ID--,
12941             is_uploaded : false,
12942             src : url,
12943             srcfile : file,
12944             title : file.name,
12945             mimetype : file.type,
12946             preview : false,
12947             is_deleted : 0
12948         });
12949         
12950     },
12951     
12952     /**
12953      * addCard - add an Attachment to the uploader
12954      * @param data - the data about the image to upload
12955      *
12956      * {
12957           id : 123
12958           title : "Title of file",
12959           is_uploaded : false,
12960           src : "http://.....",
12961           srcfile : { the File upload object },
12962           mimetype : file.type,
12963           preview : false,
12964           is_deleted : 0
12965           .. any other data...
12966         }
12967      *
12968      * 
12969     */
12970     
12971     addCard : function (data)
12972     {
12973         // hidden input element?
12974         // if the file is not an image...
12975         //then we need to use something other that and header_image
12976         var t = this;
12977         //   remove.....
12978         var footer = [
12979             {
12980                 xns : Roo.bootstrap,
12981                 xtype : 'CardFooter',
12982                  items: [
12983                     {
12984                         xns : Roo.bootstrap,
12985                         xtype : 'Element',
12986                         cls : 'd-flex',
12987                         items : [
12988                             
12989                             {
12990                                 xns : Roo.bootstrap,
12991                                 xtype : 'Button',
12992                                 html : String.format("<small>{0}</small>", data.title),
12993                                 cls : 'col-10 text-left',
12994                                 size: 'sm',
12995                                 weight: 'link',
12996                                 fa : 'download',
12997                                 listeners : {
12998                                     click : function() {
12999                                      
13000                                         t.fireEvent( "download", t, data );
13001                                     }
13002                                 }
13003                             },
13004                           
13005                             {
13006                                 xns : Roo.bootstrap,
13007                                 xtype : 'Button',
13008                                 style: 'max-height: 28px; ',
13009                                 size : 'sm',
13010                                 weight: 'danger',
13011                                 cls : 'col-2',
13012                                 fa : 'times',
13013                                 listeners : {
13014                                     click : function() {
13015                                         t.removeCard(data.id)
13016                                     }
13017                                 }
13018                             }
13019                         ]
13020                     }
13021                     
13022                 ] 
13023             }
13024             
13025         ];
13026         
13027         var cn = this.addxtype(
13028             {
13029                  
13030                 xns : Roo.bootstrap,
13031                 xtype : 'Card',
13032                 closeable : true,
13033                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13034                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13035                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13036                 data : data,
13037                 html : false,
13038                  
13039                 items : footer,
13040                 initEvents : function() {
13041                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13042                     var card = this;
13043                     this.imgEl = this.el.select('.card-img-top').first();
13044                     if (this.imgEl) {
13045                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13046                         this.imgEl.set({ 'pointer' : 'cursor' });
13047                                   
13048                     }
13049                     this.getCardFooter().addClass('p-1');
13050                     
13051                   
13052                 }
13053                 
13054             }
13055         );
13056         // dont' really need ot update items.
13057         // this.items.push(cn);
13058         this.fileCollection.add(cn);
13059         
13060         if (!data.srcfile) {
13061             this.updateInput();
13062             return;
13063         }
13064             
13065         var _t = this;
13066         var reader = new FileReader();
13067         reader.addEventListener("load", function() {  
13068             data.srcdata =  reader.result;
13069             _t.updateInput();
13070         });
13071         reader.readAsDataURL(data.srcfile);
13072         
13073         
13074         
13075     },
13076     removeCard : function(id)
13077     {
13078         
13079         var card  = this.fileCollection.get(id);
13080         card.data.is_deleted = 1;
13081         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13082         //this.fileCollection.remove(card);
13083         //this.items = this.items.filter(function(e) { return e != card });
13084         // dont' really need ot update items.
13085         card.el.dom.parentNode.removeChild(card.el.dom);
13086         this.updateInput();
13087
13088         
13089     },
13090     reset: function()
13091     {
13092         this.fileCollection.each(function(card) {
13093             if (card.el.dom && card.el.dom.parentNode) {
13094                 card.el.dom.parentNode.removeChild(card.el.dom);
13095             }
13096         });
13097         this.fileCollection.clear();
13098         this.updateInput();
13099     },
13100     
13101     updateInput : function()
13102     {
13103          var data = [];
13104         this.fileCollection.each(function(e) {
13105             data.push(e.data);
13106             
13107         });
13108         this.inputEl().dom.value = JSON.stringify(data);
13109         
13110         
13111         
13112     }
13113     
13114     
13115 });
13116
13117
13118 Roo.bootstrap.CardUploader.ID = -1;/*
13119  * Based on:
13120  * Ext JS Library 1.1.1
13121  * Copyright(c) 2006-2007, Ext JS, LLC.
13122  *
13123  * Originally Released Under LGPL - original licence link has changed is not relivant.
13124  *
13125  * Fork - LGPL
13126  * <script type="text/javascript">
13127  */
13128
13129
13130 /**
13131  * @class Roo.data.SortTypes
13132  * @singleton
13133  * Defines the default sorting (casting?) comparison functions used when sorting data.
13134  */
13135 Roo.data.SortTypes = {
13136     /**
13137      * Default sort that does nothing
13138      * @param {Mixed} s The value being converted
13139      * @return {Mixed} The comparison value
13140      */
13141     none : function(s){
13142         return s;
13143     },
13144     
13145     /**
13146      * The regular expression used to strip tags
13147      * @type {RegExp}
13148      * @property
13149      */
13150     stripTagsRE : /<\/?[^>]+>/gi,
13151     
13152     /**
13153      * Strips all HTML tags to sort on text only
13154      * @param {Mixed} s The value being converted
13155      * @return {String} The comparison value
13156      */
13157     asText : function(s){
13158         return String(s).replace(this.stripTagsRE, "");
13159     },
13160     
13161     /**
13162      * Strips all HTML tags to sort on text only - Case insensitive
13163      * @param {Mixed} s The value being converted
13164      * @return {String} The comparison value
13165      */
13166     asUCText : function(s){
13167         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13168     },
13169     
13170     /**
13171      * Case insensitive string
13172      * @param {Mixed} s The value being converted
13173      * @return {String} The comparison value
13174      */
13175     asUCString : function(s) {
13176         return String(s).toUpperCase();
13177     },
13178     
13179     /**
13180      * Date sorting
13181      * @param {Mixed} s The value being converted
13182      * @return {Number} The comparison value
13183      */
13184     asDate : function(s) {
13185         if(!s){
13186             return 0;
13187         }
13188         if(s instanceof Date){
13189             return s.getTime();
13190         }
13191         return Date.parse(String(s));
13192     },
13193     
13194     /**
13195      * Float sorting
13196      * @param {Mixed} s The value being converted
13197      * @return {Float} The comparison value
13198      */
13199     asFloat : function(s) {
13200         var val = parseFloat(String(s).replace(/,/g, ""));
13201         if(isNaN(val)) {
13202             val = 0;
13203         }
13204         return val;
13205     },
13206     
13207     /**
13208      * Integer sorting
13209      * @param {Mixed} s The value being converted
13210      * @return {Number} The comparison value
13211      */
13212     asInt : function(s) {
13213         var val = parseInt(String(s).replace(/,/g, ""));
13214         if(isNaN(val)) {
13215             val = 0;
13216         }
13217         return val;
13218     }
13219 };/*
13220  * Based on:
13221  * Ext JS Library 1.1.1
13222  * Copyright(c) 2006-2007, Ext JS, LLC.
13223  *
13224  * Originally Released Under LGPL - original licence link has changed is not relivant.
13225  *
13226  * Fork - LGPL
13227  * <script type="text/javascript">
13228  */
13229
13230 /**
13231 * @class Roo.data.Record
13232  * Instances of this class encapsulate both record <em>definition</em> information, and record
13233  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13234  * to access Records cached in an {@link Roo.data.Store} object.<br>
13235  * <p>
13236  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13237  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13238  * objects.<br>
13239  * <p>
13240  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13241  * @constructor
13242  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13243  * {@link #create}. The parameters are the same.
13244  * @param {Array} data An associative Array of data values keyed by the field name.
13245  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13246  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13247  * not specified an integer id is generated.
13248  */
13249 Roo.data.Record = function(data, id){
13250     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13251     this.data = data;
13252 };
13253
13254 /**
13255  * Generate a constructor for a specific record layout.
13256  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13257  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13258  * Each field definition object may contain the following properties: <ul>
13259  * <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,
13260  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13261  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13262  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13263  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13264  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13265  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13266  * this may be omitted.</p></li>
13267  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13268  * <ul><li>auto (Default, implies no conversion)</li>
13269  * <li>string</li>
13270  * <li>int</li>
13271  * <li>float</li>
13272  * <li>boolean</li>
13273  * <li>date</li></ul></p></li>
13274  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13275  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13276  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13277  * by the Reader into an object that will be stored in the Record. It is passed the
13278  * following parameters:<ul>
13279  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13280  * </ul></p></li>
13281  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13282  * </ul>
13283  * <br>usage:<br><pre><code>
13284 var TopicRecord = Roo.data.Record.create(
13285     {name: 'title', mapping: 'topic_title'},
13286     {name: 'author', mapping: 'username'},
13287     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13288     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13289     {name: 'lastPoster', mapping: 'user2'},
13290     {name: 'excerpt', mapping: 'post_text'}
13291 );
13292
13293 var myNewRecord = new TopicRecord({
13294     title: 'Do my job please',
13295     author: 'noobie',
13296     totalPosts: 1,
13297     lastPost: new Date(),
13298     lastPoster: 'Animal',
13299     excerpt: 'No way dude!'
13300 });
13301 myStore.add(myNewRecord);
13302 </code></pre>
13303  * @method create
13304  * @static
13305  */
13306 Roo.data.Record.create = function(o){
13307     var f = function(){
13308         f.superclass.constructor.apply(this, arguments);
13309     };
13310     Roo.extend(f, Roo.data.Record);
13311     var p = f.prototype;
13312     p.fields = new Roo.util.MixedCollection(false, function(field){
13313         return field.name;
13314     });
13315     for(var i = 0, len = o.length; i < len; i++){
13316         p.fields.add(new Roo.data.Field(o[i]));
13317     }
13318     f.getField = function(name){
13319         return p.fields.get(name);  
13320     };
13321     return f;
13322 };
13323
13324 Roo.data.Record.AUTO_ID = 1000;
13325 Roo.data.Record.EDIT = 'edit';
13326 Roo.data.Record.REJECT = 'reject';
13327 Roo.data.Record.COMMIT = 'commit';
13328
13329 Roo.data.Record.prototype = {
13330     /**
13331      * Readonly flag - true if this record has been modified.
13332      * @type Boolean
13333      */
13334     dirty : false,
13335     editing : false,
13336     error: null,
13337     modified: null,
13338
13339     // private
13340     join : function(store){
13341         this.store = store;
13342     },
13343
13344     /**
13345      * Set the named field to the specified value.
13346      * @param {String} name The name of the field to set.
13347      * @param {Object} value The value to set the field to.
13348      */
13349     set : function(name, value){
13350         if(this.data[name] == value){
13351             return;
13352         }
13353         this.dirty = true;
13354         if(!this.modified){
13355             this.modified = {};
13356         }
13357         if(typeof this.modified[name] == 'undefined'){
13358             this.modified[name] = this.data[name];
13359         }
13360         this.data[name] = value;
13361         if(!this.editing && this.store){
13362             this.store.afterEdit(this);
13363         }       
13364     },
13365
13366     /**
13367      * Get the value of the named field.
13368      * @param {String} name The name of the field to get the value of.
13369      * @return {Object} The value of the field.
13370      */
13371     get : function(name){
13372         return this.data[name]; 
13373     },
13374
13375     // private
13376     beginEdit : function(){
13377         this.editing = true;
13378         this.modified = {}; 
13379     },
13380
13381     // private
13382     cancelEdit : function(){
13383         this.editing = false;
13384         delete this.modified;
13385     },
13386
13387     // private
13388     endEdit : function(){
13389         this.editing = false;
13390         if(this.dirty && this.store){
13391             this.store.afterEdit(this);
13392         }
13393     },
13394
13395     /**
13396      * Usually called by the {@link Roo.data.Store} which owns the Record.
13397      * Rejects all changes made to the Record since either creation, or the last commit operation.
13398      * Modified fields are reverted to their original values.
13399      * <p>
13400      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13401      * of reject operations.
13402      */
13403     reject : function(){
13404         var m = this.modified;
13405         for(var n in m){
13406             if(typeof m[n] != "function"){
13407                 this.data[n] = m[n];
13408             }
13409         }
13410         this.dirty = false;
13411         delete this.modified;
13412         this.editing = false;
13413         if(this.store){
13414             this.store.afterReject(this);
13415         }
13416     },
13417
13418     /**
13419      * Usually called by the {@link Roo.data.Store} which owns the Record.
13420      * Commits all changes made to the Record since either creation, or the last commit operation.
13421      * <p>
13422      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13423      * of commit operations.
13424      */
13425     commit : function(){
13426         this.dirty = false;
13427         delete this.modified;
13428         this.editing = false;
13429         if(this.store){
13430             this.store.afterCommit(this);
13431         }
13432     },
13433
13434     // private
13435     hasError : function(){
13436         return this.error != null;
13437     },
13438
13439     // private
13440     clearError : function(){
13441         this.error = null;
13442     },
13443
13444     /**
13445      * Creates a copy of this record.
13446      * @param {String} id (optional) A new record id if you don't want to use this record's id
13447      * @return {Record}
13448      */
13449     copy : function(newId) {
13450         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13451     }
13452 };/*
13453  * Based on:
13454  * Ext JS Library 1.1.1
13455  * Copyright(c) 2006-2007, Ext JS, LLC.
13456  *
13457  * Originally Released Under LGPL - original licence link has changed is not relivant.
13458  *
13459  * Fork - LGPL
13460  * <script type="text/javascript">
13461  */
13462
13463
13464
13465 /**
13466  * @class Roo.data.Store
13467  * @extends Roo.util.Observable
13468  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13469  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13470  * <p>
13471  * 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
13472  * has no knowledge of the format of the data returned by the Proxy.<br>
13473  * <p>
13474  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13475  * instances from the data object. These records are cached and made available through accessor functions.
13476  * @constructor
13477  * Creates a new Store.
13478  * @param {Object} config A config object containing the objects needed for the Store to access data,
13479  * and read the data into Records.
13480  */
13481 Roo.data.Store = function(config){
13482     this.data = new Roo.util.MixedCollection(false);
13483     this.data.getKey = function(o){
13484         return o.id;
13485     };
13486     this.baseParams = {};
13487     // private
13488     this.paramNames = {
13489         "start" : "start",
13490         "limit" : "limit",
13491         "sort" : "sort",
13492         "dir" : "dir",
13493         "multisort" : "_multisort"
13494     };
13495
13496     if(config && config.data){
13497         this.inlineData = config.data;
13498         delete config.data;
13499     }
13500
13501     Roo.apply(this, config);
13502     
13503     if(this.reader){ // reader passed
13504         this.reader = Roo.factory(this.reader, Roo.data);
13505         this.reader.xmodule = this.xmodule || false;
13506         if(!this.recordType){
13507             this.recordType = this.reader.recordType;
13508         }
13509         if(this.reader.onMetaChange){
13510             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13511         }
13512     }
13513
13514     if(this.recordType){
13515         this.fields = this.recordType.prototype.fields;
13516     }
13517     this.modified = [];
13518
13519     this.addEvents({
13520         /**
13521          * @event datachanged
13522          * Fires when the data cache has changed, and a widget which is using this Store
13523          * as a Record cache should refresh its view.
13524          * @param {Store} this
13525          */
13526         datachanged : true,
13527         /**
13528          * @event metachange
13529          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13530          * @param {Store} this
13531          * @param {Object} meta The JSON metadata
13532          */
13533         metachange : true,
13534         /**
13535          * @event add
13536          * Fires when Records have been added to the Store
13537          * @param {Store} this
13538          * @param {Roo.data.Record[]} records The array of Records added
13539          * @param {Number} index The index at which the record(s) were added
13540          */
13541         add : true,
13542         /**
13543          * @event remove
13544          * Fires when a Record has been removed from the Store
13545          * @param {Store} this
13546          * @param {Roo.data.Record} record The Record that was removed
13547          * @param {Number} index The index at which the record was removed
13548          */
13549         remove : true,
13550         /**
13551          * @event update
13552          * Fires when a Record has been updated
13553          * @param {Store} this
13554          * @param {Roo.data.Record} record The Record that was updated
13555          * @param {String} operation The update operation being performed.  Value may be one of:
13556          * <pre><code>
13557  Roo.data.Record.EDIT
13558  Roo.data.Record.REJECT
13559  Roo.data.Record.COMMIT
13560          * </code></pre>
13561          */
13562         update : true,
13563         /**
13564          * @event clear
13565          * Fires when the data cache has been cleared.
13566          * @param {Store} this
13567          */
13568         clear : true,
13569         /**
13570          * @event beforeload
13571          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13572          * the load action will be canceled.
13573          * @param {Store} this
13574          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13575          */
13576         beforeload : true,
13577         /**
13578          * @event beforeloadadd
13579          * Fires after a new set of Records has been loaded.
13580          * @param {Store} this
13581          * @param {Roo.data.Record[]} records The Records that were loaded
13582          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13583          */
13584         beforeloadadd : true,
13585         /**
13586          * @event load
13587          * Fires after a new set of Records has been loaded, before they are added to the store.
13588          * @param {Store} this
13589          * @param {Roo.data.Record[]} records The Records that were loaded
13590          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13591          * @params {Object} return from reader
13592          */
13593         load : true,
13594         /**
13595          * @event loadexception
13596          * Fires if an exception occurs in the Proxy during loading.
13597          * Called with the signature of the Proxy's "loadexception" event.
13598          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13599          * 
13600          * @param {Proxy} 
13601          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13602          * @param {Object} load options 
13603          * @param {Object} jsonData from your request (normally this contains the Exception)
13604          */
13605         loadexception : true
13606     });
13607     
13608     if(this.proxy){
13609         this.proxy = Roo.factory(this.proxy, Roo.data);
13610         this.proxy.xmodule = this.xmodule || false;
13611         this.relayEvents(this.proxy,  ["loadexception"]);
13612     }
13613     this.sortToggle = {};
13614     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13615
13616     Roo.data.Store.superclass.constructor.call(this);
13617
13618     if(this.inlineData){
13619         this.loadData(this.inlineData);
13620         delete this.inlineData;
13621     }
13622 };
13623
13624 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13625      /**
13626     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13627     * without a remote query - used by combo/forms at present.
13628     */
13629     
13630     /**
13631     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13632     */
13633     /**
13634     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13635     */
13636     /**
13637     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13638     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13639     */
13640     /**
13641     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13642     * on any HTTP request
13643     */
13644     /**
13645     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13646     */
13647     /**
13648     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13649     */
13650     multiSort: false,
13651     /**
13652     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13653     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13654     */
13655     remoteSort : false,
13656
13657     /**
13658     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13659      * loaded or when a record is removed. (defaults to false).
13660     */
13661     pruneModifiedRecords : false,
13662
13663     // private
13664     lastOptions : null,
13665
13666     /**
13667      * Add Records to the Store and fires the add event.
13668      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13669      */
13670     add : function(records){
13671         records = [].concat(records);
13672         for(var i = 0, len = records.length; i < len; i++){
13673             records[i].join(this);
13674         }
13675         var index = this.data.length;
13676         this.data.addAll(records);
13677         this.fireEvent("add", this, records, index);
13678     },
13679
13680     /**
13681      * Remove a Record from the Store and fires the remove event.
13682      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13683      */
13684     remove : function(record){
13685         var index = this.data.indexOf(record);
13686         this.data.removeAt(index);
13687  
13688         if(this.pruneModifiedRecords){
13689             this.modified.remove(record);
13690         }
13691         this.fireEvent("remove", this, record, index);
13692     },
13693
13694     /**
13695      * Remove all Records from the Store and fires the clear event.
13696      */
13697     removeAll : function(){
13698         this.data.clear();
13699         if(this.pruneModifiedRecords){
13700             this.modified = [];
13701         }
13702         this.fireEvent("clear", this);
13703     },
13704
13705     /**
13706      * Inserts Records to the Store at the given index and fires the add event.
13707      * @param {Number} index The start index at which to insert the passed Records.
13708      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13709      */
13710     insert : function(index, records){
13711         records = [].concat(records);
13712         for(var i = 0, len = records.length; i < len; i++){
13713             this.data.insert(index, records[i]);
13714             records[i].join(this);
13715         }
13716         this.fireEvent("add", this, records, index);
13717     },
13718
13719     /**
13720      * Get the index within the cache of the passed Record.
13721      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13722      * @return {Number} The index of the passed Record. Returns -1 if not found.
13723      */
13724     indexOf : function(record){
13725         return this.data.indexOf(record);
13726     },
13727
13728     /**
13729      * Get the index within the cache of the Record with the passed id.
13730      * @param {String} id The id of the Record to find.
13731      * @return {Number} The index of the Record. Returns -1 if not found.
13732      */
13733     indexOfId : function(id){
13734         return this.data.indexOfKey(id);
13735     },
13736
13737     /**
13738      * Get the Record with the specified id.
13739      * @param {String} id The id of the Record to find.
13740      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13741      */
13742     getById : function(id){
13743         return this.data.key(id);
13744     },
13745
13746     /**
13747      * Get the Record at the specified index.
13748      * @param {Number} index The index of the Record to find.
13749      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13750      */
13751     getAt : function(index){
13752         return this.data.itemAt(index);
13753     },
13754
13755     /**
13756      * Returns a range of Records between specified indices.
13757      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13758      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13759      * @return {Roo.data.Record[]} An array of Records
13760      */
13761     getRange : function(start, end){
13762         return this.data.getRange(start, end);
13763     },
13764
13765     // private
13766     storeOptions : function(o){
13767         o = Roo.apply({}, o);
13768         delete o.callback;
13769         delete o.scope;
13770         this.lastOptions = o;
13771     },
13772
13773     /**
13774      * Loads the Record cache from the configured Proxy using the configured Reader.
13775      * <p>
13776      * If using remote paging, then the first load call must specify the <em>start</em>
13777      * and <em>limit</em> properties in the options.params property to establish the initial
13778      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13779      * <p>
13780      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13781      * and this call will return before the new data has been loaded. Perform any post-processing
13782      * in a callback function, or in a "load" event handler.</strong>
13783      * <p>
13784      * @param {Object} options An object containing properties which control loading options:<ul>
13785      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13786      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13787      * passed the following arguments:<ul>
13788      * <li>r : Roo.data.Record[]</li>
13789      * <li>options: Options object from the load call</li>
13790      * <li>success: Boolean success indicator</li></ul></li>
13791      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13792      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13793      * </ul>
13794      */
13795     load : function(options){
13796         options = options || {};
13797         if(this.fireEvent("beforeload", this, options) !== false){
13798             this.storeOptions(options);
13799             var p = Roo.apply(options.params || {}, this.baseParams);
13800             // if meta was not loaded from remote source.. try requesting it.
13801             if (!this.reader.metaFromRemote) {
13802                 p._requestMeta = 1;
13803             }
13804             if(this.sortInfo && this.remoteSort){
13805                 var pn = this.paramNames;
13806                 p[pn["sort"]] = this.sortInfo.field;
13807                 p[pn["dir"]] = this.sortInfo.direction;
13808             }
13809             if (this.multiSort) {
13810                 var pn = this.paramNames;
13811                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13812             }
13813             
13814             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13815         }
13816     },
13817
13818     /**
13819      * Reloads the Record cache from the configured Proxy using the configured Reader and
13820      * the options from the last load operation performed.
13821      * @param {Object} options (optional) An object containing properties which may override the options
13822      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13823      * the most recently used options are reused).
13824      */
13825     reload : function(options){
13826         this.load(Roo.applyIf(options||{}, this.lastOptions));
13827     },
13828
13829     // private
13830     // Called as a callback by the Reader during a load operation.
13831     loadRecords : function(o, options, success){
13832         if(!o || success === false){
13833             if(success !== false){
13834                 this.fireEvent("load", this, [], options, o);
13835             }
13836             if(options.callback){
13837                 options.callback.call(options.scope || this, [], options, false);
13838             }
13839             return;
13840         }
13841         // if data returned failure - throw an exception.
13842         if (o.success === false) {
13843             // show a message if no listener is registered.
13844             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13845                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13846             }
13847             // loadmask wil be hooked into this..
13848             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13849             return;
13850         }
13851         var r = o.records, t = o.totalRecords || r.length;
13852         
13853         this.fireEvent("beforeloadadd", this, r, options, o);
13854         
13855         if(!options || options.add !== true){
13856             if(this.pruneModifiedRecords){
13857                 this.modified = [];
13858             }
13859             for(var i = 0, len = r.length; i < len; i++){
13860                 r[i].join(this);
13861             }
13862             if(this.snapshot){
13863                 this.data = this.snapshot;
13864                 delete this.snapshot;
13865             }
13866             this.data.clear();
13867             this.data.addAll(r);
13868             this.totalLength = t;
13869             this.applySort();
13870             this.fireEvent("datachanged", this);
13871         }else{
13872             this.totalLength = Math.max(t, this.data.length+r.length);
13873             this.add(r);
13874         }
13875         
13876         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13877                 
13878             var e = new Roo.data.Record({});
13879
13880             e.set(this.parent.displayField, this.parent.emptyTitle);
13881             e.set(this.parent.valueField, '');
13882
13883             this.insert(0, e);
13884         }
13885             
13886         this.fireEvent("load", this, r, options, o);
13887         if(options.callback){
13888             options.callback.call(options.scope || this, r, options, true);
13889         }
13890     },
13891
13892
13893     /**
13894      * Loads data from a passed data block. A Reader which understands the format of the data
13895      * must have been configured in the constructor.
13896      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13897      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13898      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13899      */
13900     loadData : function(o, append){
13901         var r = this.reader.readRecords(o);
13902         this.loadRecords(r, {add: append}, true);
13903     },
13904     
13905      /**
13906      * using 'cn' the nested child reader read the child array into it's child stores.
13907      * @param {Object} rec The record with a 'children array
13908      */
13909     loadDataFromChildren : function(rec)
13910     {
13911         this.loadData(this.reader.toLoadData(rec));
13912     },
13913     
13914
13915     /**
13916      * Gets the number of cached records.
13917      * <p>
13918      * <em>If using paging, this may not be the total size of the dataset. If the data object
13919      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13920      * the data set size</em>
13921      */
13922     getCount : function(){
13923         return this.data.length || 0;
13924     },
13925
13926     /**
13927      * Gets the total number of records in the dataset as returned by the server.
13928      * <p>
13929      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13930      * the dataset size</em>
13931      */
13932     getTotalCount : function(){
13933         return this.totalLength || 0;
13934     },
13935
13936     /**
13937      * Returns the sort state of the Store as an object with two properties:
13938      * <pre><code>
13939  field {String} The name of the field by which the Records are sorted
13940  direction {String} The sort order, "ASC" or "DESC"
13941      * </code></pre>
13942      */
13943     getSortState : function(){
13944         return this.sortInfo;
13945     },
13946
13947     // private
13948     applySort : function(){
13949         if(this.sortInfo && !this.remoteSort){
13950             var s = this.sortInfo, f = s.field;
13951             var st = this.fields.get(f).sortType;
13952             var fn = function(r1, r2){
13953                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13954                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13955             };
13956             this.data.sort(s.direction, fn);
13957             if(this.snapshot && this.snapshot != this.data){
13958                 this.snapshot.sort(s.direction, fn);
13959             }
13960         }
13961     },
13962
13963     /**
13964      * Sets the default sort column and order to be used by the next load operation.
13965      * @param {String} fieldName The name of the field to sort by.
13966      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13967      */
13968     setDefaultSort : function(field, dir){
13969         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13970     },
13971
13972     /**
13973      * Sort the Records.
13974      * If remote sorting is used, the sort is performed on the server, and the cache is
13975      * reloaded. If local sorting is used, the cache is sorted internally.
13976      * @param {String} fieldName The name of the field to sort by.
13977      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13978      */
13979     sort : function(fieldName, dir){
13980         var f = this.fields.get(fieldName);
13981         if(!dir){
13982             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13983             
13984             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13985                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13986             }else{
13987                 dir = f.sortDir;
13988             }
13989         }
13990         this.sortToggle[f.name] = dir;
13991         this.sortInfo = {field: f.name, direction: dir};
13992         if(!this.remoteSort){
13993             this.applySort();
13994             this.fireEvent("datachanged", this);
13995         }else{
13996             this.load(this.lastOptions);
13997         }
13998     },
13999
14000     /**
14001      * Calls the specified function for each of the Records in the cache.
14002      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14003      * Returning <em>false</em> aborts and exits the iteration.
14004      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14005      */
14006     each : function(fn, scope){
14007         this.data.each(fn, scope);
14008     },
14009
14010     /**
14011      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14012      * (e.g., during paging).
14013      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14014      */
14015     getModifiedRecords : function(){
14016         return this.modified;
14017     },
14018
14019     // private
14020     createFilterFn : function(property, value, anyMatch){
14021         if(!value.exec){ // not a regex
14022             value = String(value);
14023             if(value.length == 0){
14024                 return false;
14025             }
14026             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14027         }
14028         return function(r){
14029             return value.test(r.data[property]);
14030         };
14031     },
14032
14033     /**
14034      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14035      * @param {String} property A field on your records
14036      * @param {Number} start The record index to start at (defaults to 0)
14037      * @param {Number} end The last record index to include (defaults to length - 1)
14038      * @return {Number} The sum
14039      */
14040     sum : function(property, start, end){
14041         var rs = this.data.items, v = 0;
14042         start = start || 0;
14043         end = (end || end === 0) ? end : rs.length-1;
14044
14045         for(var i = start; i <= end; i++){
14046             v += (rs[i].data[property] || 0);
14047         }
14048         return v;
14049     },
14050
14051     /**
14052      * Filter the records by a specified property.
14053      * @param {String} field A field on your records
14054      * @param {String/RegExp} value Either a string that the field
14055      * should start with or a RegExp to test against the field
14056      * @param {Boolean} anyMatch True to match any part not just the beginning
14057      */
14058     filter : function(property, value, anyMatch){
14059         var fn = this.createFilterFn(property, value, anyMatch);
14060         return fn ? this.filterBy(fn) : this.clearFilter();
14061     },
14062
14063     /**
14064      * Filter by a function. The specified function will be called with each
14065      * record in this data source. If the function returns true the record is included,
14066      * otherwise it is filtered.
14067      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14068      * @param {Object} scope (optional) The scope of the function (defaults to this)
14069      */
14070     filterBy : function(fn, scope){
14071         this.snapshot = this.snapshot || this.data;
14072         this.data = this.queryBy(fn, scope||this);
14073         this.fireEvent("datachanged", this);
14074     },
14075
14076     /**
14077      * Query 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      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14083      */
14084     query : function(property, value, anyMatch){
14085         var fn = this.createFilterFn(property, value, anyMatch);
14086         return fn ? this.queryBy(fn) : this.data.clone();
14087     },
14088
14089     /**
14090      * Query by a function. The specified function will be called with each
14091      * record in this data source. If the function returns true the record is included
14092      * in the results.
14093      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14094      * @param {Object} scope (optional) The scope of the function (defaults to this)
14095       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14096      **/
14097     queryBy : function(fn, scope){
14098         var data = this.snapshot || this.data;
14099         return data.filterBy(fn, scope||this);
14100     },
14101
14102     /**
14103      * Collects unique values for a particular dataIndex from this store.
14104      * @param {String} dataIndex The property to collect
14105      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14106      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14107      * @return {Array} An array of the unique values
14108      **/
14109     collect : function(dataIndex, allowNull, bypassFilter){
14110         var d = (bypassFilter === true && this.snapshot) ?
14111                 this.snapshot.items : this.data.items;
14112         var v, sv, r = [], l = {};
14113         for(var i = 0, len = d.length; i < len; i++){
14114             v = d[i].data[dataIndex];
14115             sv = String(v);
14116             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14117                 l[sv] = true;
14118                 r[r.length] = v;
14119             }
14120         }
14121         return r;
14122     },
14123
14124     /**
14125      * Revert to a view of the Record cache with no filtering applied.
14126      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14127      */
14128     clearFilter : function(suppressEvent){
14129         if(this.snapshot && this.snapshot != this.data){
14130             this.data = this.snapshot;
14131             delete this.snapshot;
14132             if(suppressEvent !== true){
14133                 this.fireEvent("datachanged", this);
14134             }
14135         }
14136     },
14137
14138     // private
14139     afterEdit : function(record){
14140         if(this.modified.indexOf(record) == -1){
14141             this.modified.push(record);
14142         }
14143         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14144     },
14145     
14146     // private
14147     afterReject : function(record){
14148         this.modified.remove(record);
14149         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14150     },
14151
14152     // private
14153     afterCommit : function(record){
14154         this.modified.remove(record);
14155         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14156     },
14157
14158     /**
14159      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14160      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14161      */
14162     commitChanges : function(){
14163         var m = this.modified.slice(0);
14164         this.modified = [];
14165         for(var i = 0, len = m.length; i < len; i++){
14166             m[i].commit();
14167         }
14168     },
14169
14170     /**
14171      * Cancel outstanding changes on all changed records.
14172      */
14173     rejectChanges : function(){
14174         var m = this.modified.slice(0);
14175         this.modified = [];
14176         for(var i = 0, len = m.length; i < len; i++){
14177             m[i].reject();
14178         }
14179     },
14180
14181     onMetaChange : function(meta, rtype, o){
14182         this.recordType = rtype;
14183         this.fields = rtype.prototype.fields;
14184         delete this.snapshot;
14185         this.sortInfo = meta.sortInfo || this.sortInfo;
14186         this.modified = [];
14187         this.fireEvent('metachange', this, this.reader.meta);
14188     },
14189     
14190     moveIndex : function(data, type)
14191     {
14192         var index = this.indexOf(data);
14193         
14194         var newIndex = index + type;
14195         
14196         this.remove(data);
14197         
14198         this.insert(newIndex, data);
14199         
14200     }
14201 });/*
14202  * Based on:
14203  * Ext JS Library 1.1.1
14204  * Copyright(c) 2006-2007, Ext JS, LLC.
14205  *
14206  * Originally Released Under LGPL - original licence link has changed is not relivant.
14207  *
14208  * Fork - LGPL
14209  * <script type="text/javascript">
14210  */
14211
14212 /**
14213  * @class Roo.data.SimpleStore
14214  * @extends Roo.data.Store
14215  * Small helper class to make creating Stores from Array data easier.
14216  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14217  * @cfg {Array} fields An array of field definition objects, or field name strings.
14218  * @cfg {Object} an existing reader (eg. copied from another store)
14219  * @cfg {Array} data The multi-dimensional array of data
14220  * @constructor
14221  * @param {Object} config
14222  */
14223 Roo.data.SimpleStore = function(config)
14224 {
14225     Roo.data.SimpleStore.superclass.constructor.call(this, {
14226         isLocal : true,
14227         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14228                 id: config.id
14229             },
14230             Roo.data.Record.create(config.fields)
14231         ),
14232         proxy : new Roo.data.MemoryProxy(config.data)
14233     });
14234     this.load();
14235 };
14236 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14237  * Based on:
14238  * Ext JS Library 1.1.1
14239  * Copyright(c) 2006-2007, Ext JS, LLC.
14240  *
14241  * Originally Released Under LGPL - original licence link has changed is not relivant.
14242  *
14243  * Fork - LGPL
14244  * <script type="text/javascript">
14245  */
14246
14247 /**
14248 /**
14249  * @extends Roo.data.Store
14250  * @class Roo.data.JsonStore
14251  * Small helper class to make creating Stores for JSON data easier. <br/>
14252 <pre><code>
14253 var store = new Roo.data.JsonStore({
14254     url: 'get-images.php',
14255     root: 'images',
14256     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14257 });
14258 </code></pre>
14259  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14260  * JsonReader and HttpProxy (unless inline data is provided).</b>
14261  * @cfg {Array} fields An array of field definition objects, or field name strings.
14262  * @constructor
14263  * @param {Object} config
14264  */
14265 Roo.data.JsonStore = function(c){
14266     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14267         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14268         reader: new Roo.data.JsonReader(c, c.fields)
14269     }));
14270 };
14271 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14272  * Based on:
14273  * Ext JS Library 1.1.1
14274  * Copyright(c) 2006-2007, Ext JS, LLC.
14275  *
14276  * Originally Released Under LGPL - original licence link has changed is not relivant.
14277  *
14278  * Fork - LGPL
14279  * <script type="text/javascript">
14280  */
14281
14282  
14283 Roo.data.Field = function(config){
14284     if(typeof config == "string"){
14285         config = {name: config};
14286     }
14287     Roo.apply(this, config);
14288     
14289     if(!this.type){
14290         this.type = "auto";
14291     }
14292     
14293     var st = Roo.data.SortTypes;
14294     // named sortTypes are supported, here we look them up
14295     if(typeof this.sortType == "string"){
14296         this.sortType = st[this.sortType];
14297     }
14298     
14299     // set default sortType for strings and dates
14300     if(!this.sortType){
14301         switch(this.type){
14302             case "string":
14303                 this.sortType = st.asUCString;
14304                 break;
14305             case "date":
14306                 this.sortType = st.asDate;
14307                 break;
14308             default:
14309                 this.sortType = st.none;
14310         }
14311     }
14312
14313     // define once
14314     var stripRe = /[\$,%]/g;
14315
14316     // prebuilt conversion function for this field, instead of
14317     // switching every time we're reading a value
14318     if(!this.convert){
14319         var cv, dateFormat = this.dateFormat;
14320         switch(this.type){
14321             case "":
14322             case "auto":
14323             case undefined:
14324                 cv = function(v){ return v; };
14325                 break;
14326             case "string":
14327                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14328                 break;
14329             case "int":
14330                 cv = function(v){
14331                     return v !== undefined && v !== null && v !== '' ?
14332                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14333                     };
14334                 break;
14335             case "float":
14336                 cv = function(v){
14337                     return v !== undefined && v !== null && v !== '' ?
14338                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14339                     };
14340                 break;
14341             case "bool":
14342             case "boolean":
14343                 cv = function(v){ return v === true || v === "true" || v == 1; };
14344                 break;
14345             case "date":
14346                 cv = function(v){
14347                     if(!v){
14348                         return '';
14349                     }
14350                     if(v instanceof Date){
14351                         return v;
14352                     }
14353                     if(dateFormat){
14354                         if(dateFormat == "timestamp"){
14355                             return new Date(v*1000);
14356                         }
14357                         return Date.parseDate(v, dateFormat);
14358                     }
14359                     var parsed = Date.parse(v);
14360                     return parsed ? new Date(parsed) : null;
14361                 };
14362              break;
14363             
14364         }
14365         this.convert = cv;
14366     }
14367 };
14368
14369 Roo.data.Field.prototype = {
14370     dateFormat: null,
14371     defaultValue: "",
14372     mapping: null,
14373     sortType : null,
14374     sortDir : "ASC"
14375 };/*
14376  * Based on:
14377  * Ext JS Library 1.1.1
14378  * Copyright(c) 2006-2007, Ext JS, LLC.
14379  *
14380  * Originally Released Under LGPL - original licence link has changed is not relivant.
14381  *
14382  * Fork - LGPL
14383  * <script type="text/javascript">
14384  */
14385  
14386 // Base class for reading structured data from a data source.  This class is intended to be
14387 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14388
14389 /**
14390  * @class Roo.data.DataReader
14391  * Base class for reading structured data from a data source.  This class is intended to be
14392  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14393  */
14394
14395 Roo.data.DataReader = function(meta, recordType){
14396     
14397     this.meta = meta;
14398     
14399     this.recordType = recordType instanceof Array ? 
14400         Roo.data.Record.create(recordType) : recordType;
14401 };
14402
14403 Roo.data.DataReader.prototype = {
14404     
14405     
14406     readerType : 'Data',
14407      /**
14408      * Create an empty record
14409      * @param {Object} data (optional) - overlay some values
14410      * @return {Roo.data.Record} record created.
14411      */
14412     newRow :  function(d) {
14413         var da =  {};
14414         this.recordType.prototype.fields.each(function(c) {
14415             switch( c.type) {
14416                 case 'int' : da[c.name] = 0; break;
14417                 case 'date' : da[c.name] = new Date(); break;
14418                 case 'float' : da[c.name] = 0.0; break;
14419                 case 'boolean' : da[c.name] = false; break;
14420                 default : da[c.name] = ""; break;
14421             }
14422             
14423         });
14424         return new this.recordType(Roo.apply(da, d));
14425     }
14426     
14427     
14428 };/*
14429  * Based on:
14430  * Ext JS Library 1.1.1
14431  * Copyright(c) 2006-2007, Ext JS, LLC.
14432  *
14433  * Originally Released Under LGPL - original licence link has changed is not relivant.
14434  *
14435  * Fork - LGPL
14436  * <script type="text/javascript">
14437  */
14438
14439 /**
14440  * @class Roo.data.DataProxy
14441  * @extends Roo.data.Observable
14442  * This class is an abstract base class for implementations which provide retrieval of
14443  * unformatted data objects.<br>
14444  * <p>
14445  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14446  * (of the appropriate type which knows how to parse the data object) to provide a block of
14447  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14448  * <p>
14449  * Custom implementations must implement the load method as described in
14450  * {@link Roo.data.HttpProxy#load}.
14451  */
14452 Roo.data.DataProxy = function(){
14453     this.addEvents({
14454         /**
14455          * @event beforeload
14456          * Fires before a network request is made to retrieve a data object.
14457          * @param {Object} This DataProxy object.
14458          * @param {Object} params The params parameter to the load function.
14459          */
14460         beforeload : true,
14461         /**
14462          * @event load
14463          * Fires before the load method's callback is called.
14464          * @param {Object} This DataProxy object.
14465          * @param {Object} o The data object.
14466          * @param {Object} arg The callback argument object passed to the load function.
14467          */
14468         load : true,
14469         /**
14470          * @event loadexception
14471          * Fires if an Exception occurs during data retrieval.
14472          * @param {Object} This DataProxy object.
14473          * @param {Object} o The data object.
14474          * @param {Object} arg The callback argument object passed to the load function.
14475          * @param {Object} e The Exception.
14476          */
14477         loadexception : true
14478     });
14479     Roo.data.DataProxy.superclass.constructor.call(this);
14480 };
14481
14482 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14483
14484     /**
14485      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14486      */
14487 /*
14488  * Based on:
14489  * Ext JS Library 1.1.1
14490  * Copyright(c) 2006-2007, Ext JS, LLC.
14491  *
14492  * Originally Released Under LGPL - original licence link has changed is not relivant.
14493  *
14494  * Fork - LGPL
14495  * <script type="text/javascript">
14496  */
14497 /**
14498  * @class Roo.data.MemoryProxy
14499  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14500  * to the Reader when its load method is called.
14501  * @constructor
14502  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14503  */
14504 Roo.data.MemoryProxy = function(data){
14505     if (data.data) {
14506         data = data.data;
14507     }
14508     Roo.data.MemoryProxy.superclass.constructor.call(this);
14509     this.data = data;
14510 };
14511
14512 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14513     
14514     /**
14515      * Load data from the requested source (in this case an in-memory
14516      * data object passed to the constructor), read the data object into
14517      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14518      * process that block using the passed callback.
14519      * @param {Object} params This parameter is not used by the MemoryProxy class.
14520      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14521      * object into a block of Roo.data.Records.
14522      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14523      * The function must be passed <ul>
14524      * <li>The Record block object</li>
14525      * <li>The "arg" argument from the load function</li>
14526      * <li>A boolean success indicator</li>
14527      * </ul>
14528      * @param {Object} scope The scope in which to call the callback
14529      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14530      */
14531     load : function(params, reader, callback, scope, arg){
14532         params = params || {};
14533         var result;
14534         try {
14535             result = reader.readRecords(params.data ? params.data :this.data);
14536         }catch(e){
14537             this.fireEvent("loadexception", this, arg, null, e);
14538             callback.call(scope, null, arg, false);
14539             return;
14540         }
14541         callback.call(scope, result, arg, true);
14542     },
14543     
14544     // private
14545     update : function(params, records){
14546         
14547     }
14548 });/*
14549  * Based on:
14550  * Ext JS Library 1.1.1
14551  * Copyright(c) 2006-2007, Ext JS, LLC.
14552  *
14553  * Originally Released Under LGPL - original licence link has changed is not relivant.
14554  *
14555  * Fork - LGPL
14556  * <script type="text/javascript">
14557  */
14558 /**
14559  * @class Roo.data.HttpProxy
14560  * @extends Roo.data.DataProxy
14561  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14562  * configured to reference a certain URL.<br><br>
14563  * <p>
14564  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14565  * from which the running page was served.<br><br>
14566  * <p>
14567  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14568  * <p>
14569  * Be aware that to enable the browser to parse an XML document, the server must set
14570  * the Content-Type header in the HTTP response to "text/xml".
14571  * @constructor
14572  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14573  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14574  * will be used to make the request.
14575  */
14576 Roo.data.HttpProxy = function(conn){
14577     Roo.data.HttpProxy.superclass.constructor.call(this);
14578     // is conn a conn config or a real conn?
14579     this.conn = conn;
14580     this.useAjax = !conn || !conn.events;
14581   
14582 };
14583
14584 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14585     // thse are take from connection...
14586     
14587     /**
14588      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14589      */
14590     /**
14591      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14592      * extra parameters to each request made by this object. (defaults to undefined)
14593      */
14594     /**
14595      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14596      *  to each request made by this object. (defaults to undefined)
14597      */
14598     /**
14599      * @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)
14600      */
14601     /**
14602      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14603      */
14604      /**
14605      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14606      * @type Boolean
14607      */
14608   
14609
14610     /**
14611      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14612      * @type Boolean
14613      */
14614     /**
14615      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14616      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14617      * a finer-grained basis than the DataProxy events.
14618      */
14619     getConnection : function(){
14620         return this.useAjax ? Roo.Ajax : this.conn;
14621     },
14622
14623     /**
14624      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14625      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14626      * process that block using the passed callback.
14627      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14628      * for the request to the remote server.
14629      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14630      * object into a block of Roo.data.Records.
14631      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14632      * The function must be passed <ul>
14633      * <li>The Record block object</li>
14634      * <li>The "arg" argument from the load function</li>
14635      * <li>A boolean success indicator</li>
14636      * </ul>
14637      * @param {Object} scope The scope in which to call the callback
14638      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14639      */
14640     load : function(params, reader, callback, scope, arg){
14641         if(this.fireEvent("beforeload", this, params) !== false){
14642             var  o = {
14643                 params : params || {},
14644                 request: {
14645                     callback : callback,
14646                     scope : scope,
14647                     arg : arg
14648                 },
14649                 reader: reader,
14650                 callback : this.loadResponse,
14651                 scope: this
14652             };
14653             if(this.useAjax){
14654                 Roo.applyIf(o, this.conn);
14655                 if(this.activeRequest){
14656                     Roo.Ajax.abort(this.activeRequest);
14657                 }
14658                 this.activeRequest = Roo.Ajax.request(o);
14659             }else{
14660                 this.conn.request(o);
14661             }
14662         }else{
14663             callback.call(scope||this, null, arg, false);
14664         }
14665     },
14666
14667     // private
14668     loadResponse : function(o, success, response){
14669         delete this.activeRequest;
14670         if(!success){
14671             this.fireEvent("loadexception", this, o, response);
14672             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14673             return;
14674         }
14675         var result;
14676         try {
14677             result = o.reader.read(response);
14678         }catch(e){
14679             this.fireEvent("loadexception", this, o, response, e);
14680             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14681             return;
14682         }
14683         
14684         this.fireEvent("load", this, o, o.request.arg);
14685         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14686     },
14687
14688     // private
14689     update : function(dataSet){
14690
14691     },
14692
14693     // private
14694     updateResponse : function(dataSet){
14695
14696     }
14697 });/*
14698  * Based on:
14699  * Ext JS Library 1.1.1
14700  * Copyright(c) 2006-2007, Ext JS, LLC.
14701  *
14702  * Originally Released Under LGPL - original licence link has changed is not relivant.
14703  *
14704  * Fork - LGPL
14705  * <script type="text/javascript">
14706  */
14707
14708 /**
14709  * @class Roo.data.ScriptTagProxy
14710  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14711  * other than the originating domain of the running page.<br><br>
14712  * <p>
14713  * <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
14714  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14715  * <p>
14716  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14717  * source code that is used as the source inside a &lt;script> tag.<br><br>
14718  * <p>
14719  * In order for the browser to process the returned data, the server must wrap the data object
14720  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14721  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14722  * depending on whether the callback name was passed:
14723  * <p>
14724  * <pre><code>
14725 boolean scriptTag = false;
14726 String cb = request.getParameter("callback");
14727 if (cb != null) {
14728     scriptTag = true;
14729     response.setContentType("text/javascript");
14730 } else {
14731     response.setContentType("application/x-json");
14732 }
14733 Writer out = response.getWriter();
14734 if (scriptTag) {
14735     out.write(cb + "(");
14736 }
14737 out.print(dataBlock.toJsonString());
14738 if (scriptTag) {
14739     out.write(");");
14740 }
14741 </pre></code>
14742  *
14743  * @constructor
14744  * @param {Object} config A configuration object.
14745  */
14746 Roo.data.ScriptTagProxy = function(config){
14747     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14748     Roo.apply(this, config);
14749     this.head = document.getElementsByTagName("head")[0];
14750 };
14751
14752 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14753
14754 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14755     /**
14756      * @cfg {String} url The URL from which to request the data object.
14757      */
14758     /**
14759      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14760      */
14761     timeout : 30000,
14762     /**
14763      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14764      * the server the name of the callback function set up by the load call to process the returned data object.
14765      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14766      * javascript output which calls this named function passing the data object as its only parameter.
14767      */
14768     callbackParam : "callback",
14769     /**
14770      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14771      * name to the request.
14772      */
14773     nocache : true,
14774
14775     /**
14776      * Load data from the configured URL, read the data object into
14777      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14778      * process that block using the passed callback.
14779      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14780      * for the request to the remote server.
14781      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14782      * object into a block of Roo.data.Records.
14783      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14784      * The function must be passed <ul>
14785      * <li>The Record block object</li>
14786      * <li>The "arg" argument from the load function</li>
14787      * <li>A boolean success indicator</li>
14788      * </ul>
14789      * @param {Object} scope The scope in which to call the callback
14790      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14791      */
14792     load : function(params, reader, callback, scope, arg){
14793         if(this.fireEvent("beforeload", this, params) !== false){
14794
14795             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14796
14797             var url = this.url;
14798             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14799             if(this.nocache){
14800                 url += "&_dc=" + (new Date().getTime());
14801             }
14802             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14803             var trans = {
14804                 id : transId,
14805                 cb : "stcCallback"+transId,
14806                 scriptId : "stcScript"+transId,
14807                 params : params,
14808                 arg : arg,
14809                 url : url,
14810                 callback : callback,
14811                 scope : scope,
14812                 reader : reader
14813             };
14814             var conn = this;
14815
14816             window[trans.cb] = function(o){
14817                 conn.handleResponse(o, trans);
14818             };
14819
14820             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14821
14822             if(this.autoAbort !== false){
14823                 this.abort();
14824             }
14825
14826             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14827
14828             var script = document.createElement("script");
14829             script.setAttribute("src", url);
14830             script.setAttribute("type", "text/javascript");
14831             script.setAttribute("id", trans.scriptId);
14832             this.head.appendChild(script);
14833
14834             this.trans = trans;
14835         }else{
14836             callback.call(scope||this, null, arg, false);
14837         }
14838     },
14839
14840     // private
14841     isLoading : function(){
14842         return this.trans ? true : false;
14843     },
14844
14845     /**
14846      * Abort the current server request.
14847      */
14848     abort : function(){
14849         if(this.isLoading()){
14850             this.destroyTrans(this.trans);
14851         }
14852     },
14853
14854     // private
14855     destroyTrans : function(trans, isLoaded){
14856         this.head.removeChild(document.getElementById(trans.scriptId));
14857         clearTimeout(trans.timeoutId);
14858         if(isLoaded){
14859             window[trans.cb] = undefined;
14860             try{
14861                 delete window[trans.cb];
14862             }catch(e){}
14863         }else{
14864             // if hasn't been loaded, wait for load to remove it to prevent script error
14865             window[trans.cb] = function(){
14866                 window[trans.cb] = undefined;
14867                 try{
14868                     delete window[trans.cb];
14869                 }catch(e){}
14870             };
14871         }
14872     },
14873
14874     // private
14875     handleResponse : function(o, trans){
14876         this.trans = false;
14877         this.destroyTrans(trans, true);
14878         var result;
14879         try {
14880             result = trans.reader.readRecords(o);
14881         }catch(e){
14882             this.fireEvent("loadexception", this, o, trans.arg, e);
14883             trans.callback.call(trans.scope||window, null, trans.arg, false);
14884             return;
14885         }
14886         this.fireEvent("load", this, o, trans.arg);
14887         trans.callback.call(trans.scope||window, result, trans.arg, true);
14888     },
14889
14890     // private
14891     handleFailure : function(trans){
14892         this.trans = false;
14893         this.destroyTrans(trans, false);
14894         this.fireEvent("loadexception", this, null, trans.arg);
14895         trans.callback.call(trans.scope||window, null, trans.arg, false);
14896     }
14897 });/*
14898  * Based on:
14899  * Ext JS Library 1.1.1
14900  * Copyright(c) 2006-2007, Ext JS, LLC.
14901  *
14902  * Originally Released Under LGPL - original licence link has changed is not relivant.
14903  *
14904  * Fork - LGPL
14905  * <script type="text/javascript">
14906  */
14907
14908 /**
14909  * @class Roo.data.JsonReader
14910  * @extends Roo.data.DataReader
14911  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14912  * based on mappings in a provided Roo.data.Record constructor.
14913  * 
14914  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14915  * in the reply previously. 
14916  * 
14917  * <p>
14918  * Example code:
14919  * <pre><code>
14920 var RecordDef = Roo.data.Record.create([
14921     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14922     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14923 ]);
14924 var myReader = new Roo.data.JsonReader({
14925     totalProperty: "results",    // The property which contains the total dataset size (optional)
14926     root: "rows",                // The property which contains an Array of row objects
14927     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14928 }, RecordDef);
14929 </code></pre>
14930  * <p>
14931  * This would consume a JSON file like this:
14932  * <pre><code>
14933 { 'results': 2, 'rows': [
14934     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14935     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14936 }
14937 </code></pre>
14938  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14939  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14940  * paged from the remote server.
14941  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14942  * @cfg {String} root name of the property which contains the Array of row objects.
14943  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14944  * @cfg {Array} fields Array of field definition objects
14945  * @constructor
14946  * Create a new JsonReader
14947  * @param {Object} meta Metadata configuration options
14948  * @param {Object} recordType Either an Array of field definition objects,
14949  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14950  */
14951 Roo.data.JsonReader = function(meta, recordType){
14952     
14953     meta = meta || {};
14954     // set some defaults:
14955     Roo.applyIf(meta, {
14956         totalProperty: 'total',
14957         successProperty : 'success',
14958         root : 'data',
14959         id : 'id'
14960     });
14961     
14962     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14963 };
14964 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14965     
14966     readerType : 'Json',
14967     
14968     /**
14969      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14970      * Used by Store query builder to append _requestMeta to params.
14971      * 
14972      */
14973     metaFromRemote : false,
14974     /**
14975      * This method is only used by a DataProxy which has retrieved data from a remote server.
14976      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14977      * @return {Object} data A data block which is used by an Roo.data.Store object as
14978      * a cache of Roo.data.Records.
14979      */
14980     read : function(response){
14981         var json = response.responseText;
14982        
14983         var o = /* eval:var:o */ eval("("+json+")");
14984         if(!o) {
14985             throw {message: "JsonReader.read: Json object not found"};
14986         }
14987         
14988         if(o.metaData){
14989             
14990             delete this.ef;
14991             this.metaFromRemote = true;
14992             this.meta = o.metaData;
14993             this.recordType = Roo.data.Record.create(o.metaData.fields);
14994             this.onMetaChange(this.meta, this.recordType, o);
14995         }
14996         return this.readRecords(o);
14997     },
14998
14999     // private function a store will implement
15000     onMetaChange : function(meta, recordType, o){
15001
15002     },
15003
15004     /**
15005          * @ignore
15006          */
15007     simpleAccess: function(obj, subsc) {
15008         return obj[subsc];
15009     },
15010
15011         /**
15012          * @ignore
15013          */
15014     getJsonAccessor: function(){
15015         var re = /[\[\.]/;
15016         return function(expr) {
15017             try {
15018                 return(re.test(expr))
15019                     ? new Function("obj", "return obj." + expr)
15020                     : function(obj){
15021                         return obj[expr];
15022                     };
15023             } catch(e){}
15024             return Roo.emptyFn;
15025         };
15026     }(),
15027
15028     /**
15029      * Create a data block containing Roo.data.Records from an XML document.
15030      * @param {Object} o An object which contains an Array of row objects in the property specified
15031      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15032      * which contains the total size of the dataset.
15033      * @return {Object} data A data block which is used by an Roo.data.Store object as
15034      * a cache of Roo.data.Records.
15035      */
15036     readRecords : function(o){
15037         /**
15038          * After any data loads, the raw JSON data is available for further custom processing.
15039          * @type Object
15040          */
15041         this.o = o;
15042         var s = this.meta, Record = this.recordType,
15043             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15044
15045 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15046         if (!this.ef) {
15047             if(s.totalProperty) {
15048                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15049                 }
15050                 if(s.successProperty) {
15051                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15052                 }
15053                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15054                 if (s.id) {
15055                         var g = this.getJsonAccessor(s.id);
15056                         this.getId = function(rec) {
15057                                 var r = g(rec);  
15058                                 return (r === undefined || r === "") ? null : r;
15059                         };
15060                 } else {
15061                         this.getId = function(){return null;};
15062                 }
15063             this.ef = [];
15064             for(var jj = 0; jj < fl; jj++){
15065                 f = fi[jj];
15066                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15067                 this.ef[jj] = this.getJsonAccessor(map);
15068             }
15069         }
15070
15071         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15072         if(s.totalProperty){
15073             var vt = parseInt(this.getTotal(o), 10);
15074             if(!isNaN(vt)){
15075                 totalRecords = vt;
15076             }
15077         }
15078         if(s.successProperty){
15079             var vs = this.getSuccess(o);
15080             if(vs === false || vs === 'false'){
15081                 success = false;
15082             }
15083         }
15084         var records = [];
15085         for(var i = 0; i < c; i++){
15086                 var n = root[i];
15087             var values = {};
15088             var id = this.getId(n);
15089             for(var j = 0; j < fl; j++){
15090                 f = fi[j];
15091             var v = this.ef[j](n);
15092             if (!f.convert) {
15093                 Roo.log('missing convert for ' + f.name);
15094                 Roo.log(f);
15095                 continue;
15096             }
15097             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15098             }
15099             var record = new Record(values, id);
15100             record.json = n;
15101             records[i] = record;
15102         }
15103         return {
15104             raw : o,
15105             success : success,
15106             records : records,
15107             totalRecords : totalRecords
15108         };
15109     },
15110     // used when loading children.. @see loadDataFromChildren
15111     toLoadData: function(rec)
15112     {
15113         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15114         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15115         return { data : data, total : data.length };
15116         
15117     }
15118 });/*
15119  * Based on:
15120  * Ext JS Library 1.1.1
15121  * Copyright(c) 2006-2007, Ext JS, LLC.
15122  *
15123  * Originally Released Under LGPL - original licence link has changed is not relivant.
15124  *
15125  * Fork - LGPL
15126  * <script type="text/javascript">
15127  */
15128
15129 /**
15130  * @class Roo.data.ArrayReader
15131  * @extends Roo.data.DataReader
15132  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15133  * Each element of that Array represents a row of data fields. The
15134  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15135  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15136  * <p>
15137  * Example code:.
15138  * <pre><code>
15139 var RecordDef = Roo.data.Record.create([
15140     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15141     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15142 ]);
15143 var myReader = new Roo.data.ArrayReader({
15144     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15145 }, RecordDef);
15146 </code></pre>
15147  * <p>
15148  * This would consume an Array like this:
15149  * <pre><code>
15150 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15151   </code></pre>
15152  
15153  * @constructor
15154  * Create a new JsonReader
15155  * @param {Object} meta Metadata configuration options.
15156  * @param {Object|Array} recordType Either an Array of field definition objects
15157  * 
15158  * @cfg {Array} fields Array of field definition objects
15159  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15160  * as specified to {@link Roo.data.Record#create},
15161  * or an {@link Roo.data.Record} object
15162  *
15163  * 
15164  * created using {@link Roo.data.Record#create}.
15165  */
15166 Roo.data.ArrayReader = function(meta, recordType)
15167 {    
15168     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15169 };
15170
15171 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15172     
15173       /**
15174      * Create a data block containing Roo.data.Records from an XML document.
15175      * @param {Object} o An Array of row objects which represents the dataset.
15176      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15177      * a cache of Roo.data.Records.
15178      */
15179     readRecords : function(o)
15180     {
15181         var sid = this.meta ? this.meta.id : null;
15182         var recordType = this.recordType, fields = recordType.prototype.fields;
15183         var records = [];
15184         var root = o;
15185         for(var i = 0; i < root.length; i++){
15186             var n = root[i];
15187             var values = {};
15188             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15189             for(var j = 0, jlen = fields.length; j < jlen; j++){
15190                 var f = fields.items[j];
15191                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15192                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15193                 v = f.convert(v);
15194                 values[f.name] = v;
15195             }
15196             var record = new recordType(values, id);
15197             record.json = n;
15198             records[records.length] = record;
15199         }
15200         return {
15201             records : records,
15202             totalRecords : records.length
15203         };
15204     },
15205     // used when loading children.. @see loadDataFromChildren
15206     toLoadData: function(rec)
15207     {
15208         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15209         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15210         
15211     }
15212     
15213     
15214 });/*
15215  * - LGPL
15216  * * 
15217  */
15218
15219 /**
15220  * @class Roo.bootstrap.ComboBox
15221  * @extends Roo.bootstrap.TriggerField
15222  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15223  * @cfg {Boolean} append (true|false) default false
15224  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15225  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15226  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15227  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15228  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15229  * @cfg {Boolean} animate default true
15230  * @cfg {Boolean} emptyResultText only for touch device
15231  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15232  * @cfg {String} emptyTitle default ''
15233  * @cfg {Number} width fixed with? experimental
15234  * @constructor
15235  * Create a new ComboBox.
15236  * @param {Object} config Configuration options
15237  */
15238 Roo.bootstrap.ComboBox = function(config){
15239     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15240     this.addEvents({
15241         /**
15242          * @event expand
15243          * Fires when the dropdown list is expanded
15244         * @param {Roo.bootstrap.ComboBox} combo This combo box
15245         */
15246         'expand' : true,
15247         /**
15248          * @event collapse
15249          * Fires when the dropdown list is collapsed
15250         * @param {Roo.bootstrap.ComboBox} combo This combo box
15251         */
15252         'collapse' : true,
15253         /**
15254          * @event beforeselect
15255          * Fires before a list item is selected. Return false to cancel the selection.
15256         * @param {Roo.bootstrap.ComboBox} combo This combo box
15257         * @param {Roo.data.Record} record The data record returned from the underlying store
15258         * @param {Number} index The index of the selected item in the dropdown list
15259         */
15260         'beforeselect' : true,
15261         /**
15262          * @event select
15263          * Fires when a list item is selected
15264         * @param {Roo.bootstrap.ComboBox} combo This combo box
15265         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15266         * @param {Number} index The index of the selected item in the dropdown list
15267         */
15268         'select' : true,
15269         /**
15270          * @event beforequery
15271          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15272          * The event object passed has these properties:
15273         * @param {Roo.bootstrap.ComboBox} combo This combo box
15274         * @param {String} query The query
15275         * @param {Boolean} forceAll true to force "all" query
15276         * @param {Boolean} cancel true to cancel the query
15277         * @param {Object} e The query event object
15278         */
15279         'beforequery': true,
15280          /**
15281          * @event add
15282          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15283         * @param {Roo.bootstrap.ComboBox} combo This combo box
15284         */
15285         'add' : true,
15286         /**
15287          * @event edit
15288          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15289         * @param {Roo.bootstrap.ComboBox} combo This combo box
15290         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15291         */
15292         'edit' : true,
15293         /**
15294          * @event remove
15295          * Fires when the remove value from the combobox array
15296         * @param {Roo.bootstrap.ComboBox} combo This combo box
15297         */
15298         'remove' : true,
15299         /**
15300          * @event afterremove
15301          * Fires when the remove value from the combobox array
15302         * @param {Roo.bootstrap.ComboBox} combo This combo box
15303         */
15304         'afterremove' : true,
15305         /**
15306          * @event specialfilter
15307          * Fires when specialfilter
15308             * @param {Roo.bootstrap.ComboBox} combo This combo box
15309             */
15310         'specialfilter' : true,
15311         /**
15312          * @event tick
15313          * Fires when tick the element
15314             * @param {Roo.bootstrap.ComboBox} combo This combo box
15315             */
15316         'tick' : true,
15317         /**
15318          * @event touchviewdisplay
15319          * Fires when touch view require special display (default is using displayField)
15320             * @param {Roo.bootstrap.ComboBox} combo This combo box
15321             * @param {Object} cfg set html .
15322             */
15323         'touchviewdisplay' : true
15324         
15325     });
15326     
15327     this.item = [];
15328     this.tickItems = [];
15329     
15330     this.selectedIndex = -1;
15331     if(this.mode == 'local'){
15332         if(config.queryDelay === undefined){
15333             this.queryDelay = 10;
15334         }
15335         if(config.minChars === undefined){
15336             this.minChars = 0;
15337         }
15338     }
15339 };
15340
15341 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15342      
15343     /**
15344      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15345      * rendering into an Roo.Editor, defaults to false)
15346      */
15347     /**
15348      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15349      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15350      */
15351     /**
15352      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15353      */
15354     /**
15355      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15356      * the dropdown list (defaults to undefined, with no header element)
15357      */
15358
15359      /**
15360      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15361      */
15362      
15363      /**
15364      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15365      */
15366     listWidth: undefined,
15367     /**
15368      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15369      * mode = 'remote' or 'text' if mode = 'local')
15370      */
15371     displayField: undefined,
15372     
15373     /**
15374      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15375      * mode = 'remote' or 'value' if mode = 'local'). 
15376      * Note: use of a valueField requires the user make a selection
15377      * in order for a value to be mapped.
15378      */
15379     valueField: undefined,
15380     /**
15381      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15382      */
15383     modalTitle : '',
15384     
15385     /**
15386      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15387      * field's data value (defaults to the underlying DOM element's name)
15388      */
15389     hiddenName: undefined,
15390     /**
15391      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15392      */
15393     listClass: '',
15394     /**
15395      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15396      */
15397     selectedClass: 'active',
15398     
15399     /**
15400      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15401      */
15402     shadow:'sides',
15403     /**
15404      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15405      * anchor positions (defaults to 'tl-bl')
15406      */
15407     listAlign: 'tl-bl?',
15408     /**
15409      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15410      */
15411     maxHeight: 300,
15412     /**
15413      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15414      * query specified by the allQuery config option (defaults to 'query')
15415      */
15416     triggerAction: 'query',
15417     /**
15418      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15419      * (defaults to 4, does not apply if editable = false)
15420      */
15421     minChars : 4,
15422     /**
15423      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15424      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15425      */
15426     typeAhead: false,
15427     /**
15428      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15429      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15430      */
15431     queryDelay: 500,
15432     /**
15433      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15434      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15435      */
15436     pageSize: 0,
15437     /**
15438      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15439      * when editable = true (defaults to false)
15440      */
15441     selectOnFocus:false,
15442     /**
15443      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15444      */
15445     queryParam: 'query',
15446     /**
15447      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15448      * when mode = 'remote' (defaults to 'Loading...')
15449      */
15450     loadingText: 'Loading...',
15451     /**
15452      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15453      */
15454     resizable: false,
15455     /**
15456      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15457      */
15458     handleHeight : 8,
15459     /**
15460      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15461      * traditional select (defaults to true)
15462      */
15463     editable: true,
15464     /**
15465      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15466      */
15467     allQuery: '',
15468     /**
15469      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15470      */
15471     mode: 'remote',
15472     /**
15473      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15474      * listWidth has a higher value)
15475      */
15476     minListWidth : 70,
15477     /**
15478      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15479      * allow the user to set arbitrary text into the field (defaults to false)
15480      */
15481     forceSelection:false,
15482     /**
15483      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15484      * if typeAhead = true (defaults to 250)
15485      */
15486     typeAheadDelay : 250,
15487     /**
15488      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15489      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15490      */
15491     valueNotFoundText : undefined,
15492     /**
15493      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15494      */
15495     blockFocus : false,
15496     
15497     /**
15498      * @cfg {Boolean} disableClear Disable showing of clear button.
15499      */
15500     disableClear : false,
15501     /**
15502      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15503      */
15504     alwaysQuery : false,
15505     
15506     /**
15507      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15508      */
15509     multiple : false,
15510     
15511     /**
15512      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15513      */
15514     invalidClass : "has-warning",
15515     
15516     /**
15517      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15518      */
15519     validClass : "has-success",
15520     
15521     /**
15522      * @cfg {Boolean} specialFilter (true|false) special filter default false
15523      */
15524     specialFilter : false,
15525     
15526     /**
15527      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15528      */
15529     mobileTouchView : true,
15530     
15531     /**
15532      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15533      */
15534     useNativeIOS : false,
15535     
15536     /**
15537      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15538      */
15539     mobile_restrict_height : false,
15540     
15541     ios_options : false,
15542     
15543     //private
15544     addicon : false,
15545     editicon: false,
15546     
15547     page: 0,
15548     hasQuery: false,
15549     append: false,
15550     loadNext: false,
15551     autoFocus : true,
15552     tickable : false,
15553     btnPosition : 'right',
15554     triggerList : true,
15555     showToggleBtn : true,
15556     animate : true,
15557     emptyResultText: 'Empty',
15558     triggerText : 'Select',
15559     emptyTitle : '',
15560     width : false,
15561     
15562     // element that contains real text value.. (when hidden is used..)
15563     
15564     getAutoCreate : function()
15565     {   
15566         var cfg = false;
15567         //render
15568         /*
15569          * Render classic select for iso
15570          */
15571         
15572         if(Roo.isIOS && this.useNativeIOS){
15573             cfg = this.getAutoCreateNativeIOS();
15574             return cfg;
15575         }
15576         
15577         /*
15578          * Touch Devices
15579          */
15580         
15581         if(Roo.isTouch && this.mobileTouchView){
15582             cfg = this.getAutoCreateTouchView();
15583             return cfg;;
15584         }
15585         
15586         /*
15587          *  Normal ComboBox
15588          */
15589         if(!this.tickable){
15590             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15591             return cfg;
15592         }
15593         
15594         /*
15595          *  ComboBox with tickable selections
15596          */
15597              
15598         var align = this.labelAlign || this.parentLabelAlign();
15599         
15600         cfg = {
15601             cls : 'form-group roo-combobox-tickable' //input-group
15602         };
15603         
15604         var btn_text_select = '';
15605         var btn_text_done = '';
15606         var btn_text_cancel = '';
15607         
15608         if (this.btn_text_show) {
15609             btn_text_select = 'Select';
15610             btn_text_done = 'Done';
15611             btn_text_cancel = 'Cancel'; 
15612         }
15613         
15614         var buttons = {
15615             tag : 'div',
15616             cls : 'tickable-buttons',
15617             cn : [
15618                 {
15619                     tag : 'button',
15620                     type : 'button',
15621                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15622                     //html : this.triggerText
15623                     html: btn_text_select
15624                 },
15625                 {
15626                     tag : 'button',
15627                     type : 'button',
15628                     name : 'ok',
15629                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15630                     //html : 'Done'
15631                     html: btn_text_done
15632                 },
15633                 {
15634                     tag : 'button',
15635                     type : 'button',
15636                     name : 'cancel',
15637                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15638                     //html : 'Cancel'
15639                     html: btn_text_cancel
15640                 }
15641             ]
15642         };
15643         
15644         if(this.editable){
15645             buttons.cn.unshift({
15646                 tag: 'input',
15647                 cls: 'roo-select2-search-field-input'
15648             });
15649         }
15650         
15651         var _this = this;
15652         
15653         Roo.each(buttons.cn, function(c){
15654             if (_this.size) {
15655                 c.cls += ' btn-' + _this.size;
15656             }
15657
15658             if (_this.disabled) {
15659                 c.disabled = true;
15660             }
15661         });
15662         
15663         var box = {
15664             tag: 'div',
15665             style : 'display: contents',
15666             cn: [
15667                 {
15668                     tag: 'input',
15669                     type : 'hidden',
15670                     cls: 'form-hidden-field'
15671                 },
15672                 {
15673                     tag: 'ul',
15674                     cls: 'roo-select2-choices',
15675                     cn:[
15676                         {
15677                             tag: 'li',
15678                             cls: 'roo-select2-search-field',
15679                             cn: [
15680                                 buttons
15681                             ]
15682                         }
15683                     ]
15684                 }
15685             ]
15686         };
15687         
15688         var combobox = {
15689             cls: 'roo-select2-container input-group roo-select2-container-multi',
15690             cn: [
15691                 
15692                 box
15693 //                {
15694 //                    tag: 'ul',
15695 //                    cls: 'typeahead typeahead-long dropdown-menu',
15696 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15697 //                }
15698             ]
15699         };
15700         
15701         if(this.hasFeedback && !this.allowBlank){
15702             
15703             var feedback = {
15704                 tag: 'span',
15705                 cls: 'glyphicon form-control-feedback'
15706             };
15707
15708             combobox.cn.push(feedback);
15709         }
15710         
15711         
15712         
15713         var indicator = {
15714             tag : 'i',
15715             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15716             tooltip : 'This field is required'
15717         };
15718         if (Roo.bootstrap.version == 4) {
15719             indicator = {
15720                 tag : 'i',
15721                 style : 'display:none'
15722             };
15723         }
15724         if (align ==='left' && this.fieldLabel.length) {
15725             
15726             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15727             
15728             cfg.cn = [
15729                 indicator,
15730                 {
15731                     tag: 'label',
15732                     'for' :  id,
15733                     cls : 'control-label col-form-label',
15734                     html : this.fieldLabel
15735
15736                 },
15737                 {
15738                     cls : "", 
15739                     cn: [
15740                         combobox
15741                     ]
15742                 }
15743
15744             ];
15745             
15746             var labelCfg = cfg.cn[1];
15747             var contentCfg = cfg.cn[2];
15748             
15749
15750             if(this.indicatorpos == 'right'){
15751                 
15752                 cfg.cn = [
15753                     {
15754                         tag: 'label',
15755                         'for' :  id,
15756                         cls : 'control-label col-form-label',
15757                         cn : [
15758                             {
15759                                 tag : 'span',
15760                                 html : this.fieldLabel
15761                             },
15762                             indicator
15763                         ]
15764                     },
15765                     {
15766                         cls : "",
15767                         cn: [
15768                             combobox
15769                         ]
15770                     }
15771
15772                 ];
15773                 
15774                 
15775                 
15776                 labelCfg = cfg.cn[0];
15777                 contentCfg = cfg.cn[1];
15778             
15779             }
15780             
15781             if(this.labelWidth > 12){
15782                 labelCfg.style = "width: " + this.labelWidth + 'px';
15783             }
15784             if(this.width * 1 > 0){
15785                 contentCfg.style = "width: " + this.width + 'px';
15786             }
15787             if(this.labelWidth < 13 && this.labelmd == 0){
15788                 this.labelmd = this.labelWidth;
15789             }
15790             
15791             if(this.labellg > 0){
15792                 labelCfg.cls += ' col-lg-' + this.labellg;
15793                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15794             }
15795             
15796             if(this.labelmd > 0){
15797                 labelCfg.cls += ' col-md-' + this.labelmd;
15798                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15799             }
15800             
15801             if(this.labelsm > 0){
15802                 labelCfg.cls += ' col-sm-' + this.labelsm;
15803                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15804             }
15805             
15806             if(this.labelxs > 0){
15807                 labelCfg.cls += ' col-xs-' + this.labelxs;
15808                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15809             }
15810                 
15811                 
15812         } else if ( this.fieldLabel.length) {
15813 //                Roo.log(" label");
15814                  cfg.cn = [
15815                    indicator,
15816                     {
15817                         tag: 'label',
15818                         //cls : 'input-group-addon',
15819                         html : this.fieldLabel
15820                     },
15821                     combobox
15822                 ];
15823                 
15824                 if(this.indicatorpos == 'right'){
15825                     cfg.cn = [
15826                         {
15827                             tag: 'label',
15828                             //cls : 'input-group-addon',
15829                             html : this.fieldLabel
15830                         },
15831                         indicator,
15832                         combobox
15833                     ];
15834                     
15835                 }
15836
15837         } else {
15838             
15839 //                Roo.log(" no label && no align");
15840                 cfg = combobox
15841                      
15842                 
15843         }
15844          
15845         var settings=this;
15846         ['xs','sm','md','lg'].map(function(size){
15847             if (settings[size]) {
15848                 cfg.cls += ' col-' + size + '-' + settings[size];
15849             }
15850         });
15851         
15852         return cfg;
15853         
15854     },
15855     
15856     _initEventsCalled : false,
15857     
15858     // private
15859     initEvents: function()
15860     {   
15861         if (this._initEventsCalled) { // as we call render... prevent looping...
15862             return;
15863         }
15864         this._initEventsCalled = true;
15865         
15866         if (!this.store) {
15867             throw "can not find store for combo";
15868         }
15869         
15870         this.indicator = this.indicatorEl();
15871         
15872         this.store = Roo.factory(this.store, Roo.data);
15873         this.store.parent = this;
15874         
15875         // if we are building from html. then this element is so complex, that we can not really
15876         // use the rendered HTML.
15877         // so we have to trash and replace the previous code.
15878         if (Roo.XComponent.build_from_html) {
15879             // remove this element....
15880             var e = this.el.dom, k=0;
15881             while (e ) { e = e.previousSibling;  ++k;}
15882
15883             this.el.remove();
15884             
15885             this.el=false;
15886             this.rendered = false;
15887             
15888             this.render(this.parent().getChildContainer(true), k);
15889         }
15890         
15891         if(Roo.isIOS && this.useNativeIOS){
15892             this.initIOSView();
15893             return;
15894         }
15895         
15896         /*
15897          * Touch Devices
15898          */
15899         
15900         if(Roo.isTouch && this.mobileTouchView){
15901             this.initTouchView();
15902             return;
15903         }
15904         
15905         if(this.tickable){
15906             this.initTickableEvents();
15907             return;
15908         }
15909         
15910         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15911         
15912         if(this.hiddenName){
15913             
15914             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15915             
15916             this.hiddenField.dom.value =
15917                 this.hiddenValue !== undefined ? this.hiddenValue :
15918                 this.value !== undefined ? this.value : '';
15919
15920             // prevent input submission
15921             this.el.dom.removeAttribute('name');
15922             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15923              
15924              
15925         }
15926         //if(Roo.isGecko){
15927         //    this.el.dom.setAttribute('autocomplete', 'off');
15928         //}
15929         
15930         var cls = 'x-combo-list';
15931         
15932         //this.list = new Roo.Layer({
15933         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15934         //});
15935         
15936         var _this = this;
15937         
15938         (function(){
15939             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15940             _this.list.setWidth(lw);
15941         }).defer(100);
15942         
15943         this.list.on('mouseover', this.onViewOver, this);
15944         this.list.on('mousemove', this.onViewMove, this);
15945         this.list.on('scroll', this.onViewScroll, this);
15946         
15947         /*
15948         this.list.swallowEvent('mousewheel');
15949         this.assetHeight = 0;
15950
15951         if(this.title){
15952             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15953             this.assetHeight += this.header.getHeight();
15954         }
15955
15956         this.innerList = this.list.createChild({cls:cls+'-inner'});
15957         this.innerList.on('mouseover', this.onViewOver, this);
15958         this.innerList.on('mousemove', this.onViewMove, this);
15959         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15960         
15961         if(this.allowBlank && !this.pageSize && !this.disableClear){
15962             this.footer = this.list.createChild({cls:cls+'-ft'});
15963             this.pageTb = new Roo.Toolbar(this.footer);
15964            
15965         }
15966         if(this.pageSize){
15967             this.footer = this.list.createChild({cls:cls+'-ft'});
15968             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15969                     {pageSize: this.pageSize});
15970             
15971         }
15972         
15973         if (this.pageTb && this.allowBlank && !this.disableClear) {
15974             var _this = this;
15975             this.pageTb.add(new Roo.Toolbar.Fill(), {
15976                 cls: 'x-btn-icon x-btn-clear',
15977                 text: '&#160;',
15978                 handler: function()
15979                 {
15980                     _this.collapse();
15981                     _this.clearValue();
15982                     _this.onSelect(false, -1);
15983                 }
15984             });
15985         }
15986         if (this.footer) {
15987             this.assetHeight += this.footer.getHeight();
15988         }
15989         */
15990             
15991         if(!this.tpl){
15992             this.tpl = Roo.bootstrap.version == 4 ?
15993                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15994                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15995         }
15996
15997         this.view = new Roo.View(this.list, this.tpl, {
15998             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15999         });
16000         //this.view.wrapEl.setDisplayed(false);
16001         this.view.on('click', this.onViewClick, this);
16002         
16003         
16004         this.store.on('beforeload', this.onBeforeLoad, this);
16005         this.store.on('load', this.onLoad, this);
16006         this.store.on('loadexception', this.onLoadException, this);
16007         /*
16008         if(this.resizable){
16009             this.resizer = new Roo.Resizable(this.list,  {
16010                pinned:true, handles:'se'
16011             });
16012             this.resizer.on('resize', function(r, w, h){
16013                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16014                 this.listWidth = w;
16015                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16016                 this.restrictHeight();
16017             }, this);
16018             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16019         }
16020         */
16021         if(!this.editable){
16022             this.editable = true;
16023             this.setEditable(false);
16024         }
16025         
16026         /*
16027         
16028         if (typeof(this.events.add.listeners) != 'undefined') {
16029             
16030             this.addicon = this.wrap.createChild(
16031                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16032        
16033             this.addicon.on('click', function(e) {
16034                 this.fireEvent('add', this);
16035             }, this);
16036         }
16037         if (typeof(this.events.edit.listeners) != 'undefined') {
16038             
16039             this.editicon = this.wrap.createChild(
16040                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16041             if (this.addicon) {
16042                 this.editicon.setStyle('margin-left', '40px');
16043             }
16044             this.editicon.on('click', function(e) {
16045                 
16046                 // we fire even  if inothing is selected..
16047                 this.fireEvent('edit', this, this.lastData );
16048                 
16049             }, this);
16050         }
16051         */
16052         
16053         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16054             "up" : function(e){
16055                 this.inKeyMode = true;
16056                 this.selectPrev();
16057             },
16058
16059             "down" : function(e){
16060                 if(!this.isExpanded()){
16061                     this.onTriggerClick();
16062                 }else{
16063                     this.inKeyMode = true;
16064                     this.selectNext();
16065                 }
16066             },
16067
16068             "enter" : function(e){
16069 //                this.onViewClick();
16070                 //return true;
16071                 this.collapse();
16072                 
16073                 if(this.fireEvent("specialkey", this, e)){
16074                     this.onViewClick(false);
16075                 }
16076                 
16077                 return true;
16078             },
16079
16080             "esc" : function(e){
16081                 this.collapse();
16082             },
16083
16084             "tab" : function(e){
16085                 this.collapse();
16086                 
16087                 if(this.fireEvent("specialkey", this, e)){
16088                     this.onViewClick(false);
16089                 }
16090                 
16091                 return true;
16092             },
16093
16094             scope : this,
16095
16096             doRelay : function(foo, bar, hname){
16097                 if(hname == 'down' || this.scope.isExpanded()){
16098                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16099                 }
16100                 return true;
16101             },
16102
16103             forceKeyDown: true
16104         });
16105         
16106         
16107         this.queryDelay = Math.max(this.queryDelay || 10,
16108                 this.mode == 'local' ? 10 : 250);
16109         
16110         
16111         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16112         
16113         if(this.typeAhead){
16114             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16115         }
16116         if(this.editable !== false){
16117             this.inputEl().on("keyup", this.onKeyUp, this);
16118         }
16119         if(this.forceSelection){
16120             this.inputEl().on('blur', this.doForce, this);
16121         }
16122         
16123         if(this.multiple){
16124             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16125             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16126         }
16127     },
16128     
16129     initTickableEvents: function()
16130     {   
16131         this.createList();
16132         
16133         if(this.hiddenName){
16134             
16135             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16136             
16137             this.hiddenField.dom.value =
16138                 this.hiddenValue !== undefined ? this.hiddenValue :
16139                 this.value !== undefined ? this.value : '';
16140
16141             // prevent input submission
16142             this.el.dom.removeAttribute('name');
16143             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16144              
16145              
16146         }
16147         
16148 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16149         
16150         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16151         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16152         if(this.triggerList){
16153             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16154         }
16155          
16156         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16157         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16158         
16159         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16160         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16161         
16162         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16163         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16164         
16165         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16166         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16167         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16168         
16169         this.okBtn.hide();
16170         this.cancelBtn.hide();
16171         
16172         var _this = this;
16173         
16174         (function(){
16175             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16176             _this.list.setWidth(lw);
16177         }).defer(100);
16178         
16179         this.list.on('mouseover', this.onViewOver, this);
16180         this.list.on('mousemove', this.onViewMove, this);
16181         
16182         this.list.on('scroll', this.onViewScroll, this);
16183         
16184         if(!this.tpl){
16185             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16186                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16187         }
16188
16189         this.view = new Roo.View(this.list, this.tpl, {
16190             singleSelect:true,
16191             tickable:true,
16192             parent:this,
16193             store: this.store,
16194             selectedClass: this.selectedClass
16195         });
16196         
16197         //this.view.wrapEl.setDisplayed(false);
16198         this.view.on('click', this.onViewClick, this);
16199         
16200         
16201         
16202         this.store.on('beforeload', this.onBeforeLoad, this);
16203         this.store.on('load', this.onLoad, this);
16204         this.store.on('loadexception', this.onLoadException, this);
16205         
16206         if(this.editable){
16207             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16208                 "up" : function(e){
16209                     this.inKeyMode = true;
16210                     this.selectPrev();
16211                 },
16212
16213                 "down" : function(e){
16214                     this.inKeyMode = true;
16215                     this.selectNext();
16216                 },
16217
16218                 "enter" : function(e){
16219                     if(this.fireEvent("specialkey", this, e)){
16220                         this.onViewClick(false);
16221                     }
16222                     
16223                     return true;
16224                 },
16225
16226                 "esc" : function(e){
16227                     this.onTickableFooterButtonClick(e, false, false);
16228                 },
16229
16230                 "tab" : function(e){
16231                     this.fireEvent("specialkey", this, e);
16232                     
16233                     this.onTickableFooterButtonClick(e, false, false);
16234                     
16235                     return true;
16236                 },
16237
16238                 scope : this,
16239
16240                 doRelay : function(e, fn, key){
16241                     if(this.scope.isExpanded()){
16242                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16243                     }
16244                     return true;
16245                 },
16246
16247                 forceKeyDown: true
16248             });
16249         }
16250         
16251         this.queryDelay = Math.max(this.queryDelay || 10,
16252                 this.mode == 'local' ? 10 : 250);
16253         
16254         
16255         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16256         
16257         if(this.typeAhead){
16258             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16259         }
16260         
16261         if(this.editable !== false){
16262             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16263         }
16264         
16265         this.indicator = this.indicatorEl();
16266         
16267         if(this.indicator){
16268             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16269             this.indicator.hide();
16270         }
16271         
16272     },
16273
16274     onDestroy : function(){
16275         if(this.view){
16276             this.view.setStore(null);
16277             this.view.el.removeAllListeners();
16278             this.view.el.remove();
16279             this.view.purgeListeners();
16280         }
16281         if(this.list){
16282             this.list.dom.innerHTML  = '';
16283         }
16284         
16285         if(this.store){
16286             this.store.un('beforeload', this.onBeforeLoad, this);
16287             this.store.un('load', this.onLoad, this);
16288             this.store.un('loadexception', this.onLoadException, this);
16289         }
16290         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16291     },
16292
16293     // private
16294     fireKey : function(e){
16295         if(e.isNavKeyPress() && !this.list.isVisible()){
16296             this.fireEvent("specialkey", this, e);
16297         }
16298     },
16299
16300     // private
16301     onResize: function(w, h)
16302     {
16303         
16304         
16305 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16306 //        
16307 //        if(typeof w != 'number'){
16308 //            // we do not handle it!?!?
16309 //            return;
16310 //        }
16311 //        var tw = this.trigger.getWidth();
16312 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16313 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16314 //        var x = w - tw;
16315 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16316 //            
16317 //        //this.trigger.setStyle('left', x+'px');
16318 //        
16319 //        if(this.list && this.listWidth === undefined){
16320 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16321 //            this.list.setWidth(lw);
16322 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16323 //        }
16324         
16325     
16326         
16327     },
16328
16329     /**
16330      * Allow or prevent the user from directly editing the field text.  If false is passed,
16331      * the user will only be able to select from the items defined in the dropdown list.  This method
16332      * is the runtime equivalent of setting the 'editable' config option at config time.
16333      * @param {Boolean} value True to allow the user to directly edit the field text
16334      */
16335     setEditable : function(value){
16336         if(value == this.editable){
16337             return;
16338         }
16339         this.editable = value;
16340         if(!value){
16341             this.inputEl().dom.setAttribute('readOnly', true);
16342             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16343             this.inputEl().addClass('x-combo-noedit');
16344         }else{
16345             this.inputEl().dom.setAttribute('readOnly', false);
16346             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16347             this.inputEl().removeClass('x-combo-noedit');
16348         }
16349     },
16350
16351     // private
16352     
16353     onBeforeLoad : function(combo,opts){
16354         if(!this.hasFocus){
16355             return;
16356         }
16357          if (!opts.add) {
16358             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16359          }
16360         this.restrictHeight();
16361         this.selectedIndex = -1;
16362     },
16363
16364     // private
16365     onLoad : function(){
16366         
16367         this.hasQuery = false;
16368         
16369         if(!this.hasFocus){
16370             return;
16371         }
16372         
16373         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16374             this.loading.hide();
16375         }
16376         
16377         if(this.store.getCount() > 0){
16378             
16379             this.expand();
16380             this.restrictHeight();
16381             if(this.lastQuery == this.allQuery){
16382                 if(this.editable && !this.tickable){
16383                     this.inputEl().dom.select();
16384                 }
16385                 
16386                 if(
16387                     !this.selectByValue(this.value, true) &&
16388                     this.autoFocus && 
16389                     (
16390                         !this.store.lastOptions ||
16391                         typeof(this.store.lastOptions.add) == 'undefined' || 
16392                         this.store.lastOptions.add != true
16393                     )
16394                 ){
16395                     this.select(0, true);
16396                 }
16397             }else{
16398                 if(this.autoFocus){
16399                     this.selectNext();
16400                 }
16401                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16402                     this.taTask.delay(this.typeAheadDelay);
16403                 }
16404             }
16405         }else{
16406             this.onEmptyResults();
16407         }
16408         
16409         //this.el.focus();
16410     },
16411     // private
16412     onLoadException : function()
16413     {
16414         this.hasQuery = false;
16415         
16416         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16417             this.loading.hide();
16418         }
16419         
16420         if(this.tickable && this.editable){
16421             return;
16422         }
16423         
16424         this.collapse();
16425         // only causes errors at present
16426         //Roo.log(this.store.reader.jsonData);
16427         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16428             // fixme
16429             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16430         //}
16431         
16432         
16433     },
16434     // private
16435     onTypeAhead : function(){
16436         if(this.store.getCount() > 0){
16437             var r = this.store.getAt(0);
16438             var newValue = r.data[this.displayField];
16439             var len = newValue.length;
16440             var selStart = this.getRawValue().length;
16441             
16442             if(selStart != len){
16443                 this.setRawValue(newValue);
16444                 this.selectText(selStart, newValue.length);
16445             }
16446         }
16447     },
16448
16449     // private
16450     onSelect : function(record, index){
16451         
16452         if(this.fireEvent('beforeselect', this, record, index) !== false){
16453         
16454             this.setFromData(index > -1 ? record.data : false);
16455             
16456             this.collapse();
16457             this.fireEvent('select', this, record, index);
16458         }
16459     },
16460
16461     /**
16462      * Returns the currently selected field value or empty string if no value is set.
16463      * @return {String} value The selected value
16464      */
16465     getValue : function()
16466     {
16467         if(Roo.isIOS && this.useNativeIOS){
16468             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16469         }
16470         
16471         if(this.multiple){
16472             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16473         }
16474         
16475         if(this.valueField){
16476             return typeof this.value != 'undefined' ? this.value : '';
16477         }else{
16478             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16479         }
16480     },
16481     
16482     getRawValue : function()
16483     {
16484         if(Roo.isIOS && this.useNativeIOS){
16485             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16486         }
16487         
16488         var v = this.inputEl().getValue();
16489         
16490         return v;
16491     },
16492
16493     /**
16494      * Clears any text/value currently set in the field
16495      */
16496     clearValue : function(){
16497         
16498         if(this.hiddenField){
16499             this.hiddenField.dom.value = '';
16500         }
16501         this.value = '';
16502         this.setRawValue('');
16503         this.lastSelectionText = '';
16504         this.lastData = false;
16505         
16506         var close = this.closeTriggerEl();
16507         
16508         if(close){
16509             close.hide();
16510         }
16511         
16512         this.validate();
16513         
16514     },
16515
16516     /**
16517      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16518      * will be displayed in the field.  If the value does not match the data value of an existing item,
16519      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16520      * Otherwise the field will be blank (although the value will still be set).
16521      * @param {String} value The value to match
16522      */
16523     setValue : function(v)
16524     {
16525         if(Roo.isIOS && this.useNativeIOS){
16526             this.setIOSValue(v);
16527             return;
16528         }
16529         
16530         if(this.multiple){
16531             this.syncValue();
16532             return;
16533         }
16534         
16535         var text = v;
16536         if(this.valueField){
16537             var r = this.findRecord(this.valueField, v);
16538             if(r){
16539                 text = r.data[this.displayField];
16540             }else if(this.valueNotFoundText !== undefined){
16541                 text = this.valueNotFoundText;
16542             }
16543         }
16544         this.lastSelectionText = text;
16545         if(this.hiddenField){
16546             this.hiddenField.dom.value = v;
16547         }
16548         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16549         this.value = v;
16550         
16551         var close = this.closeTriggerEl();
16552         
16553         if(close){
16554             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16555         }
16556         
16557         this.validate();
16558     },
16559     /**
16560      * @property {Object} the last set data for the element
16561      */
16562     
16563     lastData : false,
16564     /**
16565      * Sets the value of the field based on a object which is related to the record format for the store.
16566      * @param {Object} value the value to set as. or false on reset?
16567      */
16568     setFromData : function(o){
16569         
16570         if(this.multiple){
16571             this.addItem(o);
16572             return;
16573         }
16574             
16575         var dv = ''; // display value
16576         var vv = ''; // value value..
16577         this.lastData = o;
16578         if (this.displayField) {
16579             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16580         } else {
16581             // this is an error condition!!!
16582             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16583         }
16584         
16585         if(this.valueField){
16586             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16587         }
16588         
16589         var close = this.closeTriggerEl();
16590         
16591         if(close){
16592             if(dv.length || vv * 1 > 0){
16593                 close.show() ;
16594                 this.blockFocus=true;
16595             } else {
16596                 close.hide();
16597             }             
16598         }
16599         
16600         if(this.hiddenField){
16601             this.hiddenField.dom.value = vv;
16602             
16603             this.lastSelectionText = dv;
16604             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16605             this.value = vv;
16606             return;
16607         }
16608         // no hidden field.. - we store the value in 'value', but still display
16609         // display field!!!!
16610         this.lastSelectionText = dv;
16611         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16612         this.value = vv;
16613         
16614         
16615         
16616     },
16617     // private
16618     reset : function(){
16619         // overridden so that last data is reset..
16620         
16621         if(this.multiple){
16622             this.clearItem();
16623             return;
16624         }
16625         
16626         this.setValue(this.originalValue);
16627         //this.clearInvalid();
16628         this.lastData = false;
16629         if (this.view) {
16630             this.view.clearSelections();
16631         }
16632         
16633         this.validate();
16634     },
16635     // private
16636     findRecord : function(prop, value){
16637         var record;
16638         if(this.store.getCount() > 0){
16639             this.store.each(function(r){
16640                 if(r.data[prop] == value){
16641                     record = r;
16642                     return false;
16643                 }
16644                 return true;
16645             });
16646         }
16647         return record;
16648     },
16649     
16650     getName: function()
16651     {
16652         // returns hidden if it's set..
16653         if (!this.rendered) {return ''};
16654         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16655         
16656     },
16657     // private
16658     onViewMove : function(e, t){
16659         this.inKeyMode = false;
16660     },
16661
16662     // private
16663     onViewOver : function(e, t){
16664         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16665             return;
16666         }
16667         var item = this.view.findItemFromChild(t);
16668         
16669         if(item){
16670             var index = this.view.indexOf(item);
16671             this.select(index, false);
16672         }
16673     },
16674
16675     // private
16676     onViewClick : function(view, doFocus, el, e)
16677     {
16678         var index = this.view.getSelectedIndexes()[0];
16679         
16680         var r = this.store.getAt(index);
16681         
16682         if(this.tickable){
16683             
16684             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16685                 return;
16686             }
16687             
16688             var rm = false;
16689             var _this = this;
16690             
16691             Roo.each(this.tickItems, function(v,k){
16692                 
16693                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16694                     Roo.log(v);
16695                     _this.tickItems.splice(k, 1);
16696                     
16697                     if(typeof(e) == 'undefined' && view == false){
16698                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16699                     }
16700                     
16701                     rm = true;
16702                     return;
16703                 }
16704             });
16705             
16706             if(rm){
16707                 return;
16708             }
16709             
16710             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16711                 this.tickItems.push(r.data);
16712             }
16713             
16714             if(typeof(e) == 'undefined' && view == false){
16715                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16716             }
16717                     
16718             return;
16719         }
16720         
16721         if(r){
16722             this.onSelect(r, index);
16723         }
16724         if(doFocus !== false && !this.blockFocus){
16725             this.inputEl().focus();
16726         }
16727     },
16728
16729     // private
16730     restrictHeight : function(){
16731         //this.innerList.dom.style.height = '';
16732         //var inner = this.innerList.dom;
16733         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16734         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16735         //this.list.beginUpdate();
16736         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16737         this.list.alignTo(this.inputEl(), this.listAlign);
16738         this.list.alignTo(this.inputEl(), this.listAlign);
16739         //this.list.endUpdate();
16740     },
16741
16742     // private
16743     onEmptyResults : function(){
16744         
16745         if(this.tickable && this.editable){
16746             this.hasFocus = false;
16747             this.restrictHeight();
16748             return;
16749         }
16750         
16751         this.collapse();
16752     },
16753
16754     /**
16755      * Returns true if the dropdown list is expanded, else false.
16756      */
16757     isExpanded : function(){
16758         return this.list.isVisible();
16759     },
16760
16761     /**
16762      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16763      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16764      * @param {String} value The data value of the item to select
16765      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16766      * selected item if it is not currently in view (defaults to true)
16767      * @return {Boolean} True if the value matched an item in the list, else false
16768      */
16769     selectByValue : function(v, scrollIntoView){
16770         if(v !== undefined && v !== null){
16771             var r = this.findRecord(this.valueField || this.displayField, v);
16772             if(r){
16773                 this.select(this.store.indexOf(r), scrollIntoView);
16774                 return true;
16775             }
16776         }
16777         return false;
16778     },
16779
16780     /**
16781      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16782      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16783      * @param {Number} index The zero-based index of the list item to select
16784      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16785      * selected item if it is not currently in view (defaults to true)
16786      */
16787     select : function(index, scrollIntoView){
16788         this.selectedIndex = index;
16789         this.view.select(index);
16790         if(scrollIntoView !== false){
16791             var el = this.view.getNode(index);
16792             /*
16793              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16794              */
16795             if(el){
16796                 this.list.scrollChildIntoView(el, false);
16797             }
16798         }
16799     },
16800
16801     // private
16802     selectNext : function(){
16803         var ct = this.store.getCount();
16804         if(ct > 0){
16805             if(this.selectedIndex == -1){
16806                 this.select(0);
16807             }else if(this.selectedIndex < ct-1){
16808                 this.select(this.selectedIndex+1);
16809             }
16810         }
16811     },
16812
16813     // private
16814     selectPrev : function(){
16815         var ct = this.store.getCount();
16816         if(ct > 0){
16817             if(this.selectedIndex == -1){
16818                 this.select(0);
16819             }else if(this.selectedIndex != 0){
16820                 this.select(this.selectedIndex-1);
16821             }
16822         }
16823     },
16824
16825     // private
16826     onKeyUp : function(e){
16827         if(this.editable !== false && !e.isSpecialKey()){
16828             this.lastKey = e.getKey();
16829             this.dqTask.delay(this.queryDelay);
16830         }
16831     },
16832
16833     // private
16834     validateBlur : function(){
16835         return !this.list || !this.list.isVisible();   
16836     },
16837
16838     // private
16839     initQuery : function(){
16840         
16841         var v = this.getRawValue();
16842         
16843         if(this.tickable && this.editable){
16844             v = this.tickableInputEl().getValue();
16845         }
16846         
16847         this.doQuery(v);
16848     },
16849
16850     // private
16851     doForce : function(){
16852         if(this.inputEl().dom.value.length > 0){
16853             this.inputEl().dom.value =
16854                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16855              
16856         }
16857     },
16858
16859     /**
16860      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16861      * query allowing the query action to be canceled if needed.
16862      * @param {String} query The SQL query to execute
16863      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16864      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16865      * saved in the current store (defaults to false)
16866      */
16867     doQuery : function(q, forceAll){
16868         
16869         if(q === undefined || q === null){
16870             q = '';
16871         }
16872         var qe = {
16873             query: q,
16874             forceAll: forceAll,
16875             combo: this,
16876             cancel:false
16877         };
16878         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16879             return false;
16880         }
16881         q = qe.query;
16882         
16883         forceAll = qe.forceAll;
16884         if(forceAll === true || (q.length >= this.minChars)){
16885             
16886             this.hasQuery = true;
16887             
16888             if(this.lastQuery != q || this.alwaysQuery){
16889                 this.lastQuery = q;
16890                 if(this.mode == 'local'){
16891                     this.selectedIndex = -1;
16892                     if(forceAll){
16893                         this.store.clearFilter();
16894                     }else{
16895                         
16896                         if(this.specialFilter){
16897                             this.fireEvent('specialfilter', this);
16898                             this.onLoad();
16899                             return;
16900                         }
16901                         
16902                         this.store.filter(this.displayField, q);
16903                     }
16904                     
16905                     this.store.fireEvent("datachanged", this.store);
16906                     
16907                     this.onLoad();
16908                     
16909                     
16910                 }else{
16911                     
16912                     this.store.baseParams[this.queryParam] = q;
16913                     
16914                     var options = {params : this.getParams(q)};
16915                     
16916                     if(this.loadNext){
16917                         options.add = true;
16918                         options.params.start = this.page * this.pageSize;
16919                     }
16920                     
16921                     this.store.load(options);
16922                     
16923                     /*
16924                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16925                      *  we should expand the list on onLoad
16926                      *  so command out it
16927                      */
16928 //                    this.expand();
16929                 }
16930             }else{
16931                 this.selectedIndex = -1;
16932                 this.onLoad();   
16933             }
16934         }
16935         
16936         this.loadNext = false;
16937     },
16938     
16939     // private
16940     getParams : function(q){
16941         var p = {};
16942         //p[this.queryParam] = q;
16943         
16944         if(this.pageSize){
16945             p.start = 0;
16946             p.limit = this.pageSize;
16947         }
16948         return p;
16949     },
16950
16951     /**
16952      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16953      */
16954     collapse : function(){
16955         if(!this.isExpanded()){
16956             return;
16957         }
16958         
16959         this.list.hide();
16960         
16961         this.hasFocus = false;
16962         
16963         if(this.tickable){
16964             this.okBtn.hide();
16965             this.cancelBtn.hide();
16966             this.trigger.show();
16967             
16968             if(this.editable){
16969                 this.tickableInputEl().dom.value = '';
16970                 this.tickableInputEl().blur();
16971             }
16972             
16973         }
16974         
16975         Roo.get(document).un('mousedown', this.collapseIf, this);
16976         Roo.get(document).un('mousewheel', this.collapseIf, this);
16977         if (!this.editable) {
16978             Roo.get(document).un('keydown', this.listKeyPress, this);
16979         }
16980         this.fireEvent('collapse', this);
16981         
16982         this.validate();
16983     },
16984
16985     // private
16986     collapseIf : function(e){
16987         var in_combo  = e.within(this.el);
16988         var in_list =  e.within(this.list);
16989         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16990         
16991         if (in_combo || in_list || is_list) {
16992             //e.stopPropagation();
16993             return;
16994         }
16995         
16996         if(this.tickable){
16997             this.onTickableFooterButtonClick(e, false, false);
16998         }
16999
17000         this.collapse();
17001         
17002     },
17003
17004     /**
17005      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17006      */
17007     expand : function(){
17008        
17009         if(this.isExpanded() || !this.hasFocus){
17010             return;
17011         }
17012         
17013         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17014         this.list.setWidth(lw);
17015         
17016         Roo.log('expand');
17017         
17018         this.list.show();
17019         
17020         this.restrictHeight();
17021         
17022         if(this.tickable){
17023             
17024             this.tickItems = Roo.apply([], this.item);
17025             
17026             this.okBtn.show();
17027             this.cancelBtn.show();
17028             this.trigger.hide();
17029             
17030             if(this.editable){
17031                 this.tickableInputEl().focus();
17032             }
17033             
17034         }
17035         
17036         Roo.get(document).on('mousedown', this.collapseIf, this);
17037         Roo.get(document).on('mousewheel', this.collapseIf, this);
17038         if (!this.editable) {
17039             Roo.get(document).on('keydown', this.listKeyPress, this);
17040         }
17041         
17042         this.fireEvent('expand', this);
17043     },
17044
17045     // private
17046     // Implements the default empty TriggerField.onTriggerClick function
17047     onTriggerClick : function(e)
17048     {
17049         Roo.log('trigger click');
17050         
17051         if(this.disabled || !this.triggerList){
17052             return;
17053         }
17054         
17055         this.page = 0;
17056         this.loadNext = false;
17057         
17058         if(this.isExpanded()){
17059             this.collapse();
17060             if (!this.blockFocus) {
17061                 this.inputEl().focus();
17062             }
17063             
17064         }else {
17065             this.hasFocus = true;
17066             if(this.triggerAction == 'all') {
17067                 this.doQuery(this.allQuery, true);
17068             } else {
17069                 this.doQuery(this.getRawValue());
17070             }
17071             if (!this.blockFocus) {
17072                 this.inputEl().focus();
17073             }
17074         }
17075     },
17076     
17077     onTickableTriggerClick : function(e)
17078     {
17079         if(this.disabled){
17080             return;
17081         }
17082         
17083         this.page = 0;
17084         this.loadNext = false;
17085         this.hasFocus = true;
17086         
17087         if(this.triggerAction == 'all') {
17088             this.doQuery(this.allQuery, true);
17089         } else {
17090             this.doQuery(this.getRawValue());
17091         }
17092     },
17093     
17094     onSearchFieldClick : function(e)
17095     {
17096         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17097             this.onTickableFooterButtonClick(e, false, false);
17098             return;
17099         }
17100         
17101         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17102             return;
17103         }
17104         
17105         this.page = 0;
17106         this.loadNext = false;
17107         this.hasFocus = true;
17108         
17109         if(this.triggerAction == 'all') {
17110             this.doQuery(this.allQuery, true);
17111         } else {
17112             this.doQuery(this.getRawValue());
17113         }
17114     },
17115     
17116     listKeyPress : function(e)
17117     {
17118         //Roo.log('listkeypress');
17119         // scroll to first matching element based on key pres..
17120         if (e.isSpecialKey()) {
17121             return false;
17122         }
17123         var k = String.fromCharCode(e.getKey()).toUpperCase();
17124         //Roo.log(k);
17125         var match  = false;
17126         var csel = this.view.getSelectedNodes();
17127         var cselitem = false;
17128         if (csel.length) {
17129             var ix = this.view.indexOf(csel[0]);
17130             cselitem  = this.store.getAt(ix);
17131             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17132                 cselitem = false;
17133             }
17134             
17135         }
17136         
17137         this.store.each(function(v) { 
17138             if (cselitem) {
17139                 // start at existing selection.
17140                 if (cselitem.id == v.id) {
17141                     cselitem = false;
17142                 }
17143                 return true;
17144             }
17145                 
17146             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17147                 match = this.store.indexOf(v);
17148                 return false;
17149             }
17150             return true;
17151         }, this);
17152         
17153         if (match === false) {
17154             return true; // no more action?
17155         }
17156         // scroll to?
17157         this.view.select(match);
17158         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17159         sn.scrollIntoView(sn.dom.parentNode, false);
17160     },
17161     
17162     onViewScroll : function(e, t){
17163         
17164         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){
17165             return;
17166         }
17167         
17168         this.hasQuery = true;
17169         
17170         this.loading = this.list.select('.loading', true).first();
17171         
17172         if(this.loading === null){
17173             this.list.createChild({
17174                 tag: 'div',
17175                 cls: 'loading roo-select2-more-results roo-select2-active',
17176                 html: 'Loading more results...'
17177             });
17178             
17179             this.loading = this.list.select('.loading', true).first();
17180             
17181             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17182             
17183             this.loading.hide();
17184         }
17185         
17186         this.loading.show();
17187         
17188         var _combo = this;
17189         
17190         this.page++;
17191         this.loadNext = true;
17192         
17193         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17194         
17195         return;
17196     },
17197     
17198     addItem : function(o)
17199     {   
17200         var dv = ''; // display value
17201         
17202         if (this.displayField) {
17203             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17204         } else {
17205             // this is an error condition!!!
17206             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17207         }
17208         
17209         if(!dv.length){
17210             return;
17211         }
17212         
17213         var choice = this.choices.createChild({
17214             tag: 'li',
17215             cls: 'roo-select2-search-choice',
17216             cn: [
17217                 {
17218                     tag: 'div',
17219                     html: dv
17220                 },
17221                 {
17222                     tag: 'a',
17223                     href: '#',
17224                     cls: 'roo-select2-search-choice-close fa fa-times',
17225                     tabindex: '-1'
17226                 }
17227             ]
17228             
17229         }, this.searchField);
17230         
17231         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17232         
17233         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17234         
17235         this.item.push(o);
17236         
17237         this.lastData = o;
17238         
17239         this.syncValue();
17240         
17241         this.inputEl().dom.value = '';
17242         
17243         this.validate();
17244     },
17245     
17246     onRemoveItem : function(e, _self, o)
17247     {
17248         e.preventDefault();
17249         
17250         this.lastItem = Roo.apply([], this.item);
17251         
17252         var index = this.item.indexOf(o.data) * 1;
17253         
17254         if( index < 0){
17255             Roo.log('not this item?!');
17256             return;
17257         }
17258         
17259         this.item.splice(index, 1);
17260         o.item.remove();
17261         
17262         this.syncValue();
17263         
17264         this.fireEvent('remove', this, e);
17265         
17266         this.validate();
17267         
17268     },
17269     
17270     syncValue : function()
17271     {
17272         if(!this.item.length){
17273             this.clearValue();
17274             return;
17275         }
17276             
17277         var value = [];
17278         var _this = this;
17279         Roo.each(this.item, function(i){
17280             if(_this.valueField){
17281                 value.push(i[_this.valueField]);
17282                 return;
17283             }
17284
17285             value.push(i);
17286         });
17287
17288         this.value = value.join(',');
17289
17290         if(this.hiddenField){
17291             this.hiddenField.dom.value = this.value;
17292         }
17293         
17294         this.store.fireEvent("datachanged", this.store);
17295         
17296         this.validate();
17297     },
17298     
17299     clearItem : function()
17300     {
17301         if(!this.multiple){
17302             return;
17303         }
17304         
17305         this.item = [];
17306         
17307         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17308            c.remove();
17309         });
17310         
17311         this.syncValue();
17312         
17313         this.validate();
17314         
17315         if(this.tickable && !Roo.isTouch){
17316             this.view.refresh();
17317         }
17318     },
17319     
17320     inputEl: function ()
17321     {
17322         if(Roo.isIOS && this.useNativeIOS){
17323             return this.el.select('select.roo-ios-select', true).first();
17324         }
17325         
17326         if(Roo.isTouch && this.mobileTouchView){
17327             return this.el.select('input.form-control',true).first();
17328         }
17329         
17330         if(this.tickable){
17331             return this.searchField;
17332         }
17333         
17334         return this.el.select('input.form-control',true).first();
17335     },
17336     
17337     onTickableFooterButtonClick : function(e, btn, el)
17338     {
17339         e.preventDefault();
17340         
17341         this.lastItem = Roo.apply([], this.item);
17342         
17343         if(btn && btn.name == 'cancel'){
17344             this.tickItems = Roo.apply([], this.item);
17345             this.collapse();
17346             return;
17347         }
17348         
17349         this.clearItem();
17350         
17351         var _this = this;
17352         
17353         Roo.each(this.tickItems, function(o){
17354             _this.addItem(o);
17355         });
17356         
17357         this.collapse();
17358         
17359     },
17360     
17361     validate : function()
17362     {
17363         if(this.getVisibilityEl().hasClass('hidden')){
17364             return true;
17365         }
17366         
17367         var v = this.getRawValue();
17368         
17369         if(this.multiple){
17370             v = this.getValue();
17371         }
17372         
17373         if(this.disabled || this.allowBlank || v.length){
17374             this.markValid();
17375             return true;
17376         }
17377         
17378         this.markInvalid();
17379         return false;
17380     },
17381     
17382     tickableInputEl : function()
17383     {
17384         if(!this.tickable || !this.editable){
17385             return this.inputEl();
17386         }
17387         
17388         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17389     },
17390     
17391     
17392     getAutoCreateTouchView : function()
17393     {
17394         var id = Roo.id();
17395         
17396         var cfg = {
17397             cls: 'form-group' //input-group
17398         };
17399         
17400         var input =  {
17401             tag: 'input',
17402             id : id,
17403             type : this.inputType,
17404             cls : 'form-control x-combo-noedit',
17405             autocomplete: 'new-password',
17406             placeholder : this.placeholder || '',
17407             readonly : true
17408         };
17409         
17410         if (this.name) {
17411             input.name = this.name;
17412         }
17413         
17414         if (this.size) {
17415             input.cls += ' input-' + this.size;
17416         }
17417         
17418         if (this.disabled) {
17419             input.disabled = true;
17420         }
17421         
17422         var inputblock = {
17423             cls : 'roo-combobox-wrap',
17424             cn : [
17425                 input
17426             ]
17427         };
17428         
17429         if(this.before){
17430             inputblock.cls += ' input-group';
17431             
17432             inputblock.cn.unshift({
17433                 tag :'span',
17434                 cls : 'input-group-addon input-group-prepend input-group-text',
17435                 html : this.before
17436             });
17437         }
17438         
17439         if(this.removable && !this.multiple){
17440             inputblock.cls += ' roo-removable';
17441             
17442             inputblock.cn.push({
17443                 tag: 'button',
17444                 html : 'x',
17445                 cls : 'roo-combo-removable-btn close'
17446             });
17447         }
17448
17449         if(this.hasFeedback && !this.allowBlank){
17450             
17451             inputblock.cls += ' has-feedback';
17452             
17453             inputblock.cn.push({
17454                 tag: 'span',
17455                 cls: 'glyphicon form-control-feedback'
17456             });
17457             
17458         }
17459         
17460         if (this.after) {
17461             
17462             inputblock.cls += (this.before) ? '' : ' input-group';
17463             
17464             inputblock.cn.push({
17465                 tag :'span',
17466                 cls : 'input-group-addon input-group-append input-group-text',
17467                 html : this.after
17468             });
17469         }
17470
17471         
17472         var ibwrap = inputblock;
17473         
17474         if(this.multiple){
17475             ibwrap = {
17476                 tag: 'ul',
17477                 cls: 'roo-select2-choices',
17478                 cn:[
17479                     {
17480                         tag: 'li',
17481                         cls: 'roo-select2-search-field',
17482                         cn: [
17483
17484                             inputblock
17485                         ]
17486                     }
17487                 ]
17488             };
17489         
17490             
17491         }
17492         
17493         var combobox = {
17494             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17495             cn: [
17496                 {
17497                     tag: 'input',
17498                     type : 'hidden',
17499                     cls: 'form-hidden-field'
17500                 },
17501                 ibwrap
17502             ]
17503         };
17504         
17505         if(!this.multiple && this.showToggleBtn){
17506             
17507             var caret = {
17508                 cls: 'caret'
17509             };
17510             
17511             if (this.caret != false) {
17512                 caret = {
17513                      tag: 'i',
17514                      cls: 'fa fa-' + this.caret
17515                 };
17516                 
17517             }
17518             
17519             combobox.cn.push({
17520                 tag :'span',
17521                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17522                 cn : [
17523                     Roo.bootstrap.version == 3 ? caret : '',
17524                     {
17525                         tag: 'span',
17526                         cls: 'combobox-clear',
17527                         cn  : [
17528                             {
17529                                 tag : 'i',
17530                                 cls: 'icon-remove'
17531                             }
17532                         ]
17533                     }
17534                 ]
17535
17536             })
17537         }
17538         
17539         if(this.multiple){
17540             combobox.cls += ' roo-select2-container-multi';
17541         }
17542         
17543         var align = this.labelAlign || this.parentLabelAlign();
17544         
17545         if (align ==='left' && this.fieldLabel.length) {
17546
17547             cfg.cn = [
17548                 {
17549                    tag : 'i',
17550                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17551                    tooltip : 'This field is required'
17552                 },
17553                 {
17554                     tag: 'label',
17555                     cls : 'control-label col-form-label',
17556                     html : this.fieldLabel
17557
17558                 },
17559                 {
17560                     cls : 'roo-combobox-wrap ', 
17561                     cn: [
17562                         combobox
17563                     ]
17564                 }
17565             ];
17566             
17567             var labelCfg = cfg.cn[1];
17568             var contentCfg = cfg.cn[2];
17569             
17570
17571             if(this.indicatorpos == 'right'){
17572                 cfg.cn = [
17573                     {
17574                         tag: 'label',
17575                         'for' :  id,
17576                         cls : 'control-label col-form-label',
17577                         cn : [
17578                             {
17579                                 tag : 'span',
17580                                 html : this.fieldLabel
17581                             },
17582                             {
17583                                 tag : 'i',
17584                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17585                                 tooltip : 'This field is required'
17586                             }
17587                         ]
17588                     },
17589                     {
17590                         cls : "roo-combobox-wrap ",
17591                         cn: [
17592                             combobox
17593                         ]
17594                     }
17595
17596                 ];
17597                 
17598                 labelCfg = cfg.cn[0];
17599                 contentCfg = cfg.cn[1];
17600             }
17601             
17602            
17603             
17604             if(this.labelWidth > 12){
17605                 labelCfg.style = "width: " + this.labelWidth + 'px';
17606             }
17607            
17608             if(this.labelWidth < 13 && this.labelmd == 0){
17609                 this.labelmd = this.labelWidth;
17610             }
17611             
17612             if(this.labellg > 0){
17613                 labelCfg.cls += ' col-lg-' + this.labellg;
17614                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17615             }
17616             
17617             if(this.labelmd > 0){
17618                 labelCfg.cls += ' col-md-' + this.labelmd;
17619                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17620             }
17621             
17622             if(this.labelsm > 0){
17623                 labelCfg.cls += ' col-sm-' + this.labelsm;
17624                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17625             }
17626             
17627             if(this.labelxs > 0){
17628                 labelCfg.cls += ' col-xs-' + this.labelxs;
17629                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17630             }
17631                 
17632                 
17633         } else if ( this.fieldLabel.length) {
17634             cfg.cn = [
17635                 {
17636                    tag : 'i',
17637                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17638                    tooltip : 'This field is required'
17639                 },
17640                 {
17641                     tag: 'label',
17642                     cls : 'control-label',
17643                     html : this.fieldLabel
17644
17645                 },
17646                 {
17647                     cls : '', 
17648                     cn: [
17649                         combobox
17650                     ]
17651                 }
17652             ];
17653             
17654             if(this.indicatorpos == 'right'){
17655                 cfg.cn = [
17656                     {
17657                         tag: 'label',
17658                         cls : 'control-label',
17659                         html : this.fieldLabel,
17660                         cn : [
17661                             {
17662                                tag : 'i',
17663                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17664                                tooltip : 'This field is required'
17665                             }
17666                         ]
17667                     },
17668                     {
17669                         cls : '', 
17670                         cn: [
17671                             combobox
17672                         ]
17673                     }
17674                 ];
17675             }
17676         } else {
17677             cfg.cn = combobox;    
17678         }
17679         
17680         
17681         var settings = this;
17682         
17683         ['xs','sm','md','lg'].map(function(size){
17684             if (settings[size]) {
17685                 cfg.cls += ' col-' + size + '-' + settings[size];
17686             }
17687         });
17688         
17689         return cfg;
17690     },
17691     
17692     initTouchView : function()
17693     {
17694         this.renderTouchView();
17695         
17696         this.touchViewEl.on('scroll', function(){
17697             this.el.dom.scrollTop = 0;
17698         }, this);
17699         
17700         this.originalValue = this.getValue();
17701         
17702         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17703         
17704         this.inputEl().on("click", this.showTouchView, this);
17705         if (this.triggerEl) {
17706             this.triggerEl.on("click", this.showTouchView, this);
17707         }
17708         
17709         
17710         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17711         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17712         
17713         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17714         
17715         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17716         this.store.on('load', this.onTouchViewLoad, this);
17717         this.store.on('loadexception', this.onTouchViewLoadException, this);
17718         
17719         if(this.hiddenName){
17720             
17721             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17722             
17723             this.hiddenField.dom.value =
17724                 this.hiddenValue !== undefined ? this.hiddenValue :
17725                 this.value !== undefined ? this.value : '';
17726         
17727             this.el.dom.removeAttribute('name');
17728             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17729         }
17730         
17731         if(this.multiple){
17732             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17733             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17734         }
17735         
17736         if(this.removable && !this.multiple){
17737             var close = this.closeTriggerEl();
17738             if(close){
17739                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17740                 close.on('click', this.removeBtnClick, this, close);
17741             }
17742         }
17743         /*
17744          * fix the bug in Safari iOS8
17745          */
17746         this.inputEl().on("focus", function(e){
17747             document.activeElement.blur();
17748         }, this);
17749         
17750         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17751         
17752         return;
17753         
17754         
17755     },
17756     
17757     renderTouchView : function()
17758     {
17759         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17760         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17761         
17762         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17763         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17764         
17765         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17766         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17767         this.touchViewBodyEl.setStyle('overflow', 'auto');
17768         
17769         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17770         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17771         
17772         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17773         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17774         
17775     },
17776     
17777     showTouchView : function()
17778     {
17779         if(this.disabled){
17780             return;
17781         }
17782         
17783         this.touchViewHeaderEl.hide();
17784
17785         if(this.modalTitle.length){
17786             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17787             this.touchViewHeaderEl.show();
17788         }
17789
17790         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17791         this.touchViewEl.show();
17792
17793         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17794         
17795         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17796         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17797
17798         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17799
17800         if(this.modalTitle.length){
17801             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17802         }
17803         
17804         this.touchViewBodyEl.setHeight(bodyHeight);
17805
17806         if(this.animate){
17807             var _this = this;
17808             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17809         }else{
17810             this.touchViewEl.addClass(['in','show']);
17811         }
17812         
17813         if(this._touchViewMask){
17814             Roo.get(document.body).addClass("x-body-masked");
17815             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17816             this._touchViewMask.setStyle('z-index', 10000);
17817             this._touchViewMask.addClass('show');
17818         }
17819         
17820         this.doTouchViewQuery();
17821         
17822     },
17823     
17824     hideTouchView : function()
17825     {
17826         this.touchViewEl.removeClass(['in','show']);
17827
17828         if(this.animate){
17829             var _this = this;
17830             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17831         }else{
17832             this.touchViewEl.setStyle('display', 'none');
17833         }
17834         
17835         if(this._touchViewMask){
17836             this._touchViewMask.removeClass('show');
17837             Roo.get(document.body).removeClass("x-body-masked");
17838         }
17839     },
17840     
17841     setTouchViewValue : function()
17842     {
17843         if(this.multiple){
17844             this.clearItem();
17845         
17846             var _this = this;
17847
17848             Roo.each(this.tickItems, function(o){
17849                 this.addItem(o);
17850             }, this);
17851         }
17852         
17853         this.hideTouchView();
17854     },
17855     
17856     doTouchViewQuery : function()
17857     {
17858         var qe = {
17859             query: '',
17860             forceAll: true,
17861             combo: this,
17862             cancel:false
17863         };
17864         
17865         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17866             return false;
17867         }
17868         
17869         if(!this.alwaysQuery || this.mode == 'local'){
17870             this.onTouchViewLoad();
17871             return;
17872         }
17873         
17874         this.store.load();
17875     },
17876     
17877     onTouchViewBeforeLoad : function(combo,opts)
17878     {
17879         return;
17880     },
17881
17882     // private
17883     onTouchViewLoad : function()
17884     {
17885         if(this.store.getCount() < 1){
17886             this.onTouchViewEmptyResults();
17887             return;
17888         }
17889         
17890         this.clearTouchView();
17891         
17892         var rawValue = this.getRawValue();
17893         
17894         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17895         
17896         this.tickItems = [];
17897         
17898         this.store.data.each(function(d, rowIndex){
17899             var row = this.touchViewListGroup.createChild(template);
17900             
17901             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17902                 row.addClass(d.data.cls);
17903             }
17904             
17905             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17906                 var cfg = {
17907                     data : d.data,
17908                     html : d.data[this.displayField]
17909                 };
17910                 
17911                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17912                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17913                 }
17914             }
17915             row.removeClass('selected');
17916             if(!this.multiple && this.valueField &&
17917                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17918             {
17919                 // radio buttons..
17920                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17921                 row.addClass('selected');
17922             }
17923             
17924             if(this.multiple && this.valueField &&
17925                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17926             {
17927                 
17928                 // checkboxes...
17929                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17930                 this.tickItems.push(d.data);
17931             }
17932             
17933             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17934             
17935         }, this);
17936         
17937         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17938         
17939         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17940
17941         if(this.modalTitle.length){
17942             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17943         }
17944
17945         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17946         
17947         if(this.mobile_restrict_height && listHeight < bodyHeight){
17948             this.touchViewBodyEl.setHeight(listHeight);
17949         }
17950         
17951         var _this = this;
17952         
17953         if(firstChecked && listHeight > bodyHeight){
17954             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17955         }
17956         
17957     },
17958     
17959     onTouchViewLoadException : function()
17960     {
17961         this.hideTouchView();
17962     },
17963     
17964     onTouchViewEmptyResults : function()
17965     {
17966         this.clearTouchView();
17967         
17968         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17969         
17970         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17971         
17972     },
17973     
17974     clearTouchView : function()
17975     {
17976         this.touchViewListGroup.dom.innerHTML = '';
17977     },
17978     
17979     onTouchViewClick : function(e, el, o)
17980     {
17981         e.preventDefault();
17982         
17983         var row = o.row;
17984         var rowIndex = o.rowIndex;
17985         
17986         var r = this.store.getAt(rowIndex);
17987         
17988         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17989             
17990             if(!this.multiple){
17991                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17992                     c.dom.removeAttribute('checked');
17993                 }, this);
17994
17995                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17996
17997                 this.setFromData(r.data);
17998
17999                 var close = this.closeTriggerEl();
18000
18001                 if(close){
18002                     close.show();
18003                 }
18004
18005                 this.hideTouchView();
18006
18007                 this.fireEvent('select', this, r, rowIndex);
18008
18009                 return;
18010             }
18011
18012             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18013                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18014                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18015                 return;
18016             }
18017
18018             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18019             this.addItem(r.data);
18020             this.tickItems.push(r.data);
18021         }
18022     },
18023     
18024     getAutoCreateNativeIOS : function()
18025     {
18026         var cfg = {
18027             cls: 'form-group' //input-group,
18028         };
18029         
18030         var combobox =  {
18031             tag: 'select',
18032             cls : 'roo-ios-select'
18033         };
18034         
18035         if (this.name) {
18036             combobox.name = this.name;
18037         }
18038         
18039         if (this.disabled) {
18040             combobox.disabled = true;
18041         }
18042         
18043         var settings = this;
18044         
18045         ['xs','sm','md','lg'].map(function(size){
18046             if (settings[size]) {
18047                 cfg.cls += ' col-' + size + '-' + settings[size];
18048             }
18049         });
18050         
18051         cfg.cn = combobox;
18052         
18053         return cfg;
18054         
18055     },
18056     
18057     initIOSView : function()
18058     {
18059         this.store.on('load', this.onIOSViewLoad, this);
18060         
18061         return;
18062     },
18063     
18064     onIOSViewLoad : function()
18065     {
18066         if(this.store.getCount() < 1){
18067             return;
18068         }
18069         
18070         this.clearIOSView();
18071         
18072         if(this.allowBlank) {
18073             
18074             var default_text = '-- SELECT --';
18075             
18076             if(this.placeholder.length){
18077                 default_text = this.placeholder;
18078             }
18079             
18080             if(this.emptyTitle.length){
18081                 default_text += ' - ' + this.emptyTitle + ' -';
18082             }
18083             
18084             var opt = this.inputEl().createChild({
18085                 tag: 'option',
18086                 value : 0,
18087                 html : default_text
18088             });
18089             
18090             var o = {};
18091             o[this.valueField] = 0;
18092             o[this.displayField] = default_text;
18093             
18094             this.ios_options.push({
18095                 data : o,
18096                 el : opt
18097             });
18098             
18099         }
18100         
18101         this.store.data.each(function(d, rowIndex){
18102             
18103             var html = '';
18104             
18105             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18106                 html = d.data[this.displayField];
18107             }
18108             
18109             var value = '';
18110             
18111             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18112                 value = d.data[this.valueField];
18113             }
18114             
18115             var option = {
18116                 tag: 'option',
18117                 value : value,
18118                 html : html
18119             };
18120             
18121             if(this.value == d.data[this.valueField]){
18122                 option['selected'] = true;
18123             }
18124             
18125             var opt = this.inputEl().createChild(option);
18126             
18127             this.ios_options.push({
18128                 data : d.data,
18129                 el : opt
18130             });
18131             
18132         }, this);
18133         
18134         this.inputEl().on('change', function(){
18135            this.fireEvent('select', this);
18136         }, this);
18137         
18138     },
18139     
18140     clearIOSView: function()
18141     {
18142         this.inputEl().dom.innerHTML = '';
18143         
18144         this.ios_options = [];
18145     },
18146     
18147     setIOSValue: function(v)
18148     {
18149         this.value = v;
18150         
18151         if(!this.ios_options){
18152             return;
18153         }
18154         
18155         Roo.each(this.ios_options, function(opts){
18156            
18157            opts.el.dom.removeAttribute('selected');
18158            
18159            if(opts.data[this.valueField] != v){
18160                return;
18161            }
18162            
18163            opts.el.dom.setAttribute('selected', true);
18164            
18165         }, this);
18166     }
18167
18168     /** 
18169     * @cfg {Boolean} grow 
18170     * @hide 
18171     */
18172     /** 
18173     * @cfg {Number} growMin 
18174     * @hide 
18175     */
18176     /** 
18177     * @cfg {Number} growMax 
18178     * @hide 
18179     */
18180     /**
18181      * @hide
18182      * @method autoSize
18183      */
18184 });
18185
18186 Roo.apply(Roo.bootstrap.ComboBox,  {
18187     
18188     header : {
18189         tag: 'div',
18190         cls: 'modal-header',
18191         cn: [
18192             {
18193                 tag: 'h4',
18194                 cls: 'modal-title'
18195             }
18196         ]
18197     },
18198     
18199     body : {
18200         tag: 'div',
18201         cls: 'modal-body',
18202         cn: [
18203             {
18204                 tag: 'ul',
18205                 cls: 'list-group'
18206             }
18207         ]
18208     },
18209     
18210     listItemRadio : {
18211         tag: 'li',
18212         cls: 'list-group-item',
18213         cn: [
18214             {
18215                 tag: 'span',
18216                 cls: 'roo-combobox-list-group-item-value'
18217             },
18218             {
18219                 tag: 'div',
18220                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18221                 cn: [
18222                     {
18223                         tag: 'input',
18224                         type: 'radio'
18225                     },
18226                     {
18227                         tag: 'label'
18228                     }
18229                 ]
18230             }
18231         ]
18232     },
18233     
18234     listItemCheckbox : {
18235         tag: 'li',
18236         cls: 'list-group-item',
18237         cn: [
18238             {
18239                 tag: 'span',
18240                 cls: 'roo-combobox-list-group-item-value'
18241             },
18242             {
18243                 tag: 'div',
18244                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18245                 cn: [
18246                     {
18247                         tag: 'input',
18248                         type: 'checkbox'
18249                     },
18250                     {
18251                         tag: 'label'
18252                     }
18253                 ]
18254             }
18255         ]
18256     },
18257     
18258     emptyResult : {
18259         tag: 'div',
18260         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18261     },
18262     
18263     footer : {
18264         tag: 'div',
18265         cls: 'modal-footer',
18266         cn: [
18267             {
18268                 tag: 'div',
18269                 cls: 'row',
18270                 cn: [
18271                     {
18272                         tag: 'div',
18273                         cls: 'col-xs-6 text-left',
18274                         cn: {
18275                             tag: 'button',
18276                             cls: 'btn btn-danger roo-touch-view-cancel',
18277                             html: 'Cancel'
18278                         }
18279                     },
18280                     {
18281                         tag: 'div',
18282                         cls: 'col-xs-6 text-right',
18283                         cn: {
18284                             tag: 'button',
18285                             cls: 'btn btn-success roo-touch-view-ok',
18286                             html: 'OK'
18287                         }
18288                     }
18289                 ]
18290             }
18291         ]
18292         
18293     }
18294 });
18295
18296 Roo.apply(Roo.bootstrap.ComboBox,  {
18297     
18298     touchViewTemplate : {
18299         tag: 'div',
18300         cls: 'modal fade roo-combobox-touch-view',
18301         cn: [
18302             {
18303                 tag: 'div',
18304                 cls: 'modal-dialog',
18305                 style : 'position:fixed', // we have to fix position....
18306                 cn: [
18307                     {
18308                         tag: 'div',
18309                         cls: 'modal-content',
18310                         cn: [
18311                             Roo.bootstrap.ComboBox.header,
18312                             Roo.bootstrap.ComboBox.body,
18313                             Roo.bootstrap.ComboBox.footer
18314                         ]
18315                     }
18316                 ]
18317             }
18318         ]
18319     }
18320 });/*
18321  * Based on:
18322  * Ext JS Library 1.1.1
18323  * Copyright(c) 2006-2007, Ext JS, LLC.
18324  *
18325  * Originally Released Under LGPL - original licence link has changed is not relivant.
18326  *
18327  * Fork - LGPL
18328  * <script type="text/javascript">
18329  */
18330
18331 /**
18332  * @class Roo.View
18333  * @extends Roo.util.Observable
18334  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18335  * This class also supports single and multi selection modes. <br>
18336  * Create a data model bound view:
18337  <pre><code>
18338  var store = new Roo.data.Store(...);
18339
18340  var view = new Roo.View({
18341     el : "my-element",
18342     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18343  
18344     singleSelect: true,
18345     selectedClass: "ydataview-selected",
18346     store: store
18347  });
18348
18349  // listen for node click?
18350  view.on("click", function(vw, index, node, e){
18351  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18352  });
18353
18354  // load XML data
18355  dataModel.load("foobar.xml");
18356  </code></pre>
18357  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18358  * <br><br>
18359  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18360  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18361  * 
18362  * Note: old style constructor is still suported (container, template, config)
18363  * 
18364  * @constructor
18365  * Create a new View
18366  * @param {Object} config The config object
18367  * 
18368  */
18369 Roo.View = function(config, depreciated_tpl, depreciated_config){
18370     
18371     this.parent = false;
18372     
18373     if (typeof(depreciated_tpl) == 'undefined') {
18374         // new way.. - universal constructor.
18375         Roo.apply(this, config);
18376         this.el  = Roo.get(this.el);
18377     } else {
18378         // old format..
18379         this.el  = Roo.get(config);
18380         this.tpl = depreciated_tpl;
18381         Roo.apply(this, depreciated_config);
18382     }
18383     this.wrapEl  = this.el.wrap().wrap();
18384     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18385     
18386     
18387     if(typeof(this.tpl) == "string"){
18388         this.tpl = new Roo.Template(this.tpl);
18389     } else {
18390         // support xtype ctors..
18391         this.tpl = new Roo.factory(this.tpl, Roo);
18392     }
18393     
18394     
18395     this.tpl.compile();
18396     
18397     /** @private */
18398     this.addEvents({
18399         /**
18400          * @event beforeclick
18401          * Fires before a click is processed. Returns false to cancel the default action.
18402          * @param {Roo.View} this
18403          * @param {Number} index The index of the target node
18404          * @param {HTMLElement} node The target node
18405          * @param {Roo.EventObject} e The raw event object
18406          */
18407             "beforeclick" : true,
18408         /**
18409          * @event click
18410          * Fires when a template node is clicked.
18411          * @param {Roo.View} this
18412          * @param {Number} index The index of the target node
18413          * @param {HTMLElement} node The target node
18414          * @param {Roo.EventObject} e The raw event object
18415          */
18416             "click" : true,
18417         /**
18418          * @event dblclick
18419          * Fires when a template node is double clicked.
18420          * @param {Roo.View} this
18421          * @param {Number} index The index of the target node
18422          * @param {HTMLElement} node The target node
18423          * @param {Roo.EventObject} e The raw event object
18424          */
18425             "dblclick" : true,
18426         /**
18427          * @event contextmenu
18428          * Fires when a template node is right clicked.
18429          * @param {Roo.View} this
18430          * @param {Number} index The index of the target node
18431          * @param {HTMLElement} node The target node
18432          * @param {Roo.EventObject} e The raw event object
18433          */
18434             "contextmenu" : true,
18435         /**
18436          * @event selectionchange
18437          * Fires when the selected nodes change.
18438          * @param {Roo.View} this
18439          * @param {Array} selections Array of the selected nodes
18440          */
18441             "selectionchange" : true,
18442     
18443         /**
18444          * @event beforeselect
18445          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18446          * @param {Roo.View} this
18447          * @param {HTMLElement} node The node to be selected
18448          * @param {Array} selections Array of currently selected nodes
18449          */
18450             "beforeselect" : true,
18451         /**
18452          * @event preparedata
18453          * Fires on every row to render, to allow you to change the data.
18454          * @param {Roo.View} this
18455          * @param {Object} data to be rendered (change this)
18456          */
18457           "preparedata" : true
18458           
18459           
18460         });
18461
18462
18463
18464     this.el.on({
18465         "click": this.onClick,
18466         "dblclick": this.onDblClick,
18467         "contextmenu": this.onContextMenu,
18468         scope:this
18469     });
18470
18471     this.selections = [];
18472     this.nodes = [];
18473     this.cmp = new Roo.CompositeElementLite([]);
18474     if(this.store){
18475         this.store = Roo.factory(this.store, Roo.data);
18476         this.setStore(this.store, true);
18477     }
18478     
18479     if ( this.footer && this.footer.xtype) {
18480            
18481          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18482         
18483         this.footer.dataSource = this.store;
18484         this.footer.container = fctr;
18485         this.footer = Roo.factory(this.footer, Roo);
18486         fctr.insertFirst(this.el);
18487         
18488         // this is a bit insane - as the paging toolbar seems to detach the el..
18489 //        dom.parentNode.parentNode.parentNode
18490          // they get detached?
18491     }
18492     
18493     
18494     Roo.View.superclass.constructor.call(this);
18495     
18496     
18497 };
18498
18499 Roo.extend(Roo.View, Roo.util.Observable, {
18500     
18501      /**
18502      * @cfg {Roo.data.Store} store Data store to load data from.
18503      */
18504     store : false,
18505     
18506     /**
18507      * @cfg {String|Roo.Element} el The container element.
18508      */
18509     el : '',
18510     
18511     /**
18512      * @cfg {String|Roo.Template} tpl The template used by this View 
18513      */
18514     tpl : false,
18515     /**
18516      * @cfg {String} dataName the named area of the template to use as the data area
18517      *                          Works with domtemplates roo-name="name"
18518      */
18519     dataName: false,
18520     /**
18521      * @cfg {String} selectedClass The css class to add to selected nodes
18522      */
18523     selectedClass : "x-view-selected",
18524      /**
18525      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18526      */
18527     emptyText : "",
18528     
18529     /**
18530      * @cfg {String} text to display on mask (default Loading)
18531      */
18532     mask : false,
18533     /**
18534      * @cfg {Boolean} multiSelect Allow multiple selection
18535      */
18536     multiSelect : false,
18537     /**
18538      * @cfg {Boolean} singleSelect Allow single selection
18539      */
18540     singleSelect:  false,
18541     
18542     /**
18543      * @cfg {Boolean} toggleSelect - selecting 
18544      */
18545     toggleSelect : false,
18546     
18547     /**
18548      * @cfg {Boolean} tickable - selecting 
18549      */
18550     tickable : false,
18551     
18552     /**
18553      * Returns the element this view is bound to.
18554      * @return {Roo.Element}
18555      */
18556     getEl : function(){
18557         return this.wrapEl;
18558     },
18559     
18560     
18561
18562     /**
18563      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18564      */
18565     refresh : function(){
18566         //Roo.log('refresh');
18567         var t = this.tpl;
18568         
18569         // if we are using something like 'domtemplate', then
18570         // the what gets used is:
18571         // t.applySubtemplate(NAME, data, wrapping data..)
18572         // the outer template then get' applied with
18573         //     the store 'extra data'
18574         // and the body get's added to the
18575         //      roo-name="data" node?
18576         //      <span class='roo-tpl-{name}'></span> ?????
18577         
18578         
18579         
18580         this.clearSelections();
18581         this.el.update("");
18582         var html = [];
18583         var records = this.store.getRange();
18584         if(records.length < 1) {
18585             
18586             // is this valid??  = should it render a template??
18587             
18588             this.el.update(this.emptyText);
18589             return;
18590         }
18591         var el = this.el;
18592         if (this.dataName) {
18593             this.el.update(t.apply(this.store.meta)); //????
18594             el = this.el.child('.roo-tpl-' + this.dataName);
18595         }
18596         
18597         for(var i = 0, len = records.length; i < len; i++){
18598             var data = this.prepareData(records[i].data, i, records[i]);
18599             this.fireEvent("preparedata", this, data, i, records[i]);
18600             
18601             var d = Roo.apply({}, data);
18602             
18603             if(this.tickable){
18604                 Roo.apply(d, {'roo-id' : Roo.id()});
18605                 
18606                 var _this = this;
18607             
18608                 Roo.each(this.parent.item, function(item){
18609                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18610                         return;
18611                     }
18612                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18613                 });
18614             }
18615             
18616             html[html.length] = Roo.util.Format.trim(
18617                 this.dataName ?
18618                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18619                     t.apply(d)
18620             );
18621         }
18622         
18623         
18624         
18625         el.update(html.join(""));
18626         this.nodes = el.dom.childNodes;
18627         this.updateIndexes(0);
18628     },
18629     
18630
18631     /**
18632      * Function to override to reformat the data that is sent to
18633      * the template for each node.
18634      * DEPRICATED - use the preparedata event handler.
18635      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18636      * a JSON object for an UpdateManager bound view).
18637      */
18638     prepareData : function(data, index, record)
18639     {
18640         this.fireEvent("preparedata", this, data, index, record);
18641         return data;
18642     },
18643
18644     onUpdate : function(ds, record){
18645         // Roo.log('on update');   
18646         this.clearSelections();
18647         var index = this.store.indexOf(record);
18648         var n = this.nodes[index];
18649         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18650         n.parentNode.removeChild(n);
18651         this.updateIndexes(index, index);
18652     },
18653
18654     
18655     
18656 // --------- FIXME     
18657     onAdd : function(ds, records, index)
18658     {
18659         //Roo.log(['on Add', ds, records, index] );        
18660         this.clearSelections();
18661         if(this.nodes.length == 0){
18662             this.refresh();
18663             return;
18664         }
18665         var n = this.nodes[index];
18666         for(var i = 0, len = records.length; i < len; i++){
18667             var d = this.prepareData(records[i].data, i, records[i]);
18668             if(n){
18669                 this.tpl.insertBefore(n, d);
18670             }else{
18671                 
18672                 this.tpl.append(this.el, d);
18673             }
18674         }
18675         this.updateIndexes(index);
18676     },
18677
18678     onRemove : function(ds, record, index){
18679        // Roo.log('onRemove');
18680         this.clearSelections();
18681         var el = this.dataName  ?
18682             this.el.child('.roo-tpl-' + this.dataName) :
18683             this.el; 
18684         
18685         el.dom.removeChild(this.nodes[index]);
18686         this.updateIndexes(index);
18687     },
18688
18689     /**
18690      * Refresh an individual node.
18691      * @param {Number} index
18692      */
18693     refreshNode : function(index){
18694         this.onUpdate(this.store, this.store.getAt(index));
18695     },
18696
18697     updateIndexes : function(startIndex, endIndex){
18698         var ns = this.nodes;
18699         startIndex = startIndex || 0;
18700         endIndex = endIndex || ns.length - 1;
18701         for(var i = startIndex; i <= endIndex; i++){
18702             ns[i].nodeIndex = i;
18703         }
18704     },
18705
18706     /**
18707      * Changes the data store this view uses and refresh the view.
18708      * @param {Store} store
18709      */
18710     setStore : function(store, initial){
18711         if(!initial && this.store){
18712             this.store.un("datachanged", this.refresh);
18713             this.store.un("add", this.onAdd);
18714             this.store.un("remove", this.onRemove);
18715             this.store.un("update", this.onUpdate);
18716             this.store.un("clear", this.refresh);
18717             this.store.un("beforeload", this.onBeforeLoad);
18718             this.store.un("load", this.onLoad);
18719             this.store.un("loadexception", this.onLoad);
18720         }
18721         if(store){
18722           
18723             store.on("datachanged", this.refresh, this);
18724             store.on("add", this.onAdd, this);
18725             store.on("remove", this.onRemove, this);
18726             store.on("update", this.onUpdate, this);
18727             store.on("clear", this.refresh, this);
18728             store.on("beforeload", this.onBeforeLoad, this);
18729             store.on("load", this.onLoad, this);
18730             store.on("loadexception", this.onLoad, this);
18731         }
18732         
18733         if(store){
18734             this.refresh();
18735         }
18736     },
18737     /**
18738      * onbeforeLoad - masks the loading area.
18739      *
18740      */
18741     onBeforeLoad : function(store,opts)
18742     {
18743          //Roo.log('onBeforeLoad');   
18744         if (!opts.add) {
18745             this.el.update("");
18746         }
18747         this.el.mask(this.mask ? this.mask : "Loading" ); 
18748     },
18749     onLoad : function ()
18750     {
18751         this.el.unmask();
18752     },
18753     
18754
18755     /**
18756      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18757      * @param {HTMLElement} node
18758      * @return {HTMLElement} The template node
18759      */
18760     findItemFromChild : function(node){
18761         var el = this.dataName  ?
18762             this.el.child('.roo-tpl-' + this.dataName,true) :
18763             this.el.dom; 
18764         
18765         if(!node || node.parentNode == el){
18766                     return node;
18767             }
18768             var p = node.parentNode;
18769             while(p && p != el){
18770             if(p.parentNode == el){
18771                 return p;
18772             }
18773             p = p.parentNode;
18774         }
18775             return null;
18776     },
18777
18778     /** @ignore */
18779     onClick : function(e){
18780         var item = this.findItemFromChild(e.getTarget());
18781         if(item){
18782             var index = this.indexOf(item);
18783             if(this.onItemClick(item, index, e) !== false){
18784                 this.fireEvent("click", this, index, item, e);
18785             }
18786         }else{
18787             this.clearSelections();
18788         }
18789     },
18790
18791     /** @ignore */
18792     onContextMenu : function(e){
18793         var item = this.findItemFromChild(e.getTarget());
18794         if(item){
18795             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18796         }
18797     },
18798
18799     /** @ignore */
18800     onDblClick : function(e){
18801         var item = this.findItemFromChild(e.getTarget());
18802         if(item){
18803             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18804         }
18805     },
18806
18807     onItemClick : function(item, index, e)
18808     {
18809         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18810             return false;
18811         }
18812         if (this.toggleSelect) {
18813             var m = this.isSelected(item) ? 'unselect' : 'select';
18814             //Roo.log(m);
18815             var _t = this;
18816             _t[m](item, true, false);
18817             return true;
18818         }
18819         if(this.multiSelect || this.singleSelect){
18820             if(this.multiSelect && e.shiftKey && this.lastSelection){
18821                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18822             }else{
18823                 this.select(item, this.multiSelect && e.ctrlKey);
18824                 this.lastSelection = item;
18825             }
18826             
18827             if(!this.tickable){
18828                 e.preventDefault();
18829             }
18830             
18831         }
18832         return true;
18833     },
18834
18835     /**
18836      * Get the number of selected nodes.
18837      * @return {Number}
18838      */
18839     getSelectionCount : function(){
18840         return this.selections.length;
18841     },
18842
18843     /**
18844      * Get the currently selected nodes.
18845      * @return {Array} An array of HTMLElements
18846      */
18847     getSelectedNodes : function(){
18848         return this.selections;
18849     },
18850
18851     /**
18852      * Get the indexes of the selected nodes.
18853      * @return {Array}
18854      */
18855     getSelectedIndexes : function(){
18856         var indexes = [], s = this.selections;
18857         for(var i = 0, len = s.length; i < len; i++){
18858             indexes.push(s[i].nodeIndex);
18859         }
18860         return indexes;
18861     },
18862
18863     /**
18864      * Clear all selections
18865      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18866      */
18867     clearSelections : function(suppressEvent){
18868         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18869             this.cmp.elements = this.selections;
18870             this.cmp.removeClass(this.selectedClass);
18871             this.selections = [];
18872             if(!suppressEvent){
18873                 this.fireEvent("selectionchange", this, this.selections);
18874             }
18875         }
18876     },
18877
18878     /**
18879      * Returns true if the passed node is selected
18880      * @param {HTMLElement/Number} node The node or node index
18881      * @return {Boolean}
18882      */
18883     isSelected : function(node){
18884         var s = this.selections;
18885         if(s.length < 1){
18886             return false;
18887         }
18888         node = this.getNode(node);
18889         return s.indexOf(node) !== -1;
18890     },
18891
18892     /**
18893      * Selects nodes.
18894      * @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
18895      * @param {Boolean} keepExisting (optional) true to keep existing selections
18896      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18897      */
18898     select : function(nodeInfo, keepExisting, suppressEvent){
18899         if(nodeInfo instanceof Array){
18900             if(!keepExisting){
18901                 this.clearSelections(true);
18902             }
18903             for(var i = 0, len = nodeInfo.length; i < len; i++){
18904                 this.select(nodeInfo[i], true, true);
18905             }
18906             return;
18907         } 
18908         var node = this.getNode(nodeInfo);
18909         if(!node || this.isSelected(node)){
18910             return; // already selected.
18911         }
18912         if(!keepExisting){
18913             this.clearSelections(true);
18914         }
18915         
18916         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18917             Roo.fly(node).addClass(this.selectedClass);
18918             this.selections.push(node);
18919             if(!suppressEvent){
18920                 this.fireEvent("selectionchange", this, this.selections);
18921             }
18922         }
18923         
18924         
18925     },
18926       /**
18927      * Unselects nodes.
18928      * @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
18929      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18930      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18931      */
18932     unselect : function(nodeInfo, keepExisting, suppressEvent)
18933     {
18934         if(nodeInfo instanceof Array){
18935             Roo.each(this.selections, function(s) {
18936                 this.unselect(s, nodeInfo);
18937             }, this);
18938             return;
18939         }
18940         var node = this.getNode(nodeInfo);
18941         if(!node || !this.isSelected(node)){
18942             //Roo.log("not selected");
18943             return; // not selected.
18944         }
18945         // fireevent???
18946         var ns = [];
18947         Roo.each(this.selections, function(s) {
18948             if (s == node ) {
18949                 Roo.fly(node).removeClass(this.selectedClass);
18950
18951                 return;
18952             }
18953             ns.push(s);
18954         },this);
18955         
18956         this.selections= ns;
18957         this.fireEvent("selectionchange", this, this.selections);
18958     },
18959
18960     /**
18961      * Gets a template node.
18962      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18963      * @return {HTMLElement} The node or null if it wasn't found
18964      */
18965     getNode : function(nodeInfo){
18966         if(typeof nodeInfo == "string"){
18967             return document.getElementById(nodeInfo);
18968         }else if(typeof nodeInfo == "number"){
18969             return this.nodes[nodeInfo];
18970         }
18971         return nodeInfo;
18972     },
18973
18974     /**
18975      * Gets a range template nodes.
18976      * @param {Number} startIndex
18977      * @param {Number} endIndex
18978      * @return {Array} An array of nodes
18979      */
18980     getNodes : function(start, end){
18981         var ns = this.nodes;
18982         start = start || 0;
18983         end = typeof end == "undefined" ? ns.length - 1 : end;
18984         var nodes = [];
18985         if(start <= end){
18986             for(var i = start; i <= end; i++){
18987                 nodes.push(ns[i]);
18988             }
18989         } else{
18990             for(var i = start; i >= end; i--){
18991                 nodes.push(ns[i]);
18992             }
18993         }
18994         return nodes;
18995     },
18996
18997     /**
18998      * Finds the index of the passed node
18999      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19000      * @return {Number} The index of the node or -1
19001      */
19002     indexOf : function(node){
19003         node = this.getNode(node);
19004         if(typeof node.nodeIndex == "number"){
19005             return node.nodeIndex;
19006         }
19007         var ns = this.nodes;
19008         for(var i = 0, len = ns.length; i < len; i++){
19009             if(ns[i] == node){
19010                 return i;
19011             }
19012         }
19013         return -1;
19014     }
19015 });
19016 /*
19017  * - LGPL
19018  *
19019  * based on jquery fullcalendar
19020  * 
19021  */
19022
19023 Roo.bootstrap = Roo.bootstrap || {};
19024 /**
19025  * @class Roo.bootstrap.Calendar
19026  * @extends Roo.bootstrap.Component
19027  * Bootstrap Calendar class
19028  * @cfg {Boolean} loadMask (true|false) default false
19029  * @cfg {Object} header generate the user specific header of the calendar, default false
19030
19031  * @constructor
19032  * Create a new Container
19033  * @param {Object} config The config object
19034  */
19035
19036
19037
19038 Roo.bootstrap.Calendar = function(config){
19039     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19040      this.addEvents({
19041         /**
19042              * @event select
19043              * Fires when a date is selected
19044              * @param {DatePicker} this
19045              * @param {Date} date The selected date
19046              */
19047         'select': true,
19048         /**
19049              * @event monthchange
19050              * Fires when the displayed month changes 
19051              * @param {DatePicker} this
19052              * @param {Date} date The selected month
19053              */
19054         'monthchange': true,
19055         /**
19056              * @event evententer
19057              * Fires when mouse over an event
19058              * @param {Calendar} this
19059              * @param {event} Event
19060              */
19061         'evententer': true,
19062         /**
19063              * @event eventleave
19064              * Fires when the mouse leaves an
19065              * @param {Calendar} this
19066              * @param {event}
19067              */
19068         'eventleave': true,
19069         /**
19070              * @event eventclick
19071              * Fires when the mouse click an
19072              * @param {Calendar} this
19073              * @param {event}
19074              */
19075         'eventclick': true
19076         
19077     });
19078
19079 };
19080
19081 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19082     
19083      /**
19084      * @cfg {Number} startDay
19085      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19086      */
19087     startDay : 0,
19088     
19089     loadMask : false,
19090     
19091     header : false,
19092       
19093     getAutoCreate : function(){
19094         
19095         
19096         var fc_button = function(name, corner, style, content ) {
19097             return Roo.apply({},{
19098                 tag : 'span',
19099                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19100                          (corner.length ?
19101                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19102                             ''
19103                         ),
19104                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19105                 unselectable: 'on'
19106             });
19107         };
19108         
19109         var header = {};
19110         
19111         if(!this.header){
19112             header = {
19113                 tag : 'table',
19114                 cls : 'fc-header',
19115                 style : 'width:100%',
19116                 cn : [
19117                     {
19118                         tag: 'tr',
19119                         cn : [
19120                             {
19121                                 tag : 'td',
19122                                 cls : 'fc-header-left',
19123                                 cn : [
19124                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19125                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19126                                     { tag: 'span', cls: 'fc-header-space' },
19127                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19128
19129
19130                                 ]
19131                             },
19132
19133                             {
19134                                 tag : 'td',
19135                                 cls : 'fc-header-center',
19136                                 cn : [
19137                                     {
19138                                         tag: 'span',
19139                                         cls: 'fc-header-title',
19140                                         cn : {
19141                                             tag: 'H2',
19142                                             html : 'month / year'
19143                                         }
19144                                     }
19145
19146                                 ]
19147                             },
19148                             {
19149                                 tag : 'td',
19150                                 cls : 'fc-header-right',
19151                                 cn : [
19152                               /*      fc_button('month', 'left', '', 'month' ),
19153                                     fc_button('week', '', '', 'week' ),
19154                                     fc_button('day', 'right', '', 'day' )
19155                                 */    
19156
19157                                 ]
19158                             }
19159
19160                         ]
19161                     }
19162                 ]
19163             };
19164         }
19165         
19166         header = this.header;
19167         
19168        
19169         var cal_heads = function() {
19170             var ret = [];
19171             // fixme - handle this.
19172             
19173             for (var i =0; i < Date.dayNames.length; i++) {
19174                 var d = Date.dayNames[i];
19175                 ret.push({
19176                     tag: 'th',
19177                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19178                     html : d.substring(0,3)
19179                 });
19180                 
19181             }
19182             ret[0].cls += ' fc-first';
19183             ret[6].cls += ' fc-last';
19184             return ret;
19185         };
19186         var cal_cell = function(n) {
19187             return  {
19188                 tag: 'td',
19189                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19190                 cn : [
19191                     {
19192                         cn : [
19193                             {
19194                                 cls: 'fc-day-number',
19195                                 html: 'D'
19196                             },
19197                             {
19198                                 cls: 'fc-day-content',
19199                              
19200                                 cn : [
19201                                      {
19202                                         style: 'position: relative;' // height: 17px;
19203                                     }
19204                                 ]
19205                             }
19206                             
19207                             
19208                         ]
19209                     }
19210                 ]
19211                 
19212             }
19213         };
19214         var cal_rows = function() {
19215             
19216             var ret = [];
19217             for (var r = 0; r < 6; r++) {
19218                 var row= {
19219                     tag : 'tr',
19220                     cls : 'fc-week',
19221                     cn : []
19222                 };
19223                 
19224                 for (var i =0; i < Date.dayNames.length; i++) {
19225                     var d = Date.dayNames[i];
19226                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19227
19228                 }
19229                 row.cn[0].cls+=' fc-first';
19230                 row.cn[0].cn[0].style = 'min-height:90px';
19231                 row.cn[6].cls+=' fc-last';
19232                 ret.push(row);
19233                 
19234             }
19235             ret[0].cls += ' fc-first';
19236             ret[4].cls += ' fc-prev-last';
19237             ret[5].cls += ' fc-last';
19238             return ret;
19239             
19240         };
19241         
19242         var cal_table = {
19243             tag: 'table',
19244             cls: 'fc-border-separate',
19245             style : 'width:100%',
19246             cellspacing  : 0,
19247             cn : [
19248                 { 
19249                     tag: 'thead',
19250                     cn : [
19251                         { 
19252                             tag: 'tr',
19253                             cls : 'fc-first fc-last',
19254                             cn : cal_heads()
19255                         }
19256                     ]
19257                 },
19258                 { 
19259                     tag: 'tbody',
19260                     cn : cal_rows()
19261                 }
19262                   
19263             ]
19264         };
19265          
19266          var cfg = {
19267             cls : 'fc fc-ltr',
19268             cn : [
19269                 header,
19270                 {
19271                     cls : 'fc-content',
19272                     style : "position: relative;",
19273                     cn : [
19274                         {
19275                             cls : 'fc-view fc-view-month fc-grid',
19276                             style : 'position: relative',
19277                             unselectable : 'on',
19278                             cn : [
19279                                 {
19280                                     cls : 'fc-event-container',
19281                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19282                                 },
19283                                 cal_table
19284                             ]
19285                         }
19286                     ]
19287     
19288                 }
19289            ] 
19290             
19291         };
19292         
19293          
19294         
19295         return cfg;
19296     },
19297     
19298     
19299     initEvents : function()
19300     {
19301         if(!this.store){
19302             throw "can not find store for calendar";
19303         }
19304         
19305         var mark = {
19306             tag: "div",
19307             cls:"x-dlg-mask",
19308             style: "text-align:center",
19309             cn: [
19310                 {
19311                     tag: "div",
19312                     style: "background-color:white;width:50%;margin:250 auto",
19313                     cn: [
19314                         {
19315                             tag: "img",
19316                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19317                         },
19318                         {
19319                             tag: "span",
19320                             html: "Loading"
19321                         }
19322                         
19323                     ]
19324                 }
19325             ]
19326         };
19327         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19328         
19329         var size = this.el.select('.fc-content', true).first().getSize();
19330         this.maskEl.setSize(size.width, size.height);
19331         this.maskEl.enableDisplayMode("block");
19332         if(!this.loadMask){
19333             this.maskEl.hide();
19334         }
19335         
19336         this.store = Roo.factory(this.store, Roo.data);
19337         this.store.on('load', this.onLoad, this);
19338         this.store.on('beforeload', this.onBeforeLoad, this);
19339         
19340         this.resize();
19341         
19342         this.cells = this.el.select('.fc-day',true);
19343         //Roo.log(this.cells);
19344         this.textNodes = this.el.query('.fc-day-number');
19345         this.cells.addClassOnOver('fc-state-hover');
19346         
19347         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19348         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19349         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19350         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19351         
19352         this.on('monthchange', this.onMonthChange, this);
19353         
19354         this.update(new Date().clearTime());
19355     },
19356     
19357     resize : function() {
19358         var sz  = this.el.getSize();
19359         
19360         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19361         this.el.select('.fc-day-content div',true).setHeight(34);
19362     },
19363     
19364     
19365     // private
19366     showPrevMonth : function(e){
19367         this.update(this.activeDate.add("mo", -1));
19368     },
19369     showToday : function(e){
19370         this.update(new Date().clearTime());
19371     },
19372     // private
19373     showNextMonth : function(e){
19374         this.update(this.activeDate.add("mo", 1));
19375     },
19376
19377     // private
19378     showPrevYear : function(){
19379         this.update(this.activeDate.add("y", -1));
19380     },
19381
19382     // private
19383     showNextYear : function(){
19384         this.update(this.activeDate.add("y", 1));
19385     },
19386
19387     
19388    // private
19389     update : function(date)
19390     {
19391         var vd = this.activeDate;
19392         this.activeDate = date;
19393 //        if(vd && this.el){
19394 //            var t = date.getTime();
19395 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19396 //                Roo.log('using add remove');
19397 //                
19398 //                this.fireEvent('monthchange', this, date);
19399 //                
19400 //                this.cells.removeClass("fc-state-highlight");
19401 //                this.cells.each(function(c){
19402 //                   if(c.dateValue == t){
19403 //                       c.addClass("fc-state-highlight");
19404 //                       setTimeout(function(){
19405 //                            try{c.dom.firstChild.focus();}catch(e){}
19406 //                       }, 50);
19407 //                       return false;
19408 //                   }
19409 //                   return true;
19410 //                });
19411 //                return;
19412 //            }
19413 //        }
19414         
19415         var days = date.getDaysInMonth();
19416         
19417         var firstOfMonth = date.getFirstDateOfMonth();
19418         var startingPos = firstOfMonth.getDay()-this.startDay;
19419         
19420         if(startingPos < this.startDay){
19421             startingPos += 7;
19422         }
19423         
19424         var pm = date.add(Date.MONTH, -1);
19425         var prevStart = pm.getDaysInMonth()-startingPos;
19426 //        
19427         this.cells = this.el.select('.fc-day',true);
19428         this.textNodes = this.el.query('.fc-day-number');
19429         this.cells.addClassOnOver('fc-state-hover');
19430         
19431         var cells = this.cells.elements;
19432         var textEls = this.textNodes;
19433         
19434         Roo.each(cells, function(cell){
19435             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19436         });
19437         
19438         days += startingPos;
19439
19440         // convert everything to numbers so it's fast
19441         var day = 86400000;
19442         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19443         //Roo.log(d);
19444         //Roo.log(pm);
19445         //Roo.log(prevStart);
19446         
19447         var today = new Date().clearTime().getTime();
19448         var sel = date.clearTime().getTime();
19449         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19450         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19451         var ddMatch = this.disabledDatesRE;
19452         var ddText = this.disabledDatesText;
19453         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19454         var ddaysText = this.disabledDaysText;
19455         var format = this.format;
19456         
19457         var setCellClass = function(cal, cell){
19458             cell.row = 0;
19459             cell.events = [];
19460             cell.more = [];
19461             //Roo.log('set Cell Class');
19462             cell.title = "";
19463             var t = d.getTime();
19464             
19465             //Roo.log(d);
19466             
19467             cell.dateValue = t;
19468             if(t == today){
19469                 cell.className += " fc-today";
19470                 cell.className += " fc-state-highlight";
19471                 cell.title = cal.todayText;
19472             }
19473             if(t == sel){
19474                 // disable highlight in other month..
19475                 //cell.className += " fc-state-highlight";
19476                 
19477             }
19478             // disabling
19479             if(t < min) {
19480                 cell.className = " fc-state-disabled";
19481                 cell.title = cal.minText;
19482                 return;
19483             }
19484             if(t > max) {
19485                 cell.className = " fc-state-disabled";
19486                 cell.title = cal.maxText;
19487                 return;
19488             }
19489             if(ddays){
19490                 if(ddays.indexOf(d.getDay()) != -1){
19491                     cell.title = ddaysText;
19492                     cell.className = " fc-state-disabled";
19493                 }
19494             }
19495             if(ddMatch && format){
19496                 var fvalue = d.dateFormat(format);
19497                 if(ddMatch.test(fvalue)){
19498                     cell.title = ddText.replace("%0", fvalue);
19499                     cell.className = " fc-state-disabled";
19500                 }
19501             }
19502             
19503             if (!cell.initialClassName) {
19504                 cell.initialClassName = cell.dom.className;
19505             }
19506             
19507             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19508         };
19509
19510         var i = 0;
19511         
19512         for(; i < startingPos; i++) {
19513             textEls[i].innerHTML = (++prevStart);
19514             d.setDate(d.getDate()+1);
19515             
19516             cells[i].className = "fc-past fc-other-month";
19517             setCellClass(this, cells[i]);
19518         }
19519         
19520         var intDay = 0;
19521         
19522         for(; i < days; i++){
19523             intDay = i - startingPos + 1;
19524             textEls[i].innerHTML = (intDay);
19525             d.setDate(d.getDate()+1);
19526             
19527             cells[i].className = ''; // "x-date-active";
19528             setCellClass(this, cells[i]);
19529         }
19530         var extraDays = 0;
19531         
19532         for(; i < 42; i++) {
19533             textEls[i].innerHTML = (++extraDays);
19534             d.setDate(d.getDate()+1);
19535             
19536             cells[i].className = "fc-future fc-other-month";
19537             setCellClass(this, cells[i]);
19538         }
19539         
19540         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19541         
19542         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19543         
19544         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19545         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19546         
19547         if(totalRows != 6){
19548             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19549             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19550         }
19551         
19552         this.fireEvent('monthchange', this, date);
19553         
19554         
19555         /*
19556         if(!this.internalRender){
19557             var main = this.el.dom.firstChild;
19558             var w = main.offsetWidth;
19559             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19560             Roo.fly(main).setWidth(w);
19561             this.internalRender = true;
19562             // opera does not respect the auto grow header center column
19563             // then, after it gets a width opera refuses to recalculate
19564             // without a second pass
19565             if(Roo.isOpera && !this.secondPass){
19566                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19567                 this.secondPass = true;
19568                 this.update.defer(10, this, [date]);
19569             }
19570         }
19571         */
19572         
19573     },
19574     
19575     findCell : function(dt) {
19576         dt = dt.clearTime().getTime();
19577         var ret = false;
19578         this.cells.each(function(c){
19579             //Roo.log("check " +c.dateValue + '?=' + dt);
19580             if(c.dateValue == dt){
19581                 ret = c;
19582                 return false;
19583             }
19584             return true;
19585         });
19586         
19587         return ret;
19588     },
19589     
19590     findCells : function(ev) {
19591         var s = ev.start.clone().clearTime().getTime();
19592        // Roo.log(s);
19593         var e= ev.end.clone().clearTime().getTime();
19594        // Roo.log(e);
19595         var ret = [];
19596         this.cells.each(function(c){
19597              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19598             
19599             if(c.dateValue > e){
19600                 return ;
19601             }
19602             if(c.dateValue < s){
19603                 return ;
19604             }
19605             ret.push(c);
19606         });
19607         
19608         return ret;    
19609     },
19610     
19611 //    findBestRow: function(cells)
19612 //    {
19613 //        var ret = 0;
19614 //        
19615 //        for (var i =0 ; i < cells.length;i++) {
19616 //            ret  = Math.max(cells[i].rows || 0,ret);
19617 //        }
19618 //        return ret;
19619 //        
19620 //    },
19621     
19622     
19623     addItem : function(ev)
19624     {
19625         // look for vertical location slot in
19626         var cells = this.findCells(ev);
19627         
19628 //        ev.row = this.findBestRow(cells);
19629         
19630         // work out the location.
19631         
19632         var crow = false;
19633         var rows = [];
19634         for(var i =0; i < cells.length; i++) {
19635             
19636             cells[i].row = cells[0].row;
19637             
19638             if(i == 0){
19639                 cells[i].row = cells[i].row + 1;
19640             }
19641             
19642             if (!crow) {
19643                 crow = {
19644                     start : cells[i],
19645                     end :  cells[i]
19646                 };
19647                 continue;
19648             }
19649             if (crow.start.getY() == cells[i].getY()) {
19650                 // on same row.
19651                 crow.end = cells[i];
19652                 continue;
19653             }
19654             // different row.
19655             rows.push(crow);
19656             crow = {
19657                 start: cells[i],
19658                 end : cells[i]
19659             };
19660             
19661         }
19662         
19663         rows.push(crow);
19664         ev.els = [];
19665         ev.rows = rows;
19666         ev.cells = cells;
19667         
19668         cells[0].events.push(ev);
19669         
19670         this.calevents.push(ev);
19671     },
19672     
19673     clearEvents: function() {
19674         
19675         if(!this.calevents){
19676             return;
19677         }
19678         
19679         Roo.each(this.cells.elements, function(c){
19680             c.row = 0;
19681             c.events = [];
19682             c.more = [];
19683         });
19684         
19685         Roo.each(this.calevents, function(e) {
19686             Roo.each(e.els, function(el) {
19687                 el.un('mouseenter' ,this.onEventEnter, this);
19688                 el.un('mouseleave' ,this.onEventLeave, this);
19689                 el.remove();
19690             },this);
19691         },this);
19692         
19693         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19694             e.remove();
19695         });
19696         
19697     },
19698     
19699     renderEvents: function()
19700     {   
19701         var _this = this;
19702         
19703         this.cells.each(function(c) {
19704             
19705             if(c.row < 5){
19706                 return;
19707             }
19708             
19709             var ev = c.events;
19710             
19711             var r = 4;
19712             if(c.row != c.events.length){
19713                 r = 4 - (4 - (c.row - c.events.length));
19714             }
19715             
19716             c.events = ev.slice(0, r);
19717             c.more = ev.slice(r);
19718             
19719             if(c.more.length && c.more.length == 1){
19720                 c.events.push(c.more.pop());
19721             }
19722             
19723             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19724             
19725         });
19726             
19727         this.cells.each(function(c) {
19728             
19729             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19730             
19731             
19732             for (var e = 0; e < c.events.length; e++){
19733                 var ev = c.events[e];
19734                 var rows = ev.rows;
19735                 
19736                 for(var i = 0; i < rows.length; i++) {
19737                 
19738                     // how many rows should it span..
19739
19740                     var  cfg = {
19741                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19742                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19743
19744                         unselectable : "on",
19745                         cn : [
19746                             {
19747                                 cls: 'fc-event-inner',
19748                                 cn : [
19749     //                                {
19750     //                                  tag:'span',
19751     //                                  cls: 'fc-event-time',
19752     //                                  html : cells.length > 1 ? '' : ev.time
19753     //                                },
19754                                     {
19755                                       tag:'span',
19756                                       cls: 'fc-event-title',
19757                                       html : String.format('{0}', ev.title)
19758                                     }
19759
19760
19761                                 ]
19762                             },
19763                             {
19764                                 cls: 'ui-resizable-handle ui-resizable-e',
19765                                 html : '&nbsp;&nbsp;&nbsp'
19766                             }
19767
19768                         ]
19769                     };
19770
19771                     if (i == 0) {
19772                         cfg.cls += ' fc-event-start';
19773                     }
19774                     if ((i+1) == rows.length) {
19775                         cfg.cls += ' fc-event-end';
19776                     }
19777
19778                     var ctr = _this.el.select('.fc-event-container',true).first();
19779                     var cg = ctr.createChild(cfg);
19780
19781                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19782                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19783
19784                     var r = (c.more.length) ? 1 : 0;
19785                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19786                     cg.setWidth(ebox.right - sbox.x -2);
19787
19788                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19789                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19790                     cg.on('click', _this.onEventClick, _this, ev);
19791
19792                     ev.els.push(cg);
19793                     
19794                 }
19795                 
19796             }
19797             
19798             
19799             if(c.more.length){
19800                 var  cfg = {
19801                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19802                     style : 'position: absolute',
19803                     unselectable : "on",
19804                     cn : [
19805                         {
19806                             cls: 'fc-event-inner',
19807                             cn : [
19808                                 {
19809                                   tag:'span',
19810                                   cls: 'fc-event-title',
19811                                   html : 'More'
19812                                 }
19813
19814
19815                             ]
19816                         },
19817                         {
19818                             cls: 'ui-resizable-handle ui-resizable-e',
19819                             html : '&nbsp;&nbsp;&nbsp'
19820                         }
19821
19822                     ]
19823                 };
19824
19825                 var ctr = _this.el.select('.fc-event-container',true).first();
19826                 var cg = ctr.createChild(cfg);
19827
19828                 var sbox = c.select('.fc-day-content',true).first().getBox();
19829                 var ebox = c.select('.fc-day-content',true).first().getBox();
19830                 //Roo.log(cg);
19831                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19832                 cg.setWidth(ebox.right - sbox.x -2);
19833
19834                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19835                 
19836             }
19837             
19838         });
19839         
19840         
19841         
19842     },
19843     
19844     onEventEnter: function (e, el,event,d) {
19845         this.fireEvent('evententer', this, el, event);
19846     },
19847     
19848     onEventLeave: function (e, el,event,d) {
19849         this.fireEvent('eventleave', this, el, event);
19850     },
19851     
19852     onEventClick: function (e, el,event,d) {
19853         this.fireEvent('eventclick', this, el, event);
19854     },
19855     
19856     onMonthChange: function () {
19857         this.store.load();
19858     },
19859     
19860     onMoreEventClick: function(e, el, more)
19861     {
19862         var _this = this;
19863         
19864         this.calpopover.placement = 'right';
19865         this.calpopover.setTitle('More');
19866         
19867         this.calpopover.setContent('');
19868         
19869         var ctr = this.calpopover.el.select('.popover-content', true).first();
19870         
19871         Roo.each(more, function(m){
19872             var cfg = {
19873                 cls : 'fc-event-hori fc-event-draggable',
19874                 html : m.title
19875             };
19876             var cg = ctr.createChild(cfg);
19877             
19878             cg.on('click', _this.onEventClick, _this, m);
19879         });
19880         
19881         this.calpopover.show(el);
19882         
19883         
19884     },
19885     
19886     onLoad: function () 
19887     {   
19888         this.calevents = [];
19889         var cal = this;
19890         
19891         if(this.store.getCount() > 0){
19892             this.store.data.each(function(d){
19893                cal.addItem({
19894                     id : d.data.id,
19895                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19896                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19897                     time : d.data.start_time,
19898                     title : d.data.title,
19899                     description : d.data.description,
19900                     venue : d.data.venue
19901                 });
19902             });
19903         }
19904         
19905         this.renderEvents();
19906         
19907         if(this.calevents.length && this.loadMask){
19908             this.maskEl.hide();
19909         }
19910     },
19911     
19912     onBeforeLoad: function()
19913     {
19914         this.clearEvents();
19915         if(this.loadMask){
19916             this.maskEl.show();
19917         }
19918     }
19919 });
19920
19921  
19922  /*
19923  * - LGPL
19924  *
19925  * element
19926  * 
19927  */
19928
19929 /**
19930  * @class Roo.bootstrap.Popover
19931  * @extends Roo.bootstrap.Component
19932  * Bootstrap Popover class
19933  * @cfg {String} html contents of the popover   (or false to use children..)
19934  * @cfg {String} title of popover (or false to hide)
19935  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19936  * @cfg {String} trigger click || hover (or false to trigger manually)
19937  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19938  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19939  *      - if false and it has a 'parent' then it will be automatically added to that element
19940  *      - if string - Roo.get  will be called 
19941  * @cfg {Number} delay - delay before showing
19942  
19943  * @constructor
19944  * Create a new Popover
19945  * @param {Object} config The config object
19946  */
19947
19948 Roo.bootstrap.Popover = function(config){
19949     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19950     
19951     this.addEvents({
19952         // raw events
19953          /**
19954          * @event show
19955          * After the popover show
19956          * 
19957          * @param {Roo.bootstrap.Popover} this
19958          */
19959         "show" : true,
19960         /**
19961          * @event hide
19962          * After the popover hide
19963          * 
19964          * @param {Roo.bootstrap.Popover} this
19965          */
19966         "hide" : true
19967     });
19968 };
19969
19970 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19971     
19972     title: false,
19973     html: false,
19974     
19975     placement : 'right',
19976     trigger : 'hover', // hover
19977     modal : false,
19978     delay : 0,
19979     
19980     over: false,
19981     
19982     can_build_overlaid : false,
19983     
19984     maskEl : false, // the mask element
19985     headerEl : false,
19986     contentEl : false,
19987     alignEl : false, // when show is called with an element - this get's stored.
19988     
19989     getChildContainer : function()
19990     {
19991         return this.contentEl;
19992         
19993     },
19994     getPopoverHeader : function()
19995     {
19996         this.title = true; // flag not to hide it..
19997         this.headerEl.addClass('p-0');
19998         return this.headerEl
19999     },
20000     
20001     
20002     getAutoCreate : function(){
20003          
20004         var cfg = {
20005            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20006            style: 'display:block',
20007            cn : [
20008                 {
20009                     cls : 'arrow'
20010                 },
20011                 {
20012                     cls : 'popover-inner ',
20013                     cn : [
20014                         {
20015                             tag: 'h3',
20016                             cls: 'popover-title popover-header',
20017                             html : this.title === false ? '' : this.title
20018                         },
20019                         {
20020                             cls : 'popover-content popover-body '  + (this.cls || ''),
20021                             html : this.html || ''
20022                         }
20023                     ]
20024                     
20025                 }
20026            ]
20027         };
20028         
20029         return cfg;
20030     },
20031     /**
20032      * @param {string} the title
20033      */
20034     setTitle: function(str)
20035     {
20036         this.title = str;
20037         if (this.el) {
20038             this.headerEl.dom.innerHTML = str;
20039         }
20040         
20041     },
20042     /**
20043      * @param {string} the body content
20044      */
20045     setContent: function(str)
20046     {
20047         this.html = str;
20048         if (this.contentEl) {
20049             this.contentEl.dom.innerHTML = str;
20050         }
20051         
20052     },
20053     // as it get's added to the bottom of the page.
20054     onRender : function(ct, position)
20055     {
20056         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20057         
20058         
20059         
20060         if(!this.el){
20061             var cfg = Roo.apply({},  this.getAutoCreate());
20062             cfg.id = Roo.id();
20063             
20064             if (this.cls) {
20065                 cfg.cls += ' ' + this.cls;
20066             }
20067             if (this.style) {
20068                 cfg.style = this.style;
20069             }
20070             //Roo.log("adding to ");
20071             this.el = Roo.get(document.body).createChild(cfg, position);
20072 //            Roo.log(this.el);
20073         }
20074         
20075         this.contentEl = this.el.select('.popover-content',true).first();
20076         this.headerEl =  this.el.select('.popover-title',true).first();
20077         
20078         var nitems = [];
20079         if(typeof(this.items) != 'undefined'){
20080             var items = this.items;
20081             delete this.items;
20082
20083             for(var i =0;i < items.length;i++) {
20084                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20085             }
20086         }
20087
20088         this.items = nitems;
20089         
20090         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20091         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20092         
20093         
20094         
20095         this.initEvents();
20096     },
20097     
20098     resizeMask : function()
20099     {
20100         this.maskEl.setSize(
20101             Roo.lib.Dom.getViewWidth(true),
20102             Roo.lib.Dom.getViewHeight(true)
20103         );
20104     },
20105     
20106     initEvents : function()
20107     {
20108         
20109         if (!this.modal) { 
20110             Roo.bootstrap.Popover.register(this);
20111         }
20112          
20113         this.arrowEl = this.el.select('.arrow',true).first();
20114         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20115         this.el.enableDisplayMode('block');
20116         this.el.hide();
20117  
20118         
20119         if (this.over === false && !this.parent()) {
20120             return; 
20121         }
20122         if (this.triggers === false) {
20123             return;
20124         }
20125          
20126         // support parent
20127         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20128         var triggers = this.trigger ? this.trigger.split(' ') : [];
20129         Roo.each(triggers, function(trigger) {
20130         
20131             if (trigger == 'click') {
20132                 on_el.on('click', this.toggle, this);
20133             } else if (trigger != 'manual') {
20134                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20135                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20136       
20137                 on_el.on(eventIn  ,this.enter, this);
20138                 on_el.on(eventOut, this.leave, this);
20139             }
20140         }, this);
20141     },
20142     
20143     
20144     // private
20145     timeout : null,
20146     hoverState : null,
20147     
20148     toggle : function () {
20149         this.hoverState == 'in' ? this.leave() : this.enter();
20150     },
20151     
20152     enter : function () {
20153         
20154         clearTimeout(this.timeout);
20155     
20156         this.hoverState = 'in';
20157     
20158         if (!this.delay || !this.delay.show) {
20159             this.show();
20160             return;
20161         }
20162         var _t = this;
20163         this.timeout = setTimeout(function () {
20164             if (_t.hoverState == 'in') {
20165                 _t.show();
20166             }
20167         }, this.delay.show)
20168     },
20169     
20170     leave : function() {
20171         clearTimeout(this.timeout);
20172     
20173         this.hoverState = 'out';
20174     
20175         if (!this.delay || !this.delay.hide) {
20176             this.hide();
20177             return;
20178         }
20179         var _t = this;
20180         this.timeout = setTimeout(function () {
20181             if (_t.hoverState == 'out') {
20182                 _t.hide();
20183             }
20184         }, this.delay.hide)
20185     },
20186     /**
20187      * Show the popover
20188      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20189      * @param {string} (left|right|top|bottom) position
20190      */
20191     show : function (on_el, placement)
20192     {
20193         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20194         on_el = on_el || false; // default to false
20195          
20196         if (!on_el) {
20197             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20198                 on_el = this.parent().el;
20199             } else if (this.over) {
20200                 Roo.get(this.over);
20201             }
20202             
20203         }
20204         
20205         this.alignEl = Roo.get( on_el );
20206
20207         if (!this.el) {
20208             this.render(document.body);
20209         }
20210         
20211         
20212          
20213         
20214         if (this.title === false) {
20215             this.headerEl.hide();
20216         }
20217         
20218        
20219         this.el.show();
20220         this.el.dom.style.display = 'block';
20221          
20222  
20223         if (this.alignEl) {
20224             this.updatePosition(this.placement, true);
20225              
20226         } else {
20227             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20228             var es = this.el.getSize();
20229             var x = Roo.lib.Dom.getViewWidth()/2;
20230             var y = Roo.lib.Dom.getViewHeight()/2;
20231             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20232             
20233         }
20234
20235         
20236         //var arrow = this.el.select('.arrow',true).first();
20237         //arrow.set(align[2], 
20238         
20239         this.el.addClass('in');
20240         
20241          
20242         
20243         this.hoverState = 'in';
20244         
20245         if (this.modal) {
20246             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20247             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20248             this.maskEl.dom.style.display = 'block';
20249             this.maskEl.addClass('show');
20250         }
20251         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20252  
20253         this.fireEvent('show', this);
20254         
20255     },
20256     /**
20257      * fire this manually after loading a grid in the table for example
20258      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20259      * @param {Boolean} try and move it if we cant get right position.
20260      */
20261     updatePosition : function(placement, try_move)
20262     {
20263         // allow for calling with no parameters
20264         placement = placement   ? placement :  this.placement;
20265         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20266         
20267         this.el.removeClass([
20268             'fade','top','bottom', 'left', 'right','in',
20269             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20270         ]);
20271         this.el.addClass(placement + ' bs-popover-' + placement);
20272         
20273         if (!this.alignEl ) {
20274             return false;
20275         }
20276         
20277         switch (placement) {
20278             case 'right':
20279                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20280                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20281                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20282                     //normal display... or moved up/down.
20283                     this.el.setXY(offset);
20284                     var xy = this.alignEl.getAnchorXY('tr', false);
20285                     xy[0]+=2;xy[1]+=5;
20286                     this.arrowEl.setXY(xy);
20287                     return true;
20288                 }
20289                 // continue through...
20290                 return this.updatePosition('left', false);
20291                 
20292             
20293             case 'left':
20294                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20295                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20296                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20297                     //normal display... or moved up/down.
20298                     this.el.setXY(offset);
20299                     var xy = this.alignEl.getAnchorXY('tl', false);
20300                     xy[0]-=10;xy[1]+=5; // << fix me
20301                     this.arrowEl.setXY(xy);
20302                     return true;
20303                 }
20304                 // call self...
20305                 return this.updatePosition('right', false);
20306             
20307             case 'top':
20308                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20309                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20310                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20311                     //normal display... or moved up/down.
20312                     this.el.setXY(offset);
20313                     var xy = this.alignEl.getAnchorXY('t', false);
20314                     xy[1]-=10; // << fix me
20315                     this.arrowEl.setXY(xy);
20316                     return true;
20317                 }
20318                 // fall through
20319                return this.updatePosition('bottom', false);
20320             
20321             case 'bottom':
20322                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20323                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20324                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20325                     //normal display... or moved up/down.
20326                     this.el.setXY(offset);
20327                     var xy = this.alignEl.getAnchorXY('b', false);
20328                      xy[1]+=2; // << fix me
20329                     this.arrowEl.setXY(xy);
20330                     return true;
20331                 }
20332                 // fall through
20333                 return this.updatePosition('top', false);
20334                 
20335             
20336         }
20337         
20338         
20339         return false;
20340     },
20341     
20342     hide : function()
20343     {
20344         this.el.setXY([0,0]);
20345         this.el.removeClass('in');
20346         this.el.hide();
20347         this.hoverState = null;
20348         this.maskEl.hide(); // always..
20349         this.fireEvent('hide', this);
20350     }
20351     
20352 });
20353
20354
20355 Roo.apply(Roo.bootstrap.Popover, {
20356
20357     alignment : {
20358         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20359         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20360         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20361         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20362     },
20363     
20364     zIndex : 20001,
20365
20366     clickHander : false,
20367     
20368
20369     onMouseDown : function(e)
20370     {
20371         if (!e.getTarget(".roo-popover")) {
20372             this.hideAll();
20373         }
20374          
20375     },
20376     
20377     popups : [],
20378     
20379     register : function(popup)
20380     {
20381         if (!Roo.bootstrap.Popover.clickHandler) {
20382             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20383         }
20384         // hide other popups.
20385         this.hideAll();
20386         this.popups.push(popup);
20387     },
20388     hideAll : function()
20389     {
20390         this.popups.forEach(function(p) {
20391             p.hide();
20392         });
20393     }
20394
20395 });/*
20396  * - LGPL
20397  *
20398  * Card header - holder for the card header elements.
20399  * 
20400  */
20401
20402 /**
20403  * @class Roo.bootstrap.PopoverNav
20404  * @extends Roo.bootstrap.NavGroup
20405  * Bootstrap Popover header navigation class
20406  * @constructor
20407  * Create a new Popover Header Navigation 
20408  * @param {Object} config The config object
20409  */
20410
20411 Roo.bootstrap.PopoverNav = function(config){
20412     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20413 };
20414
20415 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20416     
20417     
20418     container_method : 'getPopoverHeader' 
20419     
20420      
20421     
20422     
20423    
20424 });
20425
20426  
20427
20428  /*
20429  * - LGPL
20430  *
20431  * Progress
20432  * 
20433  */
20434
20435 /**
20436  * @class Roo.bootstrap.Progress
20437  * @extends Roo.bootstrap.Component
20438  * Bootstrap Progress class
20439  * @cfg {Boolean} striped striped of the progress bar
20440  * @cfg {Boolean} active animated of the progress bar
20441  * 
20442  * 
20443  * @constructor
20444  * Create a new Progress
20445  * @param {Object} config The config object
20446  */
20447
20448 Roo.bootstrap.Progress = function(config){
20449     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20450 };
20451
20452 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20453     
20454     striped : false,
20455     active: false,
20456     
20457     getAutoCreate : function(){
20458         var cfg = {
20459             tag: 'div',
20460             cls: 'progress'
20461         };
20462         
20463         
20464         if(this.striped){
20465             cfg.cls += ' progress-striped';
20466         }
20467       
20468         if(this.active){
20469             cfg.cls += ' active';
20470         }
20471         
20472         
20473         return cfg;
20474     }
20475    
20476 });
20477
20478  
20479
20480  /*
20481  * - LGPL
20482  *
20483  * ProgressBar
20484  * 
20485  */
20486
20487 /**
20488  * @class Roo.bootstrap.ProgressBar
20489  * @extends Roo.bootstrap.Component
20490  * Bootstrap ProgressBar class
20491  * @cfg {Number} aria_valuenow aria-value now
20492  * @cfg {Number} aria_valuemin aria-value min
20493  * @cfg {Number} aria_valuemax aria-value max
20494  * @cfg {String} label label for the progress bar
20495  * @cfg {String} panel (success | info | warning | danger )
20496  * @cfg {String} role role of the progress bar
20497  * @cfg {String} sr_only text
20498  * 
20499  * 
20500  * @constructor
20501  * Create a new ProgressBar
20502  * @param {Object} config The config object
20503  */
20504
20505 Roo.bootstrap.ProgressBar = function(config){
20506     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20507 };
20508
20509 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20510     
20511     aria_valuenow : 0,
20512     aria_valuemin : 0,
20513     aria_valuemax : 100,
20514     label : false,
20515     panel : false,
20516     role : false,
20517     sr_only: false,
20518     
20519     getAutoCreate : function()
20520     {
20521         
20522         var cfg = {
20523             tag: 'div',
20524             cls: 'progress-bar',
20525             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20526         };
20527         
20528         if(this.sr_only){
20529             cfg.cn = {
20530                 tag: 'span',
20531                 cls: 'sr-only',
20532                 html: this.sr_only
20533             }
20534         }
20535         
20536         if(this.role){
20537             cfg.role = this.role;
20538         }
20539         
20540         if(this.aria_valuenow){
20541             cfg['aria-valuenow'] = this.aria_valuenow;
20542         }
20543         
20544         if(this.aria_valuemin){
20545             cfg['aria-valuemin'] = this.aria_valuemin;
20546         }
20547         
20548         if(this.aria_valuemax){
20549             cfg['aria-valuemax'] = this.aria_valuemax;
20550         }
20551         
20552         if(this.label && !this.sr_only){
20553             cfg.html = this.label;
20554         }
20555         
20556         if(this.panel){
20557             cfg.cls += ' progress-bar-' + this.panel;
20558         }
20559         
20560         return cfg;
20561     },
20562     
20563     update : function(aria_valuenow)
20564     {
20565         this.aria_valuenow = aria_valuenow;
20566         
20567         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20568     }
20569    
20570 });
20571
20572  
20573
20574  /*
20575  * - LGPL
20576  *
20577  * column
20578  * 
20579  */
20580
20581 /**
20582  * @class Roo.bootstrap.TabGroup
20583  * @extends Roo.bootstrap.Column
20584  * Bootstrap Column class
20585  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20586  * @cfg {Boolean} carousel true to make the group behave like a carousel
20587  * @cfg {Boolean} bullets show bullets for the panels
20588  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20589  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20590  * @cfg {Boolean} showarrow (true|false) show arrow default true
20591  * 
20592  * @constructor
20593  * Create a new TabGroup
20594  * @param {Object} config The config object
20595  */
20596
20597 Roo.bootstrap.TabGroup = function(config){
20598     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20599     if (!this.navId) {
20600         this.navId = Roo.id();
20601     }
20602     this.tabs = [];
20603     Roo.bootstrap.TabGroup.register(this);
20604     
20605 };
20606
20607 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20608     
20609     carousel : false,
20610     transition : false,
20611     bullets : 0,
20612     timer : 0,
20613     autoslide : false,
20614     slideFn : false,
20615     slideOnTouch : false,
20616     showarrow : true,
20617     
20618     getAutoCreate : function()
20619     {
20620         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20621         
20622         cfg.cls += ' tab-content';
20623         
20624         if (this.carousel) {
20625             cfg.cls += ' carousel slide';
20626             
20627             cfg.cn = [{
20628                cls : 'carousel-inner',
20629                cn : []
20630             }];
20631         
20632             if(this.bullets  && !Roo.isTouch){
20633                 
20634                 var bullets = {
20635                     cls : 'carousel-bullets',
20636                     cn : []
20637                 };
20638                
20639                 if(this.bullets_cls){
20640                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20641                 }
20642                 
20643                 bullets.cn.push({
20644                     cls : 'clear'
20645                 });
20646                 
20647                 cfg.cn[0].cn.push(bullets);
20648             }
20649             
20650             if(this.showarrow){
20651                 cfg.cn[0].cn.push({
20652                     tag : 'div',
20653                     class : 'carousel-arrow',
20654                     cn : [
20655                         {
20656                             tag : 'div',
20657                             class : 'carousel-prev',
20658                             cn : [
20659                                 {
20660                                     tag : 'i',
20661                                     class : 'fa fa-chevron-left'
20662                                 }
20663                             ]
20664                         },
20665                         {
20666                             tag : 'div',
20667                             class : 'carousel-next',
20668                             cn : [
20669                                 {
20670                                     tag : 'i',
20671                                     class : 'fa fa-chevron-right'
20672                                 }
20673                             ]
20674                         }
20675                     ]
20676                 });
20677             }
20678             
20679         }
20680         
20681         return cfg;
20682     },
20683     
20684     initEvents:  function()
20685     {
20686 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20687 //            this.el.on("touchstart", this.onTouchStart, this);
20688 //        }
20689         
20690         if(this.autoslide){
20691             var _this = this;
20692             
20693             this.slideFn = window.setInterval(function() {
20694                 _this.showPanelNext();
20695             }, this.timer);
20696         }
20697         
20698         if(this.showarrow){
20699             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20700             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20701         }
20702         
20703         
20704     },
20705     
20706 //    onTouchStart : function(e, el, o)
20707 //    {
20708 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20709 //            return;
20710 //        }
20711 //        
20712 //        this.showPanelNext();
20713 //    },
20714     
20715     
20716     getChildContainer : function()
20717     {
20718         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20719     },
20720     
20721     /**
20722     * register a Navigation item
20723     * @param {Roo.bootstrap.NavItem} the navitem to add
20724     */
20725     register : function(item)
20726     {
20727         this.tabs.push( item);
20728         item.navId = this.navId; // not really needed..
20729         this.addBullet();
20730     
20731     },
20732     
20733     getActivePanel : function()
20734     {
20735         var r = false;
20736         Roo.each(this.tabs, function(t) {
20737             if (t.active) {
20738                 r = t;
20739                 return false;
20740             }
20741             return null;
20742         });
20743         return r;
20744         
20745     },
20746     getPanelByName : function(n)
20747     {
20748         var r = false;
20749         Roo.each(this.tabs, function(t) {
20750             if (t.tabId == n) {
20751                 r = t;
20752                 return false;
20753             }
20754             return null;
20755         });
20756         return r;
20757     },
20758     indexOfPanel : function(p)
20759     {
20760         var r = false;
20761         Roo.each(this.tabs, function(t,i) {
20762             if (t.tabId == p.tabId) {
20763                 r = i;
20764                 return false;
20765             }
20766             return null;
20767         });
20768         return r;
20769     },
20770     /**
20771      * show a specific panel
20772      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20773      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20774      */
20775     showPanel : function (pan)
20776     {
20777         if(this.transition || typeof(pan) == 'undefined'){
20778             Roo.log("waiting for the transitionend");
20779             return false;
20780         }
20781         
20782         if (typeof(pan) == 'number') {
20783             pan = this.tabs[pan];
20784         }
20785         
20786         if (typeof(pan) == 'string') {
20787             pan = this.getPanelByName(pan);
20788         }
20789         
20790         var cur = this.getActivePanel();
20791         
20792         if(!pan || !cur){
20793             Roo.log('pan or acitve pan is undefined');
20794             return false;
20795         }
20796         
20797         if (pan.tabId == this.getActivePanel().tabId) {
20798             return true;
20799         }
20800         
20801         if (false === cur.fireEvent('beforedeactivate')) {
20802             return false;
20803         }
20804         
20805         if(this.bullets > 0 && !Roo.isTouch){
20806             this.setActiveBullet(this.indexOfPanel(pan));
20807         }
20808         
20809         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20810             
20811             //class="carousel-item carousel-item-next carousel-item-left"
20812             
20813             this.transition = true;
20814             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20815             var lr = dir == 'next' ? 'left' : 'right';
20816             pan.el.addClass(dir); // or prev
20817             pan.el.addClass('carousel-item-' + dir); // or prev
20818             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20819             cur.el.addClass(lr); // or right
20820             pan.el.addClass(lr);
20821             cur.el.addClass('carousel-item-' +lr); // or right
20822             pan.el.addClass('carousel-item-' +lr);
20823             
20824             
20825             var _this = this;
20826             cur.el.on('transitionend', function() {
20827                 Roo.log("trans end?");
20828                 
20829                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20830                 pan.setActive(true);
20831                 
20832                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20833                 cur.setActive(false);
20834                 
20835                 _this.transition = false;
20836                 
20837             }, this, { single:  true } );
20838             
20839             return true;
20840         }
20841         
20842         cur.setActive(false);
20843         pan.setActive(true);
20844         
20845         return true;
20846         
20847     },
20848     showPanelNext : function()
20849     {
20850         var i = this.indexOfPanel(this.getActivePanel());
20851         
20852         if (i >= this.tabs.length - 1 && !this.autoslide) {
20853             return;
20854         }
20855         
20856         if (i >= this.tabs.length - 1 && this.autoslide) {
20857             i = -1;
20858         }
20859         
20860         this.showPanel(this.tabs[i+1]);
20861     },
20862     
20863     showPanelPrev : function()
20864     {
20865         var i = this.indexOfPanel(this.getActivePanel());
20866         
20867         if (i  < 1 && !this.autoslide) {
20868             return;
20869         }
20870         
20871         if (i < 1 && this.autoslide) {
20872             i = this.tabs.length;
20873         }
20874         
20875         this.showPanel(this.tabs[i-1]);
20876     },
20877     
20878     
20879     addBullet: function()
20880     {
20881         if(!this.bullets || Roo.isTouch){
20882             return;
20883         }
20884         var ctr = this.el.select('.carousel-bullets',true).first();
20885         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20886         var bullet = ctr.createChild({
20887             cls : 'bullet bullet-' + i
20888         },ctr.dom.lastChild);
20889         
20890         
20891         var _this = this;
20892         
20893         bullet.on('click', (function(e, el, o, ii, t){
20894
20895             e.preventDefault();
20896
20897             this.showPanel(ii);
20898
20899             if(this.autoslide && this.slideFn){
20900                 clearInterval(this.slideFn);
20901                 this.slideFn = window.setInterval(function() {
20902                     _this.showPanelNext();
20903                 }, this.timer);
20904             }
20905
20906         }).createDelegate(this, [i, bullet], true));
20907                 
20908         
20909     },
20910      
20911     setActiveBullet : function(i)
20912     {
20913         if(Roo.isTouch){
20914             return;
20915         }
20916         
20917         Roo.each(this.el.select('.bullet', true).elements, function(el){
20918             el.removeClass('selected');
20919         });
20920
20921         var bullet = this.el.select('.bullet-' + i, true).first();
20922         
20923         if(!bullet){
20924             return;
20925         }
20926         
20927         bullet.addClass('selected');
20928     }
20929     
20930     
20931   
20932 });
20933
20934  
20935
20936  
20937  
20938 Roo.apply(Roo.bootstrap.TabGroup, {
20939     
20940     groups: {},
20941      /**
20942     * register a Navigation Group
20943     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20944     */
20945     register : function(navgrp)
20946     {
20947         this.groups[navgrp.navId] = navgrp;
20948         
20949     },
20950     /**
20951     * fetch a Navigation Group based on the navigation ID
20952     * if one does not exist , it will get created.
20953     * @param {string} the navgroup to add
20954     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20955     */
20956     get: function(navId) {
20957         if (typeof(this.groups[navId]) == 'undefined') {
20958             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20959         }
20960         return this.groups[navId] ;
20961     }
20962     
20963     
20964     
20965 });
20966
20967  /*
20968  * - LGPL
20969  *
20970  * TabPanel
20971  * 
20972  */
20973
20974 /**
20975  * @class Roo.bootstrap.TabPanel
20976  * @extends Roo.bootstrap.Component
20977  * Bootstrap TabPanel class
20978  * @cfg {Boolean} active panel active
20979  * @cfg {String} html panel content
20980  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20981  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20982  * @cfg {String} href click to link..
20983  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20984  * 
20985  * 
20986  * @constructor
20987  * Create a new TabPanel
20988  * @param {Object} config The config object
20989  */
20990
20991 Roo.bootstrap.TabPanel = function(config){
20992     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20993     this.addEvents({
20994         /**
20995              * @event changed
20996              * Fires when the active status changes
20997              * @param {Roo.bootstrap.TabPanel} this
20998              * @param {Boolean} state the new state
20999             
21000          */
21001         'changed': true,
21002         /**
21003              * @event beforedeactivate
21004              * Fires before a tab is de-activated - can be used to do validation on a form.
21005              * @param {Roo.bootstrap.TabPanel} this
21006              * @return {Boolean} false if there is an error
21007             
21008          */
21009         'beforedeactivate': true
21010      });
21011     
21012     this.tabId = this.tabId || Roo.id();
21013   
21014 };
21015
21016 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21017     
21018     active: false,
21019     html: false,
21020     tabId: false,
21021     navId : false,
21022     href : '',
21023     touchSlide : false,
21024     getAutoCreate : function(){
21025         
21026         
21027         var cfg = {
21028             tag: 'div',
21029             // item is needed for carousel - not sure if it has any effect otherwise
21030             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21031             html: this.html || ''
21032         };
21033         
21034         if(this.active){
21035             cfg.cls += ' active';
21036         }
21037         
21038         if(this.tabId){
21039             cfg.tabId = this.tabId;
21040         }
21041         
21042         
21043         
21044         return cfg;
21045     },
21046     
21047     initEvents:  function()
21048     {
21049         var p = this.parent();
21050         
21051         this.navId = this.navId || p.navId;
21052         
21053         if (typeof(this.navId) != 'undefined') {
21054             // not really needed.. but just in case.. parent should be a NavGroup.
21055             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21056             
21057             tg.register(this);
21058             
21059             var i = tg.tabs.length - 1;
21060             
21061             if(this.active && tg.bullets > 0 && i < tg.bullets){
21062                 tg.setActiveBullet(i);
21063             }
21064         }
21065         
21066         this.el.on('click', this.onClick, this);
21067         
21068         if(Roo.isTouch && this.touchSlide){
21069             this.el.on("touchstart", this.onTouchStart, this);
21070             this.el.on("touchmove", this.onTouchMove, this);
21071             this.el.on("touchend", this.onTouchEnd, this);
21072         }
21073         
21074     },
21075     
21076     onRender : function(ct, position)
21077     {
21078         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21079     },
21080     
21081     setActive : function(state)
21082     {
21083         Roo.log("panel - set active " + this.tabId + "=" + state);
21084         
21085         this.active = state;
21086         if (!state) {
21087             this.el.removeClass('active');
21088             
21089         } else  if (!this.el.hasClass('active')) {
21090             this.el.addClass('active');
21091         }
21092         
21093         this.fireEvent('changed', this, state);
21094     },
21095     
21096     onClick : function(e)
21097     {
21098         e.preventDefault();
21099         
21100         if(!this.href.length){
21101             return;
21102         }
21103         
21104         window.location.href = this.href;
21105     },
21106     
21107     startX : 0,
21108     startY : 0,
21109     endX : 0,
21110     endY : 0,
21111     swiping : false,
21112     
21113     onTouchStart : function(e)
21114     {
21115         this.swiping = false;
21116         
21117         this.startX = e.browserEvent.touches[0].clientX;
21118         this.startY = e.browserEvent.touches[0].clientY;
21119     },
21120     
21121     onTouchMove : function(e)
21122     {
21123         this.swiping = true;
21124         
21125         this.endX = e.browserEvent.touches[0].clientX;
21126         this.endY = e.browserEvent.touches[0].clientY;
21127     },
21128     
21129     onTouchEnd : function(e)
21130     {
21131         if(!this.swiping){
21132             this.onClick(e);
21133             return;
21134         }
21135         
21136         var tabGroup = this.parent();
21137         
21138         if(this.endX > this.startX){ // swiping right
21139             tabGroup.showPanelPrev();
21140             return;
21141         }
21142         
21143         if(this.startX > this.endX){ // swiping left
21144             tabGroup.showPanelNext();
21145             return;
21146         }
21147     }
21148     
21149     
21150 });
21151  
21152
21153  
21154
21155  /*
21156  * - LGPL
21157  *
21158  * DateField
21159  * 
21160  */
21161
21162 /**
21163  * @class Roo.bootstrap.DateField
21164  * @extends Roo.bootstrap.Input
21165  * Bootstrap DateField class
21166  * @cfg {Number} weekStart default 0
21167  * @cfg {String} viewMode default empty, (months|years)
21168  * @cfg {String} minViewMode default empty, (months|years)
21169  * @cfg {Number} startDate default -Infinity
21170  * @cfg {Number} endDate default Infinity
21171  * @cfg {Boolean} todayHighlight default false
21172  * @cfg {Boolean} todayBtn default false
21173  * @cfg {Boolean} calendarWeeks default false
21174  * @cfg {Object} daysOfWeekDisabled default empty
21175  * @cfg {Boolean} singleMode default false (true | false)
21176  * 
21177  * @cfg {Boolean} keyboardNavigation default true
21178  * @cfg {String} language default en
21179  * 
21180  * @constructor
21181  * Create a new DateField
21182  * @param {Object} config The config object
21183  */
21184
21185 Roo.bootstrap.DateField = function(config){
21186     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21187      this.addEvents({
21188             /**
21189              * @event show
21190              * Fires when this field show.
21191              * @param {Roo.bootstrap.DateField} this
21192              * @param {Mixed} date The date value
21193              */
21194             show : true,
21195             /**
21196              * @event show
21197              * Fires when this field hide.
21198              * @param {Roo.bootstrap.DateField} this
21199              * @param {Mixed} date The date value
21200              */
21201             hide : true,
21202             /**
21203              * @event select
21204              * Fires when select a date.
21205              * @param {Roo.bootstrap.DateField} this
21206              * @param {Mixed} date The date value
21207              */
21208             select : true,
21209             /**
21210              * @event beforeselect
21211              * Fires when before select a date.
21212              * @param {Roo.bootstrap.DateField} this
21213              * @param {Mixed} date The date value
21214              */
21215             beforeselect : true
21216         });
21217 };
21218
21219 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21220     
21221     /**
21222      * @cfg {String} format
21223      * The default date format string which can be overriden for localization support.  The format must be
21224      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21225      */
21226     format : "m/d/y",
21227     /**
21228      * @cfg {String} altFormats
21229      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21230      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21231      */
21232     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21233     
21234     weekStart : 0,
21235     
21236     viewMode : '',
21237     
21238     minViewMode : '',
21239     
21240     todayHighlight : false,
21241     
21242     todayBtn: false,
21243     
21244     language: 'en',
21245     
21246     keyboardNavigation: true,
21247     
21248     calendarWeeks: false,
21249     
21250     startDate: -Infinity,
21251     
21252     endDate: Infinity,
21253     
21254     daysOfWeekDisabled: [],
21255     
21256     _events: [],
21257     
21258     singleMode : false,
21259     
21260     UTCDate: function()
21261     {
21262         return new Date(Date.UTC.apply(Date, arguments));
21263     },
21264     
21265     UTCToday: function()
21266     {
21267         var today = new Date();
21268         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21269     },
21270     
21271     getDate: function() {
21272             var d = this.getUTCDate();
21273             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21274     },
21275     
21276     getUTCDate: function() {
21277             return this.date;
21278     },
21279     
21280     setDate: function(d) {
21281             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21282     },
21283     
21284     setUTCDate: function(d) {
21285             this.date = d;
21286             this.setValue(this.formatDate(this.date));
21287     },
21288         
21289     onRender: function(ct, position)
21290     {
21291         
21292         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21293         
21294         this.language = this.language || 'en';
21295         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21296         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21297         
21298         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21299         this.format = this.format || 'm/d/y';
21300         this.isInline = false;
21301         this.isInput = true;
21302         this.component = this.el.select('.add-on', true).first() || false;
21303         this.component = (this.component && this.component.length === 0) ? false : this.component;
21304         this.hasInput = this.component && this.inputEl().length;
21305         
21306         if (typeof(this.minViewMode === 'string')) {
21307             switch (this.minViewMode) {
21308                 case 'months':
21309                     this.minViewMode = 1;
21310                     break;
21311                 case 'years':
21312                     this.minViewMode = 2;
21313                     break;
21314                 default:
21315                     this.minViewMode = 0;
21316                     break;
21317             }
21318         }
21319         
21320         if (typeof(this.viewMode === 'string')) {
21321             switch (this.viewMode) {
21322                 case 'months':
21323                     this.viewMode = 1;
21324                     break;
21325                 case 'years':
21326                     this.viewMode = 2;
21327                     break;
21328                 default:
21329                     this.viewMode = 0;
21330                     break;
21331             }
21332         }
21333                 
21334         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21335         
21336 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21337         
21338         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21339         
21340         this.picker().on('mousedown', this.onMousedown, this);
21341         this.picker().on('click', this.onClick, this);
21342         
21343         this.picker().addClass('datepicker-dropdown');
21344         
21345         this.startViewMode = this.viewMode;
21346         
21347         if(this.singleMode){
21348             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21349                 v.setVisibilityMode(Roo.Element.DISPLAY);
21350                 v.hide();
21351             });
21352             
21353             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21354                 v.setStyle('width', '189px');
21355             });
21356         }
21357         
21358         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21359             if(!this.calendarWeeks){
21360                 v.remove();
21361                 return;
21362             }
21363             
21364             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21365             v.attr('colspan', function(i, val){
21366                 return parseInt(val) + 1;
21367             });
21368         });
21369                         
21370         
21371         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21372         
21373         this.setStartDate(this.startDate);
21374         this.setEndDate(this.endDate);
21375         
21376         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21377         
21378         this.fillDow();
21379         this.fillMonths();
21380         this.update();
21381         this.showMode();
21382         
21383         if(this.isInline) {
21384             this.showPopup();
21385         }
21386     },
21387     
21388     picker : function()
21389     {
21390         return this.pickerEl;
21391 //        return this.el.select('.datepicker', true).first();
21392     },
21393     
21394     fillDow: function()
21395     {
21396         var dowCnt = this.weekStart;
21397         
21398         var dow = {
21399             tag: 'tr',
21400             cn: [
21401                 
21402             ]
21403         };
21404         
21405         if(this.calendarWeeks){
21406             dow.cn.push({
21407                 tag: 'th',
21408                 cls: 'cw',
21409                 html: '&nbsp;'
21410             })
21411         }
21412         
21413         while (dowCnt < this.weekStart + 7) {
21414             dow.cn.push({
21415                 tag: 'th',
21416                 cls: 'dow',
21417                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21418             });
21419         }
21420         
21421         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21422     },
21423     
21424     fillMonths: function()
21425     {    
21426         var i = 0;
21427         var months = this.picker().select('>.datepicker-months td', true).first();
21428         
21429         months.dom.innerHTML = '';
21430         
21431         while (i < 12) {
21432             var month = {
21433                 tag: 'span',
21434                 cls: 'month',
21435                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21436             };
21437             
21438             months.createChild(month);
21439         }
21440         
21441     },
21442     
21443     update: function()
21444     {
21445         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;
21446         
21447         if (this.date < this.startDate) {
21448             this.viewDate = new Date(this.startDate);
21449         } else if (this.date > this.endDate) {
21450             this.viewDate = new Date(this.endDate);
21451         } else {
21452             this.viewDate = new Date(this.date);
21453         }
21454         
21455         this.fill();
21456     },
21457     
21458     fill: function() 
21459     {
21460         var d = new Date(this.viewDate),
21461                 year = d.getUTCFullYear(),
21462                 month = d.getUTCMonth(),
21463                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21464                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21465                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21466                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21467                 currentDate = this.date && this.date.valueOf(),
21468                 today = this.UTCToday();
21469         
21470         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21471         
21472 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21473         
21474 //        this.picker.select('>tfoot th.today').
21475 //                                              .text(dates[this.language].today)
21476 //                                              .toggle(this.todayBtn !== false);
21477     
21478         this.updateNavArrows();
21479         this.fillMonths();
21480                                                 
21481         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21482         
21483         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21484          
21485         prevMonth.setUTCDate(day);
21486         
21487         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21488         
21489         var nextMonth = new Date(prevMonth);
21490         
21491         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21492         
21493         nextMonth = nextMonth.valueOf();
21494         
21495         var fillMonths = false;
21496         
21497         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21498         
21499         while(prevMonth.valueOf() <= nextMonth) {
21500             var clsName = '';
21501             
21502             if (prevMonth.getUTCDay() === this.weekStart) {
21503                 if(fillMonths){
21504                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21505                 }
21506                     
21507                 fillMonths = {
21508                     tag: 'tr',
21509                     cn: []
21510                 };
21511                 
21512                 if(this.calendarWeeks){
21513                     // ISO 8601: First week contains first thursday.
21514                     // ISO also states week starts on Monday, but we can be more abstract here.
21515                     var
21516                     // Start of current week: based on weekstart/current date
21517                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21518                     // Thursday of this week
21519                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21520                     // First Thursday of year, year from thursday
21521                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21522                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21523                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21524                     
21525                     fillMonths.cn.push({
21526                         tag: 'td',
21527                         cls: 'cw',
21528                         html: calWeek
21529                     });
21530                 }
21531             }
21532             
21533             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21534                 clsName += ' old';
21535             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21536                 clsName += ' new';
21537             }
21538             if (this.todayHighlight &&
21539                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21540                 prevMonth.getUTCMonth() == today.getMonth() &&
21541                 prevMonth.getUTCDate() == today.getDate()) {
21542                 clsName += ' today';
21543             }
21544             
21545             if (currentDate && prevMonth.valueOf() === currentDate) {
21546                 clsName += ' active';
21547             }
21548             
21549             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21550                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21551                     clsName += ' disabled';
21552             }
21553             
21554             fillMonths.cn.push({
21555                 tag: 'td',
21556                 cls: 'day ' + clsName,
21557                 html: prevMonth.getDate()
21558             });
21559             
21560             prevMonth.setDate(prevMonth.getDate()+1);
21561         }
21562           
21563         var currentYear = this.date && this.date.getUTCFullYear();
21564         var currentMonth = this.date && this.date.getUTCMonth();
21565         
21566         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21567         
21568         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21569             v.removeClass('active');
21570             
21571             if(currentYear === year && k === currentMonth){
21572                 v.addClass('active');
21573             }
21574             
21575             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21576                 v.addClass('disabled');
21577             }
21578             
21579         });
21580         
21581         
21582         year = parseInt(year/10, 10) * 10;
21583         
21584         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21585         
21586         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21587         
21588         year -= 1;
21589         for (var i = -1; i < 11; i++) {
21590             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21591                 tag: 'span',
21592                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21593                 html: year
21594             });
21595             
21596             year += 1;
21597         }
21598     },
21599     
21600     showMode: function(dir) 
21601     {
21602         if (dir) {
21603             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21604         }
21605         
21606         Roo.each(this.picker().select('>div',true).elements, function(v){
21607             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21608             v.hide();
21609         });
21610         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21611     },
21612     
21613     place: function()
21614     {
21615         if(this.isInline) {
21616             return;
21617         }
21618         
21619         this.picker().removeClass(['bottom', 'top']);
21620         
21621         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21622             /*
21623              * place to the top of element!
21624              *
21625              */
21626             
21627             this.picker().addClass('top');
21628             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21629             
21630             return;
21631         }
21632         
21633         this.picker().addClass('bottom');
21634         
21635         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21636     },
21637     
21638     parseDate : function(value)
21639     {
21640         if(!value || value instanceof Date){
21641             return value;
21642         }
21643         var v = Date.parseDate(value, this.format);
21644         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21645             v = Date.parseDate(value, 'Y-m-d');
21646         }
21647         if(!v && this.altFormats){
21648             if(!this.altFormatsArray){
21649                 this.altFormatsArray = this.altFormats.split("|");
21650             }
21651             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21652                 v = Date.parseDate(value, this.altFormatsArray[i]);
21653             }
21654         }
21655         return v;
21656     },
21657     
21658     formatDate : function(date, fmt)
21659     {   
21660         return (!date || !(date instanceof Date)) ?
21661         date : date.dateFormat(fmt || this.format);
21662     },
21663     
21664     onFocus : function()
21665     {
21666         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21667         this.showPopup();
21668     },
21669     
21670     onBlur : function()
21671     {
21672         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21673         
21674         var d = this.inputEl().getValue();
21675         
21676         this.setValue(d);
21677                 
21678         this.hidePopup();
21679     },
21680     
21681     showPopup : function()
21682     {
21683         this.picker().show();
21684         this.update();
21685         this.place();
21686         
21687         this.fireEvent('showpopup', this, this.date);
21688     },
21689     
21690     hidePopup : function()
21691     {
21692         if(this.isInline) {
21693             return;
21694         }
21695         this.picker().hide();
21696         this.viewMode = this.startViewMode;
21697         this.showMode();
21698         
21699         this.fireEvent('hidepopup', this, this.date);
21700         
21701     },
21702     
21703     onMousedown: function(e)
21704     {
21705         e.stopPropagation();
21706         e.preventDefault();
21707     },
21708     
21709     keyup: function(e)
21710     {
21711         Roo.bootstrap.DateField.superclass.keyup.call(this);
21712         this.update();
21713     },
21714
21715     setValue: function(v)
21716     {
21717         if(this.fireEvent('beforeselect', this, v) !== false){
21718             var d = new Date(this.parseDate(v) ).clearTime();
21719         
21720             if(isNaN(d.getTime())){
21721                 this.date = this.viewDate = '';
21722                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21723                 return;
21724             }
21725
21726             v = this.formatDate(d);
21727
21728             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21729
21730             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21731
21732             this.update();
21733
21734             this.fireEvent('select', this, this.date);
21735         }
21736     },
21737     
21738     getValue: function()
21739     {
21740         return this.formatDate(this.date);
21741     },
21742     
21743     fireKey: function(e)
21744     {
21745         if (!this.picker().isVisible()){
21746             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21747                 this.showPopup();
21748             }
21749             return;
21750         }
21751         
21752         var dateChanged = false,
21753         dir, day, month,
21754         newDate, newViewDate;
21755         
21756         switch(e.keyCode){
21757             case 27: // escape
21758                 this.hidePopup();
21759                 e.preventDefault();
21760                 break;
21761             case 37: // left
21762             case 39: // right
21763                 if (!this.keyboardNavigation) {
21764                     break;
21765                 }
21766                 dir = e.keyCode == 37 ? -1 : 1;
21767                 
21768                 if (e.ctrlKey){
21769                     newDate = this.moveYear(this.date, dir);
21770                     newViewDate = this.moveYear(this.viewDate, dir);
21771                 } else if (e.shiftKey){
21772                     newDate = this.moveMonth(this.date, dir);
21773                     newViewDate = this.moveMonth(this.viewDate, dir);
21774                 } else {
21775                     newDate = new Date(this.date);
21776                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21777                     newViewDate = new Date(this.viewDate);
21778                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21779                 }
21780                 if (this.dateWithinRange(newDate)){
21781                     this.date = newDate;
21782                     this.viewDate = newViewDate;
21783                     this.setValue(this.formatDate(this.date));
21784 //                    this.update();
21785                     e.preventDefault();
21786                     dateChanged = true;
21787                 }
21788                 break;
21789             case 38: // up
21790             case 40: // down
21791                 if (!this.keyboardNavigation) {
21792                     break;
21793                 }
21794                 dir = e.keyCode == 38 ? -1 : 1;
21795                 if (e.ctrlKey){
21796                     newDate = this.moveYear(this.date, dir);
21797                     newViewDate = this.moveYear(this.viewDate, dir);
21798                 } else if (e.shiftKey){
21799                     newDate = this.moveMonth(this.date, dir);
21800                     newViewDate = this.moveMonth(this.viewDate, dir);
21801                 } else {
21802                     newDate = new Date(this.date);
21803                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21804                     newViewDate = new Date(this.viewDate);
21805                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21806                 }
21807                 if (this.dateWithinRange(newDate)){
21808                     this.date = newDate;
21809                     this.viewDate = newViewDate;
21810                     this.setValue(this.formatDate(this.date));
21811 //                    this.update();
21812                     e.preventDefault();
21813                     dateChanged = true;
21814                 }
21815                 break;
21816             case 13: // enter
21817                 this.setValue(this.formatDate(this.date));
21818                 this.hidePopup();
21819                 e.preventDefault();
21820                 break;
21821             case 9: // tab
21822                 this.setValue(this.formatDate(this.date));
21823                 this.hidePopup();
21824                 break;
21825             case 16: // shift
21826             case 17: // ctrl
21827             case 18: // alt
21828                 break;
21829             default :
21830                 this.hidePopup();
21831                 
21832         }
21833     },
21834     
21835     
21836     onClick: function(e) 
21837     {
21838         e.stopPropagation();
21839         e.preventDefault();
21840         
21841         var target = e.getTarget();
21842         
21843         if(target.nodeName.toLowerCase() === 'i'){
21844             target = Roo.get(target).dom.parentNode;
21845         }
21846         
21847         var nodeName = target.nodeName;
21848         var className = target.className;
21849         var html = target.innerHTML;
21850         //Roo.log(nodeName);
21851         
21852         switch(nodeName.toLowerCase()) {
21853             case 'th':
21854                 switch(className) {
21855                     case 'switch':
21856                         this.showMode(1);
21857                         break;
21858                     case 'prev':
21859                     case 'next':
21860                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21861                         switch(this.viewMode){
21862                                 case 0:
21863                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21864                                         break;
21865                                 case 1:
21866                                 case 2:
21867                                         this.viewDate = this.moveYear(this.viewDate, dir);
21868                                         break;
21869                         }
21870                         this.fill();
21871                         break;
21872                     case 'today':
21873                         var date = new Date();
21874                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21875 //                        this.fill()
21876                         this.setValue(this.formatDate(this.date));
21877                         
21878                         this.hidePopup();
21879                         break;
21880                 }
21881                 break;
21882             case 'span':
21883                 if (className.indexOf('disabled') < 0) {
21884                     this.viewDate.setUTCDate(1);
21885                     if (className.indexOf('month') > -1) {
21886                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21887                     } else {
21888                         var year = parseInt(html, 10) || 0;
21889                         this.viewDate.setUTCFullYear(year);
21890                         
21891                     }
21892                     
21893                     if(this.singleMode){
21894                         this.setValue(this.formatDate(this.viewDate));
21895                         this.hidePopup();
21896                         return;
21897                     }
21898                     
21899                     this.showMode(-1);
21900                     this.fill();
21901                 }
21902                 break;
21903                 
21904             case 'td':
21905                 //Roo.log(className);
21906                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21907                     var day = parseInt(html, 10) || 1;
21908                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21909                         month = (this.viewDate || new Date()).getUTCMonth();
21910
21911                     if (className.indexOf('old') > -1) {
21912                         if(month === 0 ){
21913                             month = 11;
21914                             year -= 1;
21915                         }else{
21916                             month -= 1;
21917                         }
21918                     } else if (className.indexOf('new') > -1) {
21919                         if (month == 11) {
21920                             month = 0;
21921                             year += 1;
21922                         } else {
21923                             month += 1;
21924                         }
21925                     }
21926                     //Roo.log([year,month,day]);
21927                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21928                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21929 //                    this.fill();
21930                     //Roo.log(this.formatDate(this.date));
21931                     this.setValue(this.formatDate(this.date));
21932                     this.hidePopup();
21933                 }
21934                 break;
21935         }
21936     },
21937     
21938     setStartDate: function(startDate)
21939     {
21940         this.startDate = startDate || -Infinity;
21941         if (this.startDate !== -Infinity) {
21942             this.startDate = this.parseDate(this.startDate);
21943         }
21944         this.update();
21945         this.updateNavArrows();
21946     },
21947
21948     setEndDate: function(endDate)
21949     {
21950         this.endDate = endDate || Infinity;
21951         if (this.endDate !== Infinity) {
21952             this.endDate = this.parseDate(this.endDate);
21953         }
21954         this.update();
21955         this.updateNavArrows();
21956     },
21957     
21958     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21959     {
21960         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21961         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21962             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21963         }
21964         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21965             return parseInt(d, 10);
21966         });
21967         this.update();
21968         this.updateNavArrows();
21969     },
21970     
21971     updateNavArrows: function() 
21972     {
21973         if(this.singleMode){
21974             return;
21975         }
21976         
21977         var d = new Date(this.viewDate),
21978         year = d.getUTCFullYear(),
21979         month = d.getUTCMonth();
21980         
21981         Roo.each(this.picker().select('.prev', true).elements, function(v){
21982             v.show();
21983             switch (this.viewMode) {
21984                 case 0:
21985
21986                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21987                         v.hide();
21988                     }
21989                     break;
21990                 case 1:
21991                 case 2:
21992                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21993                         v.hide();
21994                     }
21995                     break;
21996             }
21997         });
21998         
21999         Roo.each(this.picker().select('.next', true).elements, function(v){
22000             v.show();
22001             switch (this.viewMode) {
22002                 case 0:
22003
22004                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22005                         v.hide();
22006                     }
22007                     break;
22008                 case 1:
22009                 case 2:
22010                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22011                         v.hide();
22012                     }
22013                     break;
22014             }
22015         })
22016     },
22017     
22018     moveMonth: function(date, dir)
22019     {
22020         if (!dir) {
22021             return date;
22022         }
22023         var new_date = new Date(date.valueOf()),
22024         day = new_date.getUTCDate(),
22025         month = new_date.getUTCMonth(),
22026         mag = Math.abs(dir),
22027         new_month, test;
22028         dir = dir > 0 ? 1 : -1;
22029         if (mag == 1){
22030             test = dir == -1
22031             // If going back one month, make sure month is not current month
22032             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22033             ? function(){
22034                 return new_date.getUTCMonth() == month;
22035             }
22036             // If going forward one month, make sure month is as expected
22037             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22038             : function(){
22039                 return new_date.getUTCMonth() != new_month;
22040             };
22041             new_month = month + dir;
22042             new_date.setUTCMonth(new_month);
22043             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22044             if (new_month < 0 || new_month > 11) {
22045                 new_month = (new_month + 12) % 12;
22046             }
22047         } else {
22048             // For magnitudes >1, move one month at a time...
22049             for (var i=0; i<mag; i++) {
22050                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22051                 new_date = this.moveMonth(new_date, dir);
22052             }
22053             // ...then reset the day, keeping it in the new month
22054             new_month = new_date.getUTCMonth();
22055             new_date.setUTCDate(day);
22056             test = function(){
22057                 return new_month != new_date.getUTCMonth();
22058             };
22059         }
22060         // Common date-resetting loop -- if date is beyond end of month, make it
22061         // end of month
22062         while (test()){
22063             new_date.setUTCDate(--day);
22064             new_date.setUTCMonth(new_month);
22065         }
22066         return new_date;
22067     },
22068
22069     moveYear: function(date, dir)
22070     {
22071         return this.moveMonth(date, dir*12);
22072     },
22073
22074     dateWithinRange: function(date)
22075     {
22076         return date >= this.startDate && date <= this.endDate;
22077     },
22078
22079     
22080     remove: function() 
22081     {
22082         this.picker().remove();
22083     },
22084     
22085     validateValue : function(value)
22086     {
22087         if(this.getVisibilityEl().hasClass('hidden')){
22088             return true;
22089         }
22090         
22091         if(value.length < 1)  {
22092             if(this.allowBlank){
22093                 return true;
22094             }
22095             return false;
22096         }
22097         
22098         if(value.length < this.minLength){
22099             return false;
22100         }
22101         if(value.length > this.maxLength){
22102             return false;
22103         }
22104         if(this.vtype){
22105             var vt = Roo.form.VTypes;
22106             if(!vt[this.vtype](value, this)){
22107                 return false;
22108             }
22109         }
22110         if(typeof this.validator == "function"){
22111             var msg = this.validator(value);
22112             if(msg !== true){
22113                 return false;
22114             }
22115         }
22116         
22117         if(this.regex && !this.regex.test(value)){
22118             return false;
22119         }
22120         
22121         if(typeof(this.parseDate(value)) == 'undefined'){
22122             return false;
22123         }
22124         
22125         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22126             return false;
22127         }      
22128         
22129         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22130             return false;
22131         } 
22132         
22133         
22134         return true;
22135     },
22136     
22137     reset : function()
22138     {
22139         this.date = this.viewDate = '';
22140         
22141         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22142     }
22143    
22144 });
22145
22146 Roo.apply(Roo.bootstrap.DateField,  {
22147     
22148     head : {
22149         tag: 'thead',
22150         cn: [
22151         {
22152             tag: 'tr',
22153             cn: [
22154             {
22155                 tag: 'th',
22156                 cls: 'prev',
22157                 html: '<i class="fa fa-arrow-left"/>'
22158             },
22159             {
22160                 tag: 'th',
22161                 cls: 'switch',
22162                 colspan: '5'
22163             },
22164             {
22165                 tag: 'th',
22166                 cls: 'next',
22167                 html: '<i class="fa fa-arrow-right"/>'
22168             }
22169
22170             ]
22171         }
22172         ]
22173     },
22174     
22175     content : {
22176         tag: 'tbody',
22177         cn: [
22178         {
22179             tag: 'tr',
22180             cn: [
22181             {
22182                 tag: 'td',
22183                 colspan: '7'
22184             }
22185             ]
22186         }
22187         ]
22188     },
22189     
22190     footer : {
22191         tag: 'tfoot',
22192         cn: [
22193         {
22194             tag: 'tr',
22195             cn: [
22196             {
22197                 tag: 'th',
22198                 colspan: '7',
22199                 cls: 'today'
22200             }
22201                     
22202             ]
22203         }
22204         ]
22205     },
22206     
22207     dates:{
22208         en: {
22209             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22210             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22211             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22212             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22213             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22214             today: "Today"
22215         }
22216     },
22217     
22218     modes: [
22219     {
22220         clsName: 'days',
22221         navFnc: 'Month',
22222         navStep: 1
22223     },
22224     {
22225         clsName: 'months',
22226         navFnc: 'FullYear',
22227         navStep: 1
22228     },
22229     {
22230         clsName: 'years',
22231         navFnc: 'FullYear',
22232         navStep: 10
22233     }]
22234 });
22235
22236 Roo.apply(Roo.bootstrap.DateField,  {
22237   
22238     template : {
22239         tag: 'div',
22240         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22241         cn: [
22242         {
22243             tag: 'div',
22244             cls: 'datepicker-days',
22245             cn: [
22246             {
22247                 tag: 'table',
22248                 cls: 'table-condensed',
22249                 cn:[
22250                 Roo.bootstrap.DateField.head,
22251                 {
22252                     tag: 'tbody'
22253                 },
22254                 Roo.bootstrap.DateField.footer
22255                 ]
22256             }
22257             ]
22258         },
22259         {
22260             tag: 'div',
22261             cls: 'datepicker-months',
22262             cn: [
22263             {
22264                 tag: 'table',
22265                 cls: 'table-condensed',
22266                 cn:[
22267                 Roo.bootstrap.DateField.head,
22268                 Roo.bootstrap.DateField.content,
22269                 Roo.bootstrap.DateField.footer
22270                 ]
22271             }
22272             ]
22273         },
22274         {
22275             tag: 'div',
22276             cls: 'datepicker-years',
22277             cn: [
22278             {
22279                 tag: 'table',
22280                 cls: 'table-condensed',
22281                 cn:[
22282                 Roo.bootstrap.DateField.head,
22283                 Roo.bootstrap.DateField.content,
22284                 Roo.bootstrap.DateField.footer
22285                 ]
22286             }
22287             ]
22288         }
22289         ]
22290     }
22291 });
22292
22293  
22294
22295  /*
22296  * - LGPL
22297  *
22298  * TimeField
22299  * 
22300  */
22301
22302 /**
22303  * @class Roo.bootstrap.TimeField
22304  * @extends Roo.bootstrap.Input
22305  * Bootstrap DateField class
22306  * 
22307  * 
22308  * @constructor
22309  * Create a new TimeField
22310  * @param {Object} config The config object
22311  */
22312
22313 Roo.bootstrap.TimeField = function(config){
22314     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22315     this.addEvents({
22316             /**
22317              * @event show
22318              * Fires when this field show.
22319              * @param {Roo.bootstrap.DateField} thisthis
22320              * @param {Mixed} date The date value
22321              */
22322             show : true,
22323             /**
22324              * @event show
22325              * Fires when this field hide.
22326              * @param {Roo.bootstrap.DateField} this
22327              * @param {Mixed} date The date value
22328              */
22329             hide : true,
22330             /**
22331              * @event select
22332              * Fires when select a date.
22333              * @param {Roo.bootstrap.DateField} this
22334              * @param {Mixed} date The date value
22335              */
22336             select : true
22337         });
22338 };
22339
22340 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22341     
22342     /**
22343      * @cfg {String} format
22344      * The default time format string which can be overriden for localization support.  The format must be
22345      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22346      */
22347     format : "H:i",
22348
22349     getAutoCreate : function()
22350     {
22351         this.after = '<i class="fa far fa-clock"></i>';
22352         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22353         
22354          
22355     },
22356     onRender: function(ct, position)
22357     {
22358         
22359         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22360                 
22361         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22362         
22363         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22364         
22365         this.pop = this.picker().select('>.datepicker-time',true).first();
22366         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22367         
22368         this.picker().on('mousedown', this.onMousedown, this);
22369         this.picker().on('click', this.onClick, this);
22370         
22371         this.picker().addClass('datepicker-dropdown');
22372     
22373         this.fillTime();
22374         this.update();
22375             
22376         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22377         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22378         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22379         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22380         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22381         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22382
22383     },
22384     
22385     fireKey: function(e){
22386         if (!this.picker().isVisible()){
22387             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22388                 this.show();
22389             }
22390             return;
22391         }
22392
22393         e.preventDefault();
22394         
22395         switch(e.keyCode){
22396             case 27: // escape
22397                 this.hide();
22398                 break;
22399             case 37: // left
22400             case 39: // right
22401                 this.onTogglePeriod();
22402                 break;
22403             case 38: // up
22404                 this.onIncrementMinutes();
22405                 break;
22406             case 40: // down
22407                 this.onDecrementMinutes();
22408                 break;
22409             case 13: // enter
22410             case 9: // tab
22411                 this.setTime();
22412                 break;
22413         }
22414     },
22415     
22416     onClick: function(e) {
22417         e.stopPropagation();
22418         e.preventDefault();
22419     },
22420     
22421     picker : function()
22422     {
22423         return this.pickerEl;
22424     },
22425     
22426     fillTime: function()
22427     {    
22428         var time = this.pop.select('tbody', true).first();
22429         
22430         time.dom.innerHTML = '';
22431         
22432         time.createChild({
22433             tag: 'tr',
22434             cn: [
22435                 {
22436                     tag: 'td',
22437                     cn: [
22438                         {
22439                             tag: 'a',
22440                             href: '#',
22441                             cls: 'btn',
22442                             cn: [
22443                                 {
22444                                     tag: 'i',
22445                                     cls: 'hours-up fa fas fa-chevron-up'
22446                                 }
22447                             ]
22448                         } 
22449                     ]
22450                 },
22451                 {
22452                     tag: 'td',
22453                     cls: 'separator'
22454                 },
22455                 {
22456                     tag: 'td',
22457                     cn: [
22458                         {
22459                             tag: 'a',
22460                             href: '#',
22461                             cls: 'btn',
22462                             cn: [
22463                                 {
22464                                     tag: 'i',
22465                                     cls: 'minutes-up fa fas fa-chevron-up'
22466                                 }
22467                             ]
22468                         }
22469                     ]
22470                 },
22471                 {
22472                     tag: 'td',
22473                     cls: 'separator'
22474                 }
22475             ]
22476         });
22477         
22478         time.createChild({
22479             tag: 'tr',
22480             cn: [
22481                 {
22482                     tag: 'td',
22483                     cn: [
22484                         {
22485                             tag: 'span',
22486                             cls: 'timepicker-hour',
22487                             html: '00'
22488                         }  
22489                     ]
22490                 },
22491                 {
22492                     tag: 'td',
22493                     cls: 'separator',
22494                     html: ':'
22495                 },
22496                 {
22497                     tag: 'td',
22498                     cn: [
22499                         {
22500                             tag: 'span',
22501                             cls: 'timepicker-minute',
22502                             html: '00'
22503                         }  
22504                     ]
22505                 },
22506                 {
22507                     tag: 'td',
22508                     cls: 'separator'
22509                 },
22510                 {
22511                     tag: 'td',
22512                     cn: [
22513                         {
22514                             tag: 'button',
22515                             type: 'button',
22516                             cls: 'btn btn-primary period',
22517                             html: 'AM'
22518                             
22519                         }
22520                     ]
22521                 }
22522             ]
22523         });
22524         
22525         time.createChild({
22526             tag: 'tr',
22527             cn: [
22528                 {
22529                     tag: 'td',
22530                     cn: [
22531                         {
22532                             tag: 'a',
22533                             href: '#',
22534                             cls: 'btn',
22535                             cn: [
22536                                 {
22537                                     tag: 'span',
22538                                     cls: 'hours-down fa fas fa-chevron-down'
22539                                 }
22540                             ]
22541                         }
22542                     ]
22543                 },
22544                 {
22545                     tag: 'td',
22546                     cls: 'separator'
22547                 },
22548                 {
22549                     tag: 'td',
22550                     cn: [
22551                         {
22552                             tag: 'a',
22553                             href: '#',
22554                             cls: 'btn',
22555                             cn: [
22556                                 {
22557                                     tag: 'span',
22558                                     cls: 'minutes-down fa fas fa-chevron-down'
22559                                 }
22560                             ]
22561                         }
22562                     ]
22563                 },
22564                 {
22565                     tag: 'td',
22566                     cls: 'separator'
22567                 }
22568             ]
22569         });
22570         
22571     },
22572     
22573     update: function()
22574     {
22575         
22576         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22577         
22578         this.fill();
22579     },
22580     
22581     fill: function() 
22582     {
22583         var hours = this.time.getHours();
22584         var minutes = this.time.getMinutes();
22585         var period = 'AM';
22586         
22587         if(hours > 11){
22588             period = 'PM';
22589         }
22590         
22591         if(hours == 0){
22592             hours = 12;
22593         }
22594         
22595         
22596         if(hours > 12){
22597             hours = hours - 12;
22598         }
22599         
22600         if(hours < 10){
22601             hours = '0' + hours;
22602         }
22603         
22604         if(minutes < 10){
22605             minutes = '0' + minutes;
22606         }
22607         
22608         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22609         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22610         this.pop.select('button', true).first().dom.innerHTML = period;
22611         
22612     },
22613     
22614     place: function()
22615     {   
22616         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22617         
22618         var cls = ['bottom'];
22619         
22620         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22621             cls.pop();
22622             cls.push('top');
22623         }
22624         
22625         cls.push('right');
22626         
22627         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22628             cls.pop();
22629             cls.push('left');
22630         }
22631         //this.picker().setXY(20000,20000);
22632         this.picker().addClass(cls.join('-'));
22633         
22634         var _this = this;
22635         
22636         Roo.each(cls, function(c){
22637             if(c == 'bottom'){
22638                 (function() {
22639                  //  
22640                 }).defer(200);
22641                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22642                 //_this.picker().setTop(_this.inputEl().getHeight());
22643                 return;
22644             }
22645             if(c == 'top'){
22646                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22647                 
22648                 //_this.picker().setTop(0 - _this.picker().getHeight());
22649                 return;
22650             }
22651             /*
22652             if(c == 'left'){
22653                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22654                 return;
22655             }
22656             if(c == 'right'){
22657                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22658                 return;
22659             }
22660             */
22661         });
22662         
22663     },
22664   
22665     onFocus : function()
22666     {
22667         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22668         this.show();
22669     },
22670     
22671     onBlur : function()
22672     {
22673         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22674         this.hide();
22675     },
22676     
22677     show : function()
22678     {
22679         this.picker().show();
22680         this.pop.show();
22681         this.update();
22682         this.place();
22683         
22684         this.fireEvent('show', this, this.date);
22685     },
22686     
22687     hide : function()
22688     {
22689         this.picker().hide();
22690         this.pop.hide();
22691         
22692         this.fireEvent('hide', this, this.date);
22693     },
22694     
22695     setTime : function()
22696     {
22697         this.hide();
22698         this.setValue(this.time.format(this.format));
22699         
22700         this.fireEvent('select', this, this.date);
22701         
22702         
22703     },
22704     
22705     onMousedown: function(e){
22706         e.stopPropagation();
22707         e.preventDefault();
22708     },
22709     
22710     onIncrementHours: function()
22711     {
22712         Roo.log('onIncrementHours');
22713         this.time = this.time.add(Date.HOUR, 1);
22714         this.update();
22715         
22716     },
22717     
22718     onDecrementHours: function()
22719     {
22720         Roo.log('onDecrementHours');
22721         this.time = this.time.add(Date.HOUR, -1);
22722         this.update();
22723     },
22724     
22725     onIncrementMinutes: function()
22726     {
22727         Roo.log('onIncrementMinutes');
22728         this.time = this.time.add(Date.MINUTE, 1);
22729         this.update();
22730     },
22731     
22732     onDecrementMinutes: function()
22733     {
22734         Roo.log('onDecrementMinutes');
22735         this.time = this.time.add(Date.MINUTE, -1);
22736         this.update();
22737     },
22738     
22739     onTogglePeriod: function()
22740     {
22741         Roo.log('onTogglePeriod');
22742         this.time = this.time.add(Date.HOUR, 12);
22743         this.update();
22744     }
22745     
22746    
22747 });
22748  
22749
22750 Roo.apply(Roo.bootstrap.TimeField,  {
22751   
22752     template : {
22753         tag: 'div',
22754         cls: 'datepicker dropdown-menu',
22755         cn: [
22756             {
22757                 tag: 'div',
22758                 cls: 'datepicker-time',
22759                 cn: [
22760                 {
22761                     tag: 'table',
22762                     cls: 'table-condensed',
22763                     cn:[
22764                         {
22765                             tag: 'tbody',
22766                             cn: [
22767                                 {
22768                                     tag: 'tr',
22769                                     cn: [
22770                                     {
22771                                         tag: 'td',
22772                                         colspan: '7'
22773                                     }
22774                                     ]
22775                                 }
22776                             ]
22777                         },
22778                         {
22779                             tag: 'tfoot',
22780                             cn: [
22781                                 {
22782                                     tag: 'tr',
22783                                     cn: [
22784                                     {
22785                                         tag: 'th',
22786                                         colspan: '7',
22787                                         cls: '',
22788                                         cn: [
22789                                             {
22790                                                 tag: 'button',
22791                                                 cls: 'btn btn-info ok',
22792                                                 html: 'OK'
22793                                             }
22794                                         ]
22795                                     }
22796                     
22797                                     ]
22798                                 }
22799                             ]
22800                         }
22801                     ]
22802                 }
22803                 ]
22804             }
22805         ]
22806     }
22807 });
22808
22809  
22810
22811  /*
22812  * - LGPL
22813  *
22814  * MonthField
22815  * 
22816  */
22817
22818 /**
22819  * @class Roo.bootstrap.MonthField
22820  * @extends Roo.bootstrap.Input
22821  * Bootstrap MonthField class
22822  * 
22823  * @cfg {String} language default en
22824  * 
22825  * @constructor
22826  * Create a new MonthField
22827  * @param {Object} config The config object
22828  */
22829
22830 Roo.bootstrap.MonthField = function(config){
22831     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22832     
22833     this.addEvents({
22834         /**
22835          * @event show
22836          * Fires when this field show.
22837          * @param {Roo.bootstrap.MonthField} this
22838          * @param {Mixed} date The date value
22839          */
22840         show : true,
22841         /**
22842          * @event show
22843          * Fires when this field hide.
22844          * @param {Roo.bootstrap.MonthField} this
22845          * @param {Mixed} date The date value
22846          */
22847         hide : true,
22848         /**
22849          * @event select
22850          * Fires when select a date.
22851          * @param {Roo.bootstrap.MonthField} this
22852          * @param {String} oldvalue The old value
22853          * @param {String} newvalue The new value
22854          */
22855         select : true
22856     });
22857 };
22858
22859 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22860     
22861     onRender: function(ct, position)
22862     {
22863         
22864         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22865         
22866         this.language = this.language || 'en';
22867         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22868         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22869         
22870         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22871         this.isInline = false;
22872         this.isInput = true;
22873         this.component = this.el.select('.add-on', true).first() || false;
22874         this.component = (this.component && this.component.length === 0) ? false : this.component;
22875         this.hasInput = this.component && this.inputEL().length;
22876         
22877         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22878         
22879         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22880         
22881         this.picker().on('mousedown', this.onMousedown, this);
22882         this.picker().on('click', this.onClick, this);
22883         
22884         this.picker().addClass('datepicker-dropdown');
22885         
22886         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22887             v.setStyle('width', '189px');
22888         });
22889         
22890         this.fillMonths();
22891         
22892         this.update();
22893         
22894         if(this.isInline) {
22895             this.show();
22896         }
22897         
22898     },
22899     
22900     setValue: function(v, suppressEvent)
22901     {   
22902         var o = this.getValue();
22903         
22904         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22905         
22906         this.update();
22907
22908         if(suppressEvent !== true){
22909             this.fireEvent('select', this, o, v);
22910         }
22911         
22912     },
22913     
22914     getValue: function()
22915     {
22916         return this.value;
22917     },
22918     
22919     onClick: function(e) 
22920     {
22921         e.stopPropagation();
22922         e.preventDefault();
22923         
22924         var target = e.getTarget();
22925         
22926         if(target.nodeName.toLowerCase() === 'i'){
22927             target = Roo.get(target).dom.parentNode;
22928         }
22929         
22930         var nodeName = target.nodeName;
22931         var className = target.className;
22932         var html = target.innerHTML;
22933         
22934         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22935             return;
22936         }
22937         
22938         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22939         
22940         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22941         
22942         this.hide();
22943                         
22944     },
22945     
22946     picker : function()
22947     {
22948         return this.pickerEl;
22949     },
22950     
22951     fillMonths: function()
22952     {    
22953         var i = 0;
22954         var months = this.picker().select('>.datepicker-months td', true).first();
22955         
22956         months.dom.innerHTML = '';
22957         
22958         while (i < 12) {
22959             var month = {
22960                 tag: 'span',
22961                 cls: 'month',
22962                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22963             };
22964             
22965             months.createChild(month);
22966         }
22967         
22968     },
22969     
22970     update: function()
22971     {
22972         var _this = this;
22973         
22974         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22975             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22976         }
22977         
22978         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22979             e.removeClass('active');
22980             
22981             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22982                 e.addClass('active');
22983             }
22984         })
22985     },
22986     
22987     place: function()
22988     {
22989         if(this.isInline) {
22990             return;
22991         }
22992         
22993         this.picker().removeClass(['bottom', 'top']);
22994         
22995         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22996             /*
22997              * place to the top of element!
22998              *
22999              */
23000             
23001             this.picker().addClass('top');
23002             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23003             
23004             return;
23005         }
23006         
23007         this.picker().addClass('bottom');
23008         
23009         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23010     },
23011     
23012     onFocus : function()
23013     {
23014         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23015         this.show();
23016     },
23017     
23018     onBlur : function()
23019     {
23020         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23021         
23022         var d = this.inputEl().getValue();
23023         
23024         this.setValue(d);
23025                 
23026         this.hide();
23027     },
23028     
23029     show : function()
23030     {
23031         this.picker().show();
23032         this.picker().select('>.datepicker-months', true).first().show();
23033         this.update();
23034         this.place();
23035         
23036         this.fireEvent('show', this, this.date);
23037     },
23038     
23039     hide : function()
23040     {
23041         if(this.isInline) {
23042             return;
23043         }
23044         this.picker().hide();
23045         this.fireEvent('hide', this, this.date);
23046         
23047     },
23048     
23049     onMousedown: function(e)
23050     {
23051         e.stopPropagation();
23052         e.preventDefault();
23053     },
23054     
23055     keyup: function(e)
23056     {
23057         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23058         this.update();
23059     },
23060
23061     fireKey: function(e)
23062     {
23063         if (!this.picker().isVisible()){
23064             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23065                 this.show();
23066             }
23067             return;
23068         }
23069         
23070         var dir;
23071         
23072         switch(e.keyCode){
23073             case 27: // escape
23074                 this.hide();
23075                 e.preventDefault();
23076                 break;
23077             case 37: // left
23078             case 39: // right
23079                 dir = e.keyCode == 37 ? -1 : 1;
23080                 
23081                 this.vIndex = this.vIndex + dir;
23082                 
23083                 if(this.vIndex < 0){
23084                     this.vIndex = 0;
23085                 }
23086                 
23087                 if(this.vIndex > 11){
23088                     this.vIndex = 11;
23089                 }
23090                 
23091                 if(isNaN(this.vIndex)){
23092                     this.vIndex = 0;
23093                 }
23094                 
23095                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23096                 
23097                 break;
23098             case 38: // up
23099             case 40: // down
23100                 
23101                 dir = e.keyCode == 38 ? -1 : 1;
23102                 
23103                 this.vIndex = this.vIndex + dir * 4;
23104                 
23105                 if(this.vIndex < 0){
23106                     this.vIndex = 0;
23107                 }
23108                 
23109                 if(this.vIndex > 11){
23110                     this.vIndex = 11;
23111                 }
23112                 
23113                 if(isNaN(this.vIndex)){
23114                     this.vIndex = 0;
23115                 }
23116                 
23117                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23118                 break;
23119                 
23120             case 13: // enter
23121                 
23122                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23123                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23124                 }
23125                 
23126                 this.hide();
23127                 e.preventDefault();
23128                 break;
23129             case 9: // tab
23130                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23131                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23132                 }
23133                 this.hide();
23134                 break;
23135             case 16: // shift
23136             case 17: // ctrl
23137             case 18: // alt
23138                 break;
23139             default :
23140                 this.hide();
23141                 
23142         }
23143     },
23144     
23145     remove: function() 
23146     {
23147         this.picker().remove();
23148     }
23149    
23150 });
23151
23152 Roo.apply(Roo.bootstrap.MonthField,  {
23153     
23154     content : {
23155         tag: 'tbody',
23156         cn: [
23157         {
23158             tag: 'tr',
23159             cn: [
23160             {
23161                 tag: 'td',
23162                 colspan: '7'
23163             }
23164             ]
23165         }
23166         ]
23167     },
23168     
23169     dates:{
23170         en: {
23171             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23172             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23173         }
23174     }
23175 });
23176
23177 Roo.apply(Roo.bootstrap.MonthField,  {
23178   
23179     template : {
23180         tag: 'div',
23181         cls: 'datepicker dropdown-menu roo-dynamic',
23182         cn: [
23183             {
23184                 tag: 'div',
23185                 cls: 'datepicker-months',
23186                 cn: [
23187                 {
23188                     tag: 'table',
23189                     cls: 'table-condensed',
23190                     cn:[
23191                         Roo.bootstrap.DateField.content
23192                     ]
23193                 }
23194                 ]
23195             }
23196         ]
23197     }
23198 });
23199
23200  
23201
23202  
23203  /*
23204  * - LGPL
23205  *
23206  * CheckBox
23207  * 
23208  */
23209
23210 /**
23211  * @class Roo.bootstrap.CheckBox
23212  * @extends Roo.bootstrap.Input
23213  * Bootstrap CheckBox class
23214  * 
23215  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23216  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23217  * @cfg {String} boxLabel The text that appears beside the checkbox
23218  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23219  * @cfg {Boolean} checked initnal the element
23220  * @cfg {Boolean} inline inline the element (default false)
23221  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23222  * @cfg {String} tooltip label tooltip
23223  * 
23224  * @constructor
23225  * Create a new CheckBox
23226  * @param {Object} config The config object
23227  */
23228
23229 Roo.bootstrap.CheckBox = function(config){
23230     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23231    
23232     this.addEvents({
23233         /**
23234         * @event check
23235         * Fires when the element is checked or unchecked.
23236         * @param {Roo.bootstrap.CheckBox} this This input
23237         * @param {Boolean} checked The new checked value
23238         */
23239        check : true,
23240        /**
23241         * @event click
23242         * Fires when the element is click.
23243         * @param {Roo.bootstrap.CheckBox} this This input
23244         */
23245        click : true
23246     });
23247     
23248 };
23249
23250 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23251   
23252     inputType: 'checkbox',
23253     inputValue: 1,
23254     valueOff: 0,
23255     boxLabel: false,
23256     checked: false,
23257     weight : false,
23258     inline: false,
23259     tooltip : '',
23260     
23261     // checkbox success does not make any sense really.. 
23262     invalidClass : "",
23263     validClass : "",
23264     
23265     
23266     getAutoCreate : function()
23267     {
23268         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23269         
23270         var id = Roo.id();
23271         
23272         var cfg = {};
23273         
23274         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23275         
23276         if(this.inline){
23277             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23278         }
23279         
23280         var input =  {
23281             tag: 'input',
23282             id : id,
23283             type : this.inputType,
23284             value : this.inputValue,
23285             cls : 'roo-' + this.inputType, //'form-box',
23286             placeholder : this.placeholder || ''
23287             
23288         };
23289         
23290         if(this.inputType != 'radio'){
23291             var hidden =  {
23292                 tag: 'input',
23293                 type : 'hidden',
23294                 cls : 'roo-hidden-value',
23295                 value : this.checked ? this.inputValue : this.valueOff
23296             };
23297         }
23298         
23299             
23300         if (this.weight) { // Validity check?
23301             cfg.cls += " " + this.inputType + "-" + this.weight;
23302         }
23303         
23304         if (this.disabled) {
23305             input.disabled=true;
23306         }
23307         
23308         if(this.checked){
23309             input.checked = this.checked;
23310         }
23311         
23312         if (this.name) {
23313             
23314             input.name = this.name;
23315             
23316             if(this.inputType != 'radio'){
23317                 hidden.name = this.name;
23318                 input.name = '_hidden_' + this.name;
23319             }
23320         }
23321         
23322         if (this.size) {
23323             input.cls += ' input-' + this.size;
23324         }
23325         
23326         var settings=this;
23327         
23328         ['xs','sm','md','lg'].map(function(size){
23329             if (settings[size]) {
23330                 cfg.cls += ' col-' + size + '-' + settings[size];
23331             }
23332         });
23333         
23334         var inputblock = input;
23335          
23336         if (this.before || this.after) {
23337             
23338             inputblock = {
23339                 cls : 'input-group',
23340                 cn :  [] 
23341             };
23342             
23343             if (this.before) {
23344                 inputblock.cn.push({
23345                     tag :'span',
23346                     cls : 'input-group-addon',
23347                     html : this.before
23348                 });
23349             }
23350             
23351             inputblock.cn.push(input);
23352             
23353             if(this.inputType != 'radio'){
23354                 inputblock.cn.push(hidden);
23355             }
23356             
23357             if (this.after) {
23358                 inputblock.cn.push({
23359                     tag :'span',
23360                     cls : 'input-group-addon',
23361                     html : this.after
23362                 });
23363             }
23364             
23365         }
23366         var boxLabelCfg = false;
23367         
23368         if(this.boxLabel){
23369            
23370             boxLabelCfg = {
23371                 tag: 'label',
23372                 //'for': id, // box label is handled by onclick - so no for...
23373                 cls: 'box-label',
23374                 html: this.boxLabel
23375             };
23376             if(this.tooltip){
23377                 boxLabelCfg.tooltip = this.tooltip;
23378             }
23379              
23380         }
23381         
23382         
23383         if (align ==='left' && this.fieldLabel.length) {
23384 //                Roo.log("left and has label");
23385             cfg.cn = [
23386                 {
23387                     tag: 'label',
23388                     'for' :  id,
23389                     cls : 'control-label',
23390                     html : this.fieldLabel
23391                 },
23392                 {
23393                     cls : "", 
23394                     cn: [
23395                         inputblock
23396                     ]
23397                 }
23398             ];
23399             
23400             if (boxLabelCfg) {
23401                 cfg.cn[1].cn.push(boxLabelCfg);
23402             }
23403             
23404             if(this.labelWidth > 12){
23405                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23406             }
23407             
23408             if(this.labelWidth < 13 && this.labelmd == 0){
23409                 this.labelmd = this.labelWidth;
23410             }
23411             
23412             if(this.labellg > 0){
23413                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23414                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23415             }
23416             
23417             if(this.labelmd > 0){
23418                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23419                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23420             }
23421             
23422             if(this.labelsm > 0){
23423                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23424                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23425             }
23426             
23427             if(this.labelxs > 0){
23428                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23429                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23430             }
23431             
23432         } else if ( this.fieldLabel.length) {
23433 //                Roo.log(" label");
23434                 cfg.cn = [
23435                    
23436                     {
23437                         tag: this.boxLabel ? 'span' : 'label',
23438                         'for': id,
23439                         cls: 'control-label box-input-label',
23440                         //cls : 'input-group-addon',
23441                         html : this.fieldLabel
23442                     },
23443                     
23444                     inputblock
23445                     
23446                 ];
23447                 if (boxLabelCfg) {
23448                     cfg.cn.push(boxLabelCfg);
23449                 }
23450
23451         } else {
23452             
23453 //                Roo.log(" no label && no align");
23454                 cfg.cn = [  inputblock ] ;
23455                 if (boxLabelCfg) {
23456                     cfg.cn.push(boxLabelCfg);
23457                 }
23458
23459                 
23460         }
23461         
23462        
23463         
23464         if(this.inputType != 'radio'){
23465             cfg.cn.push(hidden);
23466         }
23467         
23468         return cfg;
23469         
23470     },
23471     
23472     /**
23473      * return the real input element.
23474      */
23475     inputEl: function ()
23476     {
23477         return this.el.select('input.roo-' + this.inputType,true).first();
23478     },
23479     hiddenEl: function ()
23480     {
23481         return this.el.select('input.roo-hidden-value',true).first();
23482     },
23483     
23484     labelEl: function()
23485     {
23486         return this.el.select('label.control-label',true).first();
23487     },
23488     /* depricated... */
23489     
23490     label: function()
23491     {
23492         return this.labelEl();
23493     },
23494     
23495     boxLabelEl: function()
23496     {
23497         return this.el.select('label.box-label',true).first();
23498     },
23499     
23500     initEvents : function()
23501     {
23502 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23503         
23504         this.inputEl().on('click', this.onClick,  this);
23505         
23506         if (this.boxLabel) { 
23507             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23508         }
23509         
23510         this.startValue = this.getValue();
23511         
23512         if(this.groupId){
23513             Roo.bootstrap.CheckBox.register(this);
23514         }
23515     },
23516     
23517     onClick : function(e)
23518     {   
23519         if(this.fireEvent('click', this, e) !== false){
23520             this.setChecked(!this.checked);
23521         }
23522         
23523     },
23524     
23525     setChecked : function(state,suppressEvent)
23526     {
23527         this.startValue = this.getValue();
23528
23529         if(this.inputType == 'radio'){
23530             
23531             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23532                 e.dom.checked = false;
23533             });
23534             
23535             this.inputEl().dom.checked = true;
23536             
23537             this.inputEl().dom.value = this.inputValue;
23538             
23539             if(suppressEvent !== true){
23540                 this.fireEvent('check', this, true);
23541             }
23542             
23543             this.validate();
23544             
23545             return;
23546         }
23547         
23548         this.checked = state;
23549         
23550         this.inputEl().dom.checked = state;
23551         
23552         
23553         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23554         
23555         if(suppressEvent !== true){
23556             this.fireEvent('check', this, state);
23557         }
23558         
23559         this.validate();
23560     },
23561     
23562     getValue : function()
23563     {
23564         if(this.inputType == 'radio'){
23565             return this.getGroupValue();
23566         }
23567         
23568         return this.hiddenEl().dom.value;
23569         
23570     },
23571     
23572     getGroupValue : function()
23573     {
23574         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23575             return '';
23576         }
23577         
23578         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23579     },
23580     
23581     setValue : function(v,suppressEvent)
23582     {
23583         if(this.inputType == 'radio'){
23584             this.setGroupValue(v, suppressEvent);
23585             return;
23586         }
23587         
23588         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23589         
23590         this.validate();
23591     },
23592     
23593     setGroupValue : function(v, suppressEvent)
23594     {
23595         this.startValue = this.getValue();
23596         
23597         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23598             e.dom.checked = false;
23599             
23600             if(e.dom.value == v){
23601                 e.dom.checked = true;
23602             }
23603         });
23604         
23605         if(suppressEvent !== true){
23606             this.fireEvent('check', this, true);
23607         }
23608
23609         this.validate();
23610         
23611         return;
23612     },
23613     
23614     validate : function()
23615     {
23616         if(this.getVisibilityEl().hasClass('hidden')){
23617             return true;
23618         }
23619         
23620         if(
23621                 this.disabled || 
23622                 (this.inputType == 'radio' && this.validateRadio()) ||
23623                 (this.inputType == 'checkbox' && this.validateCheckbox())
23624         ){
23625             this.markValid();
23626             return true;
23627         }
23628         
23629         this.markInvalid();
23630         return false;
23631     },
23632     
23633     validateRadio : function()
23634     {
23635         if(this.getVisibilityEl().hasClass('hidden')){
23636             return true;
23637         }
23638         
23639         if(this.allowBlank){
23640             return true;
23641         }
23642         
23643         var valid = false;
23644         
23645         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23646             if(!e.dom.checked){
23647                 return;
23648             }
23649             
23650             valid = true;
23651             
23652             return false;
23653         });
23654         
23655         return valid;
23656     },
23657     
23658     validateCheckbox : function()
23659     {
23660         if(!this.groupId){
23661             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23662             //return (this.getValue() == this.inputValue) ? true : false;
23663         }
23664         
23665         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23666         
23667         if(!group){
23668             return false;
23669         }
23670         
23671         var r = false;
23672         
23673         for(var i in group){
23674             if(group[i].el.isVisible(true)){
23675                 r = false;
23676                 break;
23677             }
23678             
23679             r = true;
23680         }
23681         
23682         for(var i in group){
23683             if(r){
23684                 break;
23685             }
23686             
23687             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23688         }
23689         
23690         return r;
23691     },
23692     
23693     /**
23694      * Mark this field as valid
23695      */
23696     markValid : function()
23697     {
23698         var _this = this;
23699         
23700         this.fireEvent('valid', this);
23701         
23702         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23703         
23704         if(this.groupId){
23705             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23706         }
23707         
23708         if(label){
23709             label.markValid();
23710         }
23711
23712         if(this.inputType == 'radio'){
23713             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23714                 var fg = e.findParent('.form-group', false, true);
23715                 if (Roo.bootstrap.version == 3) {
23716                     fg.removeClass([_this.invalidClass, _this.validClass]);
23717                     fg.addClass(_this.validClass);
23718                 } else {
23719                     fg.removeClass(['is-valid', 'is-invalid']);
23720                     fg.addClass('is-valid');
23721                 }
23722             });
23723             
23724             return;
23725         }
23726
23727         if(!this.groupId){
23728             var fg = this.el.findParent('.form-group', false, true);
23729             if (Roo.bootstrap.version == 3) {
23730                 fg.removeClass([this.invalidClass, this.validClass]);
23731                 fg.addClass(this.validClass);
23732             } else {
23733                 fg.removeClass(['is-valid', 'is-invalid']);
23734                 fg.addClass('is-valid');
23735             }
23736             return;
23737         }
23738         
23739         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23740         
23741         if(!group){
23742             return;
23743         }
23744         
23745         for(var i in group){
23746             var fg = group[i].el.findParent('.form-group', false, true);
23747             if (Roo.bootstrap.version == 3) {
23748                 fg.removeClass([this.invalidClass, this.validClass]);
23749                 fg.addClass(this.validClass);
23750             } else {
23751                 fg.removeClass(['is-valid', 'is-invalid']);
23752                 fg.addClass('is-valid');
23753             }
23754         }
23755     },
23756     
23757      /**
23758      * Mark this field as invalid
23759      * @param {String} msg The validation message
23760      */
23761     markInvalid : function(msg)
23762     {
23763         if(this.allowBlank){
23764             return;
23765         }
23766         
23767         var _this = this;
23768         
23769         this.fireEvent('invalid', this, msg);
23770         
23771         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23772         
23773         if(this.groupId){
23774             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23775         }
23776         
23777         if(label){
23778             label.markInvalid();
23779         }
23780             
23781         if(this.inputType == 'radio'){
23782             
23783             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23784                 var fg = e.findParent('.form-group', false, true);
23785                 if (Roo.bootstrap.version == 3) {
23786                     fg.removeClass([_this.invalidClass, _this.validClass]);
23787                     fg.addClass(_this.invalidClass);
23788                 } else {
23789                     fg.removeClass(['is-invalid', 'is-valid']);
23790                     fg.addClass('is-invalid');
23791                 }
23792             });
23793             
23794             return;
23795         }
23796         
23797         if(!this.groupId){
23798             var fg = this.el.findParent('.form-group', false, true);
23799             if (Roo.bootstrap.version == 3) {
23800                 fg.removeClass([_this.invalidClass, _this.validClass]);
23801                 fg.addClass(_this.invalidClass);
23802             } else {
23803                 fg.removeClass(['is-invalid', 'is-valid']);
23804                 fg.addClass('is-invalid');
23805             }
23806             return;
23807         }
23808         
23809         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23810         
23811         if(!group){
23812             return;
23813         }
23814         
23815         for(var i in group){
23816             var fg = group[i].el.findParent('.form-group', false, true);
23817             if (Roo.bootstrap.version == 3) {
23818                 fg.removeClass([_this.invalidClass, _this.validClass]);
23819                 fg.addClass(_this.invalidClass);
23820             } else {
23821                 fg.removeClass(['is-invalid', 'is-valid']);
23822                 fg.addClass('is-invalid');
23823             }
23824         }
23825         
23826     },
23827     
23828     clearInvalid : function()
23829     {
23830         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23831         
23832         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23833         
23834         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23835         
23836         if (label && label.iconEl) {
23837             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23838             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23839         }
23840     },
23841     
23842     disable : function()
23843     {
23844         if(this.inputType != 'radio'){
23845             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23846             return;
23847         }
23848         
23849         var _this = this;
23850         
23851         if(this.rendered){
23852             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23853                 _this.getActionEl().addClass(this.disabledClass);
23854                 e.dom.disabled = true;
23855             });
23856         }
23857         
23858         this.disabled = true;
23859         this.fireEvent("disable", this);
23860         return this;
23861     },
23862
23863     enable : function()
23864     {
23865         if(this.inputType != 'radio'){
23866             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23867             return;
23868         }
23869         
23870         var _this = this;
23871         
23872         if(this.rendered){
23873             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23874                 _this.getActionEl().removeClass(this.disabledClass);
23875                 e.dom.disabled = false;
23876             });
23877         }
23878         
23879         this.disabled = false;
23880         this.fireEvent("enable", this);
23881         return this;
23882     },
23883     
23884     setBoxLabel : function(v)
23885     {
23886         this.boxLabel = v;
23887         
23888         if(this.rendered){
23889             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23890         }
23891     }
23892
23893 });
23894
23895 Roo.apply(Roo.bootstrap.CheckBox, {
23896     
23897     groups: {},
23898     
23899      /**
23900     * register a CheckBox Group
23901     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23902     */
23903     register : function(checkbox)
23904     {
23905         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23906             this.groups[checkbox.groupId] = {};
23907         }
23908         
23909         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23910             return;
23911         }
23912         
23913         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23914         
23915     },
23916     /**
23917     * fetch a CheckBox Group based on the group ID
23918     * @param {string} the group ID
23919     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23920     */
23921     get: function(groupId) {
23922         if (typeof(this.groups[groupId]) == 'undefined') {
23923             return false;
23924         }
23925         
23926         return this.groups[groupId] ;
23927     }
23928     
23929     
23930 });
23931 /*
23932  * - LGPL
23933  *
23934  * RadioItem
23935  * 
23936  */
23937
23938 /**
23939  * @class Roo.bootstrap.Radio
23940  * @extends Roo.bootstrap.Component
23941  * Bootstrap Radio class
23942  * @cfg {String} boxLabel - the label associated
23943  * @cfg {String} value - the value of radio
23944  * 
23945  * @constructor
23946  * Create a new Radio
23947  * @param {Object} config The config object
23948  */
23949 Roo.bootstrap.Radio = function(config){
23950     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23951     
23952 };
23953
23954 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23955     
23956     boxLabel : '',
23957     
23958     value : '',
23959     
23960     getAutoCreate : function()
23961     {
23962         var cfg = {
23963             tag : 'div',
23964             cls : 'form-group radio',
23965             cn : [
23966                 {
23967                     tag : 'label',
23968                     cls : 'box-label',
23969                     html : this.boxLabel
23970                 }
23971             ]
23972         };
23973         
23974         return cfg;
23975     },
23976     
23977     initEvents : function() 
23978     {
23979         this.parent().register(this);
23980         
23981         this.el.on('click', this.onClick, this);
23982         
23983     },
23984     
23985     onClick : function(e)
23986     {
23987         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23988             this.setChecked(true);
23989         }
23990     },
23991     
23992     setChecked : function(state, suppressEvent)
23993     {
23994         this.parent().setValue(this.value, suppressEvent);
23995         
23996     },
23997     
23998     setBoxLabel : function(v)
23999     {
24000         this.boxLabel = v;
24001         
24002         if(this.rendered){
24003             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24004         }
24005     }
24006     
24007 });
24008  
24009
24010  /*
24011  * - LGPL
24012  *
24013  * Input
24014  * 
24015  */
24016
24017 /**
24018  * @class Roo.bootstrap.SecurePass
24019  * @extends Roo.bootstrap.Input
24020  * Bootstrap SecurePass class
24021  *
24022  * 
24023  * @constructor
24024  * Create a new SecurePass
24025  * @param {Object} config The config object
24026  */
24027  
24028 Roo.bootstrap.SecurePass = function (config) {
24029     // these go here, so the translation tool can replace them..
24030     this.errors = {
24031         PwdEmpty: "Please type a password, and then retype it to confirm.",
24032         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24033         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24034         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24035         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24036         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24037         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24038         TooWeak: "Your password is Too Weak."
24039     },
24040     this.meterLabel = "Password strength:";
24041     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24042     this.meterClass = [
24043         "roo-password-meter-tooweak", 
24044         "roo-password-meter-weak", 
24045         "roo-password-meter-medium", 
24046         "roo-password-meter-strong", 
24047         "roo-password-meter-grey"
24048     ];
24049     
24050     this.errors = {};
24051     
24052     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24053 }
24054
24055 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24056     /**
24057      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24058      * {
24059      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24060      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24061      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24062      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24063      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24064      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24065      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24066      * })
24067      */
24068     // private
24069     
24070     meterWidth: 300,
24071     errorMsg :'',    
24072     errors: false,
24073     imageRoot: '/',
24074     /**
24075      * @cfg {String/Object} Label for the strength meter (defaults to
24076      * 'Password strength:')
24077      */
24078     // private
24079     meterLabel: '',
24080     /**
24081      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24082      * ['Weak', 'Medium', 'Strong'])
24083      */
24084     // private    
24085     pwdStrengths: false,    
24086     // private
24087     strength: 0,
24088     // private
24089     _lastPwd: null,
24090     // private
24091     kCapitalLetter: 0,
24092     kSmallLetter: 1,
24093     kDigit: 2,
24094     kPunctuation: 3,
24095     
24096     insecure: false,
24097     // private
24098     initEvents: function ()
24099     {
24100         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24101
24102         if (this.el.is('input[type=password]') && Roo.isSafari) {
24103             this.el.on('keydown', this.SafariOnKeyDown, this);
24104         }
24105
24106         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24107     },
24108     // private
24109     onRender: function (ct, position)
24110     {
24111         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24112         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24113         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24114
24115         this.trigger.createChild({
24116                    cn: [
24117                     {
24118                     //id: 'PwdMeter',
24119                     tag: 'div',
24120                     cls: 'roo-password-meter-grey col-xs-12',
24121                     style: {
24122                         //width: 0,
24123                         //width: this.meterWidth + 'px'                                                
24124                         }
24125                     },
24126                     {                            
24127                          cls: 'roo-password-meter-text'                          
24128                     }
24129                 ]            
24130         });
24131
24132          
24133         if (this.hideTrigger) {
24134             this.trigger.setDisplayed(false);
24135         }
24136         this.setSize(this.width || '', this.height || '');
24137     },
24138     // private
24139     onDestroy: function ()
24140     {
24141         if (this.trigger) {
24142             this.trigger.removeAllListeners();
24143             this.trigger.remove();
24144         }
24145         if (this.wrap) {
24146             this.wrap.remove();
24147         }
24148         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24149     },
24150     // private
24151     checkStrength: function ()
24152     {
24153         var pwd = this.inputEl().getValue();
24154         if (pwd == this._lastPwd) {
24155             return;
24156         }
24157
24158         var strength;
24159         if (this.ClientSideStrongPassword(pwd)) {
24160             strength = 3;
24161         } else if (this.ClientSideMediumPassword(pwd)) {
24162             strength = 2;
24163         } else if (this.ClientSideWeakPassword(pwd)) {
24164             strength = 1;
24165         } else {
24166             strength = 0;
24167         }
24168         
24169         Roo.log('strength1: ' + strength);
24170         
24171         //var pm = this.trigger.child('div/div/div').dom;
24172         var pm = this.trigger.child('div/div');
24173         pm.removeClass(this.meterClass);
24174         pm.addClass(this.meterClass[strength]);
24175                 
24176         
24177         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24178                 
24179         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24180         
24181         this._lastPwd = pwd;
24182     },
24183     reset: function ()
24184     {
24185         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24186         
24187         this._lastPwd = '';
24188         
24189         var pm = this.trigger.child('div/div');
24190         pm.removeClass(this.meterClass);
24191         pm.addClass('roo-password-meter-grey');        
24192         
24193         
24194         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24195         
24196         pt.innerHTML = '';
24197         this.inputEl().dom.type='password';
24198     },
24199     // private
24200     validateValue: function (value)
24201     {
24202         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24203             return false;
24204         }
24205         if (value.length == 0) {
24206             if (this.allowBlank) {
24207                 this.clearInvalid();
24208                 return true;
24209             }
24210
24211             this.markInvalid(this.errors.PwdEmpty);
24212             this.errorMsg = this.errors.PwdEmpty;
24213             return false;
24214         }
24215         
24216         if(this.insecure){
24217             return true;
24218         }
24219         
24220         if (!value.match(/[\x21-\x7e]+/)) {
24221             this.markInvalid(this.errors.PwdBadChar);
24222             this.errorMsg = this.errors.PwdBadChar;
24223             return false;
24224         }
24225         if (value.length < 6) {
24226             this.markInvalid(this.errors.PwdShort);
24227             this.errorMsg = this.errors.PwdShort;
24228             return false;
24229         }
24230         if (value.length > 16) {
24231             this.markInvalid(this.errors.PwdLong);
24232             this.errorMsg = this.errors.PwdLong;
24233             return false;
24234         }
24235         var strength;
24236         if (this.ClientSideStrongPassword(value)) {
24237             strength = 3;
24238         } else if (this.ClientSideMediumPassword(value)) {
24239             strength = 2;
24240         } else if (this.ClientSideWeakPassword(value)) {
24241             strength = 1;
24242         } else {
24243             strength = 0;
24244         }
24245
24246         
24247         if (strength < 2) {
24248             //this.markInvalid(this.errors.TooWeak);
24249             this.errorMsg = this.errors.TooWeak;
24250             //return false;
24251         }
24252         
24253         
24254         console.log('strength2: ' + strength);
24255         
24256         //var pm = this.trigger.child('div/div/div').dom;
24257         
24258         var pm = this.trigger.child('div/div');
24259         pm.removeClass(this.meterClass);
24260         pm.addClass(this.meterClass[strength]);
24261                 
24262         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24263                 
24264         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24265         
24266         this.errorMsg = ''; 
24267         return true;
24268     },
24269     // private
24270     CharacterSetChecks: function (type)
24271     {
24272         this.type = type;
24273         this.fResult = false;
24274     },
24275     // private
24276     isctype: function (character, type)
24277     {
24278         switch (type) {  
24279             case this.kCapitalLetter:
24280                 if (character >= 'A' && character <= 'Z') {
24281                     return true;
24282                 }
24283                 break;
24284             
24285             case this.kSmallLetter:
24286                 if (character >= 'a' && character <= 'z') {
24287                     return true;
24288                 }
24289                 break;
24290             
24291             case this.kDigit:
24292                 if (character >= '0' && character <= '9') {
24293                     return true;
24294                 }
24295                 break;
24296             
24297             case this.kPunctuation:
24298                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24299                     return true;
24300                 }
24301                 break;
24302             
24303             default:
24304                 return false;
24305         }
24306
24307     },
24308     // private
24309     IsLongEnough: function (pwd, size)
24310     {
24311         return !(pwd == null || isNaN(size) || pwd.length < size);
24312     },
24313     // private
24314     SpansEnoughCharacterSets: function (word, nb)
24315     {
24316         if (!this.IsLongEnough(word, nb))
24317         {
24318             return false;
24319         }
24320
24321         var characterSetChecks = new Array(
24322             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24323             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24324         );
24325         
24326         for (var index = 0; index < word.length; ++index) {
24327             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24328                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24329                     characterSetChecks[nCharSet].fResult = true;
24330                     break;
24331                 }
24332             }
24333         }
24334
24335         var nCharSets = 0;
24336         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24337             if (characterSetChecks[nCharSet].fResult) {
24338                 ++nCharSets;
24339             }
24340         }
24341
24342         if (nCharSets < nb) {
24343             return false;
24344         }
24345         return true;
24346     },
24347     // private
24348     ClientSideStrongPassword: function (pwd)
24349     {
24350         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24351     },
24352     // private
24353     ClientSideMediumPassword: function (pwd)
24354     {
24355         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24356     },
24357     // private
24358     ClientSideWeakPassword: function (pwd)
24359     {
24360         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24361     }
24362           
24363 })//<script type="text/javascript">
24364
24365 /*
24366  * Based  Ext JS Library 1.1.1
24367  * Copyright(c) 2006-2007, Ext JS, LLC.
24368  * LGPL
24369  *
24370  */
24371  
24372 /**
24373  * @class Roo.HtmlEditorCore
24374  * @extends Roo.Component
24375  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24376  *
24377  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24378  */
24379
24380 Roo.HtmlEditorCore = function(config){
24381     
24382     
24383     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24384     
24385     
24386     this.addEvents({
24387         /**
24388          * @event initialize
24389          * Fires when the editor is fully initialized (including the iframe)
24390          * @param {Roo.HtmlEditorCore} this
24391          */
24392         initialize: true,
24393         /**
24394          * @event activate
24395          * Fires when the editor is first receives the focus. Any insertion must wait
24396          * until after this event.
24397          * @param {Roo.HtmlEditorCore} this
24398          */
24399         activate: true,
24400          /**
24401          * @event beforesync
24402          * Fires before the textarea is updated with content from the editor iframe. Return false
24403          * to cancel the sync.
24404          * @param {Roo.HtmlEditorCore} this
24405          * @param {String} html
24406          */
24407         beforesync: true,
24408          /**
24409          * @event beforepush
24410          * Fires before the iframe editor is updated with content from the textarea. Return false
24411          * to cancel the push.
24412          * @param {Roo.HtmlEditorCore} this
24413          * @param {String} html
24414          */
24415         beforepush: true,
24416          /**
24417          * @event sync
24418          * Fires when the textarea is updated with content from the editor iframe.
24419          * @param {Roo.HtmlEditorCore} this
24420          * @param {String} html
24421          */
24422         sync: true,
24423          /**
24424          * @event push
24425          * Fires when the iframe editor is updated with content from the textarea.
24426          * @param {Roo.HtmlEditorCore} this
24427          * @param {String} html
24428          */
24429         push: true,
24430         
24431         /**
24432          * @event editorevent
24433          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24434          * @param {Roo.HtmlEditorCore} this
24435          */
24436         editorevent: true
24437         
24438     });
24439     
24440     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24441     
24442     // defaults : white / black...
24443     this.applyBlacklists();
24444     
24445     
24446     
24447 };
24448
24449
24450 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24451
24452
24453      /**
24454      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24455      */
24456     
24457     owner : false,
24458     
24459      /**
24460      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24461      *                        Roo.resizable.
24462      */
24463     resizable : false,
24464      /**
24465      * @cfg {Number} height (in pixels)
24466      */   
24467     height: 300,
24468    /**
24469      * @cfg {Number} width (in pixels)
24470      */   
24471     width: 500,
24472     
24473     /**
24474      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24475      * 
24476      */
24477     stylesheets: false,
24478     
24479     // id of frame..
24480     frameId: false,
24481     
24482     // private properties
24483     validationEvent : false,
24484     deferHeight: true,
24485     initialized : false,
24486     activated : false,
24487     sourceEditMode : false,
24488     onFocus : Roo.emptyFn,
24489     iframePad:3,
24490     hideMode:'offsets',
24491     
24492     clearUp: true,
24493     
24494     // blacklist + whitelisted elements..
24495     black: false,
24496     white: false,
24497      
24498     bodyCls : '',
24499
24500     /**
24501      * Protected method that will not generally be called directly. It
24502      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24503      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24504      */
24505     getDocMarkup : function(){
24506         // body styles..
24507         var st = '';
24508         
24509         // inherit styels from page...?? 
24510         if (this.stylesheets === false) {
24511             
24512             Roo.get(document.head).select('style').each(function(node) {
24513                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24514             });
24515             
24516             Roo.get(document.head).select('link').each(function(node) { 
24517                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24518             });
24519             
24520         } else if (!this.stylesheets.length) {
24521                 // simple..
24522                 st = '<style type="text/css">' +
24523                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24524                    '</style>';
24525         } else {
24526             for (var i in this.stylesheets) { 
24527                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24528             }
24529             
24530         }
24531         
24532         st +=  '<style type="text/css">' +
24533             'IMG { cursor: pointer } ' +
24534         '</style>';
24535
24536         var cls = 'roo-htmleditor-body';
24537         
24538         if(this.bodyCls.length){
24539             cls += ' ' + this.bodyCls;
24540         }
24541         
24542         return '<html><head>' + st  +
24543             //<style type="text/css">' +
24544             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24545             //'</style>' +
24546             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24547     },
24548
24549     // private
24550     onRender : function(ct, position)
24551     {
24552         var _t = this;
24553         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24554         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24555         
24556         
24557         this.el.dom.style.border = '0 none';
24558         this.el.dom.setAttribute('tabIndex', -1);
24559         this.el.addClass('x-hidden hide');
24560         
24561         
24562         
24563         if(Roo.isIE){ // fix IE 1px bogus margin
24564             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24565         }
24566        
24567         
24568         this.frameId = Roo.id();
24569         
24570          
24571         
24572         var iframe = this.owner.wrap.createChild({
24573             tag: 'iframe',
24574             cls: 'form-control', // bootstrap..
24575             id: this.frameId,
24576             name: this.frameId,
24577             frameBorder : 'no',
24578             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24579         }, this.el
24580         );
24581         
24582         
24583         this.iframe = iframe.dom;
24584
24585          this.assignDocWin();
24586         
24587         this.doc.designMode = 'on';
24588        
24589         this.doc.open();
24590         this.doc.write(this.getDocMarkup());
24591         this.doc.close();
24592
24593         
24594         var task = { // must defer to wait for browser to be ready
24595             run : function(){
24596                 //console.log("run task?" + this.doc.readyState);
24597                 this.assignDocWin();
24598                 if(this.doc.body || this.doc.readyState == 'complete'){
24599                     try {
24600                         this.doc.designMode="on";
24601                     } catch (e) {
24602                         return;
24603                     }
24604                     Roo.TaskMgr.stop(task);
24605                     this.initEditor.defer(10, this);
24606                 }
24607             },
24608             interval : 10,
24609             duration: 10000,
24610             scope: this
24611         };
24612         Roo.TaskMgr.start(task);
24613
24614     },
24615
24616     // private
24617     onResize : function(w, h)
24618     {
24619          Roo.log('resize: ' +w + ',' + h );
24620         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24621         if(!this.iframe){
24622             return;
24623         }
24624         if(typeof w == 'number'){
24625             
24626             this.iframe.style.width = w + 'px';
24627         }
24628         if(typeof h == 'number'){
24629             
24630             this.iframe.style.height = h + 'px';
24631             if(this.doc){
24632                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24633             }
24634         }
24635         
24636     },
24637
24638     /**
24639      * Toggles the editor between standard and source edit mode.
24640      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24641      */
24642     toggleSourceEdit : function(sourceEditMode){
24643         
24644         this.sourceEditMode = sourceEditMode === true;
24645         
24646         if(this.sourceEditMode){
24647  
24648             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24649             
24650         }else{
24651             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24652             //this.iframe.className = '';
24653             this.deferFocus();
24654         }
24655         //this.setSize(this.owner.wrap.getSize());
24656         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24657     },
24658
24659     
24660   
24661
24662     /**
24663      * Protected method that will not generally be called directly. If you need/want
24664      * custom HTML cleanup, this is the method you should override.
24665      * @param {String} html The HTML to be cleaned
24666      * return {String} The cleaned HTML
24667      */
24668     cleanHtml : function(html){
24669         html = String(html);
24670         if(html.length > 5){
24671             if(Roo.isSafari){ // strip safari nonsense
24672                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24673             }
24674         }
24675         if(html == '&nbsp;'){
24676             html = '';
24677         }
24678         return html;
24679     },
24680
24681     /**
24682      * HTML Editor -> Textarea
24683      * Protected method that will not generally be called directly. Syncs the contents
24684      * of the editor iframe with the textarea.
24685      */
24686     syncValue : function(){
24687         if(this.initialized){
24688             var bd = (this.doc.body || this.doc.documentElement);
24689             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24690             var html = bd.innerHTML;
24691             if(Roo.isSafari){
24692                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24693                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24694                 if(m && m[1]){
24695                     html = '<div style="'+m[0]+'">' + html + '</div>';
24696                 }
24697             }
24698             html = this.cleanHtml(html);
24699             // fix up the special chars.. normaly like back quotes in word...
24700             // however we do not want to do this with chinese..
24701             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24702                 
24703                 var cc = match.charCodeAt();
24704
24705                 // Get the character value, handling surrogate pairs
24706                 if (match.length == 2) {
24707                     // It's a surrogate pair, calculate the Unicode code point
24708                     var high = match.charCodeAt(0) - 0xD800;
24709                     var low  = match.charCodeAt(1) - 0xDC00;
24710                     cc = (high * 0x400) + low + 0x10000;
24711                 }  else if (
24712                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24713                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24714                     (cc >= 0xf900 && cc < 0xfb00 )
24715                 ) {
24716                         return match;
24717                 }  
24718          
24719                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24720                 return "&#" + cc + ";";
24721                 
24722                 
24723             });
24724             
24725             
24726              
24727             if(this.owner.fireEvent('beforesync', this, html) !== false){
24728                 this.el.dom.value = html;
24729                 this.owner.fireEvent('sync', this, html);
24730             }
24731         }
24732     },
24733
24734     /**
24735      * Protected method that will not generally be called directly. Pushes the value of the textarea
24736      * into the iframe editor.
24737      */
24738     pushValue : function(){
24739         if(this.initialized){
24740             var v = this.el.dom.value.trim();
24741             
24742 //            if(v.length < 1){
24743 //                v = '&#160;';
24744 //            }
24745             
24746             if(this.owner.fireEvent('beforepush', this, v) !== false){
24747                 var d = (this.doc.body || this.doc.documentElement);
24748                 d.innerHTML = v;
24749                 this.cleanUpPaste();
24750                 this.el.dom.value = d.innerHTML;
24751                 this.owner.fireEvent('push', this, v);
24752             }
24753         }
24754     },
24755
24756     // private
24757     deferFocus : function(){
24758         this.focus.defer(10, this);
24759     },
24760
24761     // doc'ed in Field
24762     focus : function(){
24763         if(this.win && !this.sourceEditMode){
24764             this.win.focus();
24765         }else{
24766             this.el.focus();
24767         }
24768     },
24769     
24770     assignDocWin: function()
24771     {
24772         var iframe = this.iframe;
24773         
24774          if(Roo.isIE){
24775             this.doc = iframe.contentWindow.document;
24776             this.win = iframe.contentWindow;
24777         } else {
24778 //            if (!Roo.get(this.frameId)) {
24779 //                return;
24780 //            }
24781 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24782 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24783             
24784             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24785                 return;
24786             }
24787             
24788             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24789             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24790         }
24791     },
24792     
24793     // private
24794     initEditor : function(){
24795         //console.log("INIT EDITOR");
24796         this.assignDocWin();
24797         
24798         
24799         
24800         this.doc.designMode="on";
24801         this.doc.open();
24802         this.doc.write(this.getDocMarkup());
24803         this.doc.close();
24804         
24805         var dbody = (this.doc.body || this.doc.documentElement);
24806         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24807         // this copies styles from the containing element into thsi one..
24808         // not sure why we need all of this..
24809         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24810         
24811         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24812         //ss['background-attachment'] = 'fixed'; // w3c
24813         dbody.bgProperties = 'fixed'; // ie
24814         //Roo.DomHelper.applyStyles(dbody, ss);
24815         Roo.EventManager.on(this.doc, {
24816             //'mousedown': this.onEditorEvent,
24817             'mouseup': this.onEditorEvent,
24818             'dblclick': this.onEditorEvent,
24819             'click': this.onEditorEvent,
24820             'keyup': this.onEditorEvent,
24821             buffer:100,
24822             scope: this
24823         });
24824         if(Roo.isGecko){
24825             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24826         }
24827         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24828             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24829         }
24830         this.initialized = true;
24831
24832         this.owner.fireEvent('initialize', this);
24833         this.pushValue();
24834     },
24835
24836     // private
24837     onDestroy : function(){
24838         
24839         
24840         
24841         if(this.rendered){
24842             
24843             //for (var i =0; i < this.toolbars.length;i++) {
24844             //    // fixme - ask toolbars for heights?
24845             //    this.toolbars[i].onDestroy();
24846            // }
24847             
24848             //this.wrap.dom.innerHTML = '';
24849             //this.wrap.remove();
24850         }
24851     },
24852
24853     // private
24854     onFirstFocus : function(){
24855         
24856         this.assignDocWin();
24857         
24858         
24859         this.activated = true;
24860          
24861     
24862         if(Roo.isGecko){ // prevent silly gecko errors
24863             this.win.focus();
24864             var s = this.win.getSelection();
24865             if(!s.focusNode || s.focusNode.nodeType != 3){
24866                 var r = s.getRangeAt(0);
24867                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24868                 r.collapse(true);
24869                 this.deferFocus();
24870             }
24871             try{
24872                 this.execCmd('useCSS', true);
24873                 this.execCmd('styleWithCSS', false);
24874             }catch(e){}
24875         }
24876         this.owner.fireEvent('activate', this);
24877     },
24878
24879     // private
24880     adjustFont: function(btn){
24881         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24882         //if(Roo.isSafari){ // safari
24883         //    adjust *= 2;
24884        // }
24885         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24886         if(Roo.isSafari){ // safari
24887             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24888             v =  (v < 10) ? 10 : v;
24889             v =  (v > 48) ? 48 : v;
24890             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24891             
24892         }
24893         
24894         
24895         v = Math.max(1, v+adjust);
24896         
24897         this.execCmd('FontSize', v  );
24898     },
24899
24900     onEditorEvent : function(e)
24901     {
24902         this.owner.fireEvent('editorevent', this, e);
24903       //  this.updateToolbar();
24904         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24905     },
24906
24907     insertTag : function(tg)
24908     {
24909         // could be a bit smarter... -> wrap the current selected tRoo..
24910         if (tg.toLowerCase() == 'span' ||
24911             tg.toLowerCase() == 'code' ||
24912             tg.toLowerCase() == 'sup' ||
24913             tg.toLowerCase() == 'sub' 
24914             ) {
24915             
24916             range = this.createRange(this.getSelection());
24917             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24918             wrappingNode.appendChild(range.extractContents());
24919             range.insertNode(wrappingNode);
24920
24921             return;
24922             
24923             
24924             
24925         }
24926         this.execCmd("formatblock",   tg);
24927         
24928     },
24929     
24930     insertText : function(txt)
24931     {
24932         
24933         
24934         var range = this.createRange();
24935         range.deleteContents();
24936                //alert(Sender.getAttribute('label'));
24937                
24938         range.insertNode(this.doc.createTextNode(txt));
24939     } ,
24940     
24941      
24942
24943     /**
24944      * Executes a Midas editor command on the editor document and performs necessary focus and
24945      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24946      * @param {String} cmd The Midas command
24947      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24948      */
24949     relayCmd : function(cmd, value){
24950         this.win.focus();
24951         this.execCmd(cmd, value);
24952         this.owner.fireEvent('editorevent', this);
24953         //this.updateToolbar();
24954         this.owner.deferFocus();
24955     },
24956
24957     /**
24958      * Executes a Midas editor command directly on the editor document.
24959      * For visual commands, you should use {@link #relayCmd} instead.
24960      * <b>This should only be called after the editor is initialized.</b>
24961      * @param {String} cmd The Midas command
24962      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24963      */
24964     execCmd : function(cmd, value){
24965         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24966         this.syncValue();
24967     },
24968  
24969  
24970    
24971     /**
24972      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24973      * to insert tRoo.
24974      * @param {String} text | dom node.. 
24975      */
24976     insertAtCursor : function(text)
24977     {
24978         
24979         if(!this.activated){
24980             return;
24981         }
24982         /*
24983         if(Roo.isIE){
24984             this.win.focus();
24985             var r = this.doc.selection.createRange();
24986             if(r){
24987                 r.collapse(true);
24988                 r.pasteHTML(text);
24989                 this.syncValue();
24990                 this.deferFocus();
24991             
24992             }
24993             return;
24994         }
24995         */
24996         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24997             this.win.focus();
24998             
24999             
25000             // from jquery ui (MIT licenced)
25001             var range, node;
25002             var win = this.win;
25003             
25004             if (win.getSelection && win.getSelection().getRangeAt) {
25005                 range = win.getSelection().getRangeAt(0);
25006                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25007                 range.insertNode(node);
25008             } else if (win.document.selection && win.document.selection.createRange) {
25009                 // no firefox support
25010                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25011                 win.document.selection.createRange().pasteHTML(txt);
25012             } else {
25013                 // no firefox support
25014                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25015                 this.execCmd('InsertHTML', txt);
25016             } 
25017             
25018             this.syncValue();
25019             
25020             this.deferFocus();
25021         }
25022     },
25023  // private
25024     mozKeyPress : function(e){
25025         if(e.ctrlKey){
25026             var c = e.getCharCode(), cmd;
25027           
25028             if(c > 0){
25029                 c = String.fromCharCode(c).toLowerCase();
25030                 switch(c){
25031                     case 'b':
25032                         cmd = 'bold';
25033                         break;
25034                     case 'i':
25035                         cmd = 'italic';
25036                         break;
25037                     
25038                     case 'u':
25039                         cmd = 'underline';
25040                         break;
25041                     
25042                     case 'v':
25043                         this.cleanUpPaste.defer(100, this);
25044                         return;
25045                         
25046                 }
25047                 if(cmd){
25048                     this.win.focus();
25049                     this.execCmd(cmd);
25050                     this.deferFocus();
25051                     e.preventDefault();
25052                 }
25053                 
25054             }
25055         }
25056     },
25057
25058     // private
25059     fixKeys : function(){ // load time branching for fastest keydown performance
25060         if(Roo.isIE){
25061             return function(e){
25062                 var k = e.getKey(), r;
25063                 if(k == e.TAB){
25064                     e.stopEvent();
25065                     r = this.doc.selection.createRange();
25066                     if(r){
25067                         r.collapse(true);
25068                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25069                         this.deferFocus();
25070                     }
25071                     return;
25072                 }
25073                 
25074                 if(k == e.ENTER){
25075                     r = this.doc.selection.createRange();
25076                     if(r){
25077                         var target = r.parentElement();
25078                         if(!target || target.tagName.toLowerCase() != 'li'){
25079                             e.stopEvent();
25080                             r.pasteHTML('<br />');
25081                             r.collapse(false);
25082                             r.select();
25083                         }
25084                     }
25085                 }
25086                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25087                     this.cleanUpPaste.defer(100, this);
25088                     return;
25089                 }
25090                 
25091                 
25092             };
25093         }else if(Roo.isOpera){
25094             return function(e){
25095                 var k = e.getKey();
25096                 if(k == e.TAB){
25097                     e.stopEvent();
25098                     this.win.focus();
25099                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25100                     this.deferFocus();
25101                 }
25102                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25103                     this.cleanUpPaste.defer(100, this);
25104                     return;
25105                 }
25106                 
25107             };
25108         }else if(Roo.isSafari){
25109             return function(e){
25110                 var k = e.getKey();
25111                 
25112                 if(k == e.TAB){
25113                     e.stopEvent();
25114                     this.execCmd('InsertText','\t');
25115                     this.deferFocus();
25116                     return;
25117                 }
25118                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25119                     this.cleanUpPaste.defer(100, this);
25120                     return;
25121                 }
25122                 
25123              };
25124         }
25125     }(),
25126     
25127     getAllAncestors: function()
25128     {
25129         var p = this.getSelectedNode();
25130         var a = [];
25131         if (!p) {
25132             a.push(p); // push blank onto stack..
25133             p = this.getParentElement();
25134         }
25135         
25136         
25137         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25138             a.push(p);
25139             p = p.parentNode;
25140         }
25141         a.push(this.doc.body);
25142         return a;
25143     },
25144     lastSel : false,
25145     lastSelNode : false,
25146     
25147     
25148     getSelection : function() 
25149     {
25150         this.assignDocWin();
25151         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25152     },
25153     
25154     getSelectedNode: function() 
25155     {
25156         // this may only work on Gecko!!!
25157         
25158         // should we cache this!!!!
25159         
25160         
25161         
25162          
25163         var range = this.createRange(this.getSelection()).cloneRange();
25164         
25165         if (Roo.isIE) {
25166             var parent = range.parentElement();
25167             while (true) {
25168                 var testRange = range.duplicate();
25169                 testRange.moveToElementText(parent);
25170                 if (testRange.inRange(range)) {
25171                     break;
25172                 }
25173                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25174                     break;
25175                 }
25176                 parent = parent.parentElement;
25177             }
25178             return parent;
25179         }
25180         
25181         // is ancestor a text element.
25182         var ac =  range.commonAncestorContainer;
25183         if (ac.nodeType == 3) {
25184             ac = ac.parentNode;
25185         }
25186         
25187         var ar = ac.childNodes;
25188          
25189         var nodes = [];
25190         var other_nodes = [];
25191         var has_other_nodes = false;
25192         for (var i=0;i<ar.length;i++) {
25193             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25194                 continue;
25195             }
25196             // fullly contained node.
25197             
25198             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25199                 nodes.push(ar[i]);
25200                 continue;
25201             }
25202             
25203             // probably selected..
25204             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25205                 other_nodes.push(ar[i]);
25206                 continue;
25207             }
25208             // outer..
25209             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25210                 continue;
25211             }
25212             
25213             
25214             has_other_nodes = true;
25215         }
25216         if (!nodes.length && other_nodes.length) {
25217             nodes= other_nodes;
25218         }
25219         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25220             return false;
25221         }
25222         
25223         return nodes[0];
25224     },
25225     createRange: function(sel)
25226     {
25227         // this has strange effects when using with 
25228         // top toolbar - not sure if it's a great idea.
25229         //this.editor.contentWindow.focus();
25230         if (typeof sel != "undefined") {
25231             try {
25232                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25233             } catch(e) {
25234                 return this.doc.createRange();
25235             }
25236         } else {
25237             return this.doc.createRange();
25238         }
25239     },
25240     getParentElement: function()
25241     {
25242         
25243         this.assignDocWin();
25244         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25245         
25246         var range = this.createRange(sel);
25247          
25248         try {
25249             var p = range.commonAncestorContainer;
25250             while (p.nodeType == 3) { // text node
25251                 p = p.parentNode;
25252             }
25253             return p;
25254         } catch (e) {
25255             return null;
25256         }
25257     
25258     },
25259     /***
25260      *
25261      * Range intersection.. the hard stuff...
25262      *  '-1' = before
25263      *  '0' = hits..
25264      *  '1' = after.
25265      *         [ -- selected range --- ]
25266      *   [fail]                        [fail]
25267      *
25268      *    basically..
25269      *      if end is before start or  hits it. fail.
25270      *      if start is after end or hits it fail.
25271      *
25272      *   if either hits (but other is outside. - then it's not 
25273      *   
25274      *    
25275      **/
25276     
25277     
25278     // @see http://www.thismuchiknow.co.uk/?p=64.
25279     rangeIntersectsNode : function(range, node)
25280     {
25281         var nodeRange = node.ownerDocument.createRange();
25282         try {
25283             nodeRange.selectNode(node);
25284         } catch (e) {
25285             nodeRange.selectNodeContents(node);
25286         }
25287     
25288         var rangeStartRange = range.cloneRange();
25289         rangeStartRange.collapse(true);
25290     
25291         var rangeEndRange = range.cloneRange();
25292         rangeEndRange.collapse(false);
25293     
25294         var nodeStartRange = nodeRange.cloneRange();
25295         nodeStartRange.collapse(true);
25296     
25297         var nodeEndRange = nodeRange.cloneRange();
25298         nodeEndRange.collapse(false);
25299     
25300         return rangeStartRange.compareBoundaryPoints(
25301                  Range.START_TO_START, nodeEndRange) == -1 &&
25302                rangeEndRange.compareBoundaryPoints(
25303                  Range.START_TO_START, nodeStartRange) == 1;
25304         
25305          
25306     },
25307     rangeCompareNode : function(range, node)
25308     {
25309         var nodeRange = node.ownerDocument.createRange();
25310         try {
25311             nodeRange.selectNode(node);
25312         } catch (e) {
25313             nodeRange.selectNodeContents(node);
25314         }
25315         
25316         
25317         range.collapse(true);
25318     
25319         nodeRange.collapse(true);
25320      
25321         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25322         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25323          
25324         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25325         
25326         var nodeIsBefore   =  ss == 1;
25327         var nodeIsAfter    = ee == -1;
25328         
25329         if (nodeIsBefore && nodeIsAfter) {
25330             return 0; // outer
25331         }
25332         if (!nodeIsBefore && nodeIsAfter) {
25333             return 1; //right trailed.
25334         }
25335         
25336         if (nodeIsBefore && !nodeIsAfter) {
25337             return 2;  // left trailed.
25338         }
25339         // fully contined.
25340         return 3;
25341     },
25342
25343     // private? - in a new class?
25344     cleanUpPaste :  function()
25345     {
25346         // cleans up the whole document..
25347         Roo.log('cleanuppaste');
25348         
25349         this.cleanUpChildren(this.doc.body);
25350         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25351         if (clean != this.doc.body.innerHTML) {
25352             this.doc.body.innerHTML = clean;
25353         }
25354         
25355     },
25356     
25357     cleanWordChars : function(input) {// change the chars to hex code
25358         var he = Roo.HtmlEditorCore;
25359         
25360         var output = input;
25361         Roo.each(he.swapCodes, function(sw) { 
25362             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25363             
25364             output = output.replace(swapper, sw[1]);
25365         });
25366         
25367         return output;
25368     },
25369     
25370     
25371     cleanUpChildren : function (n)
25372     {
25373         if (!n.childNodes.length) {
25374             return;
25375         }
25376         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25377            this.cleanUpChild(n.childNodes[i]);
25378         }
25379     },
25380     
25381     
25382         
25383     
25384     cleanUpChild : function (node)
25385     {
25386         var ed = this;
25387         //console.log(node);
25388         if (node.nodeName == "#text") {
25389             // clean up silly Windows -- stuff?
25390             return; 
25391         }
25392         if (node.nodeName == "#comment") {
25393             node.parentNode.removeChild(node);
25394             // clean up silly Windows -- stuff?
25395             return; 
25396         }
25397         var lcname = node.tagName.toLowerCase();
25398         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25399         // whitelist of tags..
25400         
25401         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25402             // remove node.
25403             node.parentNode.removeChild(node);
25404             return;
25405             
25406         }
25407         
25408         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25409         
25410         // spans with no attributes - just remove them..
25411         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25412             remove_keep_children = true;
25413         }
25414         
25415         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25416         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25417         
25418         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25419         //    remove_keep_children = true;
25420         //}
25421         
25422         if (remove_keep_children) {
25423             this.cleanUpChildren(node);
25424             // inserts everything just before this node...
25425             while (node.childNodes.length) {
25426                 var cn = node.childNodes[0];
25427                 node.removeChild(cn);
25428                 node.parentNode.insertBefore(cn, node);
25429             }
25430             node.parentNode.removeChild(node);
25431             return;
25432         }
25433         
25434         if (!node.attributes || !node.attributes.length) {
25435             
25436           
25437             
25438             
25439             this.cleanUpChildren(node);
25440             return;
25441         }
25442         
25443         function cleanAttr(n,v)
25444         {
25445             
25446             if (v.match(/^\./) || v.match(/^\//)) {
25447                 return;
25448             }
25449             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25450                 return;
25451             }
25452             if (v.match(/^#/)) {
25453                 return;
25454             }
25455             if (v.match(/^\{/)) { // allow template editing.
25456                 return;
25457             }
25458 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25459             node.removeAttribute(n);
25460             
25461         }
25462         
25463         var cwhite = this.cwhite;
25464         var cblack = this.cblack;
25465             
25466         function cleanStyle(n,v)
25467         {
25468             if (v.match(/expression/)) { //XSS?? should we even bother..
25469                 node.removeAttribute(n);
25470                 return;
25471             }
25472             
25473             var parts = v.split(/;/);
25474             var clean = [];
25475             
25476             Roo.each(parts, function(p) {
25477                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25478                 if (!p.length) {
25479                     return true;
25480                 }
25481                 var l = p.split(':').shift().replace(/\s+/g,'');
25482                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25483                 
25484                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25485 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25486                     //node.removeAttribute(n);
25487                     return true;
25488                 }
25489                 //Roo.log()
25490                 // only allow 'c whitelisted system attributes'
25491                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25492 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25493                     //node.removeAttribute(n);
25494                     return true;
25495                 }
25496                 
25497                 
25498                  
25499                 
25500                 clean.push(p);
25501                 return true;
25502             });
25503             if (clean.length) { 
25504                 node.setAttribute(n, clean.join(';'));
25505             } else {
25506                 node.removeAttribute(n);
25507             }
25508             
25509         }
25510         
25511         
25512         for (var i = node.attributes.length-1; i > -1 ; i--) {
25513             var a = node.attributes[i];
25514             //console.log(a);
25515             
25516             if (a.name.toLowerCase().substr(0,2)=='on')  {
25517                 node.removeAttribute(a.name);
25518                 continue;
25519             }
25520             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25521                 node.removeAttribute(a.name);
25522                 continue;
25523             }
25524             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25525                 cleanAttr(a.name,a.value); // fixme..
25526                 continue;
25527             }
25528             if (a.name == 'style') {
25529                 cleanStyle(a.name,a.value);
25530                 continue;
25531             }
25532             /// clean up MS crap..
25533             // tecnically this should be a list of valid class'es..
25534             
25535             
25536             if (a.name == 'class') {
25537                 if (a.value.match(/^Mso/)) {
25538                     node.removeAttribute('class');
25539                 }
25540                 
25541                 if (a.value.match(/^body$/)) {
25542                     node.removeAttribute('class');
25543                 }
25544                 continue;
25545             }
25546             
25547             // style cleanup!?
25548             // class cleanup?
25549             
25550         }
25551         
25552         
25553         this.cleanUpChildren(node);
25554         
25555         
25556     },
25557     
25558     /**
25559      * Clean up MS wordisms...
25560      */
25561     cleanWord : function(node)
25562     {
25563         if (!node) {
25564             this.cleanWord(this.doc.body);
25565             return;
25566         }
25567         
25568         if(
25569                 node.nodeName == 'SPAN' &&
25570                 !node.hasAttributes() &&
25571                 node.childNodes.length == 1 &&
25572                 node.firstChild.nodeName == "#text"  
25573         ) {
25574             var textNode = node.firstChild;
25575             node.removeChild(textNode);
25576             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25577                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25578             }
25579             node.parentNode.insertBefore(textNode, node);
25580             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25581                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25582             }
25583             node.parentNode.removeChild(node);
25584         }
25585         
25586         if (node.nodeName == "#text") {
25587             // clean up silly Windows -- stuff?
25588             return; 
25589         }
25590         if (node.nodeName == "#comment") {
25591             node.parentNode.removeChild(node);
25592             // clean up silly Windows -- stuff?
25593             return; 
25594         }
25595         
25596         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25597             node.parentNode.removeChild(node);
25598             return;
25599         }
25600         //Roo.log(node.tagName);
25601         // remove - but keep children..
25602         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25603             //Roo.log('-- removed');
25604             while (node.childNodes.length) {
25605                 var cn = node.childNodes[0];
25606                 node.removeChild(cn);
25607                 node.parentNode.insertBefore(cn, node);
25608                 // move node to parent - and clean it..
25609                 this.cleanWord(cn);
25610             }
25611             node.parentNode.removeChild(node);
25612             /// no need to iterate chidlren = it's got none..
25613             //this.iterateChildren(node, this.cleanWord);
25614             return;
25615         }
25616         // clean styles
25617         if (node.className.length) {
25618             
25619             var cn = node.className.split(/\W+/);
25620             var cna = [];
25621             Roo.each(cn, function(cls) {
25622                 if (cls.match(/Mso[a-zA-Z]+/)) {
25623                     return;
25624                 }
25625                 cna.push(cls);
25626             });
25627             node.className = cna.length ? cna.join(' ') : '';
25628             if (!cna.length) {
25629                 node.removeAttribute("class");
25630             }
25631         }
25632         
25633         if (node.hasAttribute("lang")) {
25634             node.removeAttribute("lang");
25635         }
25636         
25637         if (node.hasAttribute("style")) {
25638             
25639             var styles = node.getAttribute("style").split(";");
25640             var nstyle = [];
25641             Roo.each(styles, function(s) {
25642                 if (!s.match(/:/)) {
25643                     return;
25644                 }
25645                 var kv = s.split(":");
25646                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25647                     return;
25648                 }
25649                 // what ever is left... we allow.
25650                 nstyle.push(s);
25651             });
25652             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25653             if (!nstyle.length) {
25654                 node.removeAttribute('style');
25655             }
25656         }
25657         this.iterateChildren(node, this.cleanWord);
25658         
25659         
25660         
25661     },
25662     /**
25663      * iterateChildren of a Node, calling fn each time, using this as the scole..
25664      * @param {DomNode} node node to iterate children of.
25665      * @param {Function} fn method of this class to call on each item.
25666      */
25667     iterateChildren : function(node, fn)
25668     {
25669         if (!node.childNodes.length) {
25670                 return;
25671         }
25672         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25673            fn.call(this, node.childNodes[i])
25674         }
25675     },
25676     
25677     
25678     /**
25679      * cleanTableWidths.
25680      *
25681      * Quite often pasting from word etc.. results in tables with column and widths.
25682      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25683      *
25684      */
25685     cleanTableWidths : function(node)
25686     {
25687          
25688          
25689         if (!node) {
25690             this.cleanTableWidths(this.doc.body);
25691             return;
25692         }
25693         
25694         // ignore list...
25695         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25696             return; 
25697         }
25698         Roo.log(node.tagName);
25699         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25700             this.iterateChildren(node, this.cleanTableWidths);
25701             return;
25702         }
25703         if (node.hasAttribute('width')) {
25704             node.removeAttribute('width');
25705         }
25706         
25707          
25708         if (node.hasAttribute("style")) {
25709             // pretty basic...
25710             
25711             var styles = node.getAttribute("style").split(";");
25712             var nstyle = [];
25713             Roo.each(styles, function(s) {
25714                 if (!s.match(/:/)) {
25715                     return;
25716                 }
25717                 var kv = s.split(":");
25718                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25719                     return;
25720                 }
25721                 // what ever is left... we allow.
25722                 nstyle.push(s);
25723             });
25724             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25725             if (!nstyle.length) {
25726                 node.removeAttribute('style');
25727             }
25728         }
25729         
25730         this.iterateChildren(node, this.cleanTableWidths);
25731         
25732         
25733     },
25734     
25735     
25736     
25737     
25738     domToHTML : function(currentElement, depth, nopadtext) {
25739         
25740         depth = depth || 0;
25741         nopadtext = nopadtext || false;
25742     
25743         if (!currentElement) {
25744             return this.domToHTML(this.doc.body);
25745         }
25746         
25747         //Roo.log(currentElement);
25748         var j;
25749         var allText = false;
25750         var nodeName = currentElement.nodeName;
25751         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25752         
25753         if  (nodeName == '#text') {
25754             
25755             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25756         }
25757         
25758         
25759         var ret = '';
25760         if (nodeName != 'BODY') {
25761              
25762             var i = 0;
25763             // Prints the node tagName, such as <A>, <IMG>, etc
25764             if (tagName) {
25765                 var attr = [];
25766                 for(i = 0; i < currentElement.attributes.length;i++) {
25767                     // quoting?
25768                     var aname = currentElement.attributes.item(i).name;
25769                     if (!currentElement.attributes.item(i).value.length) {
25770                         continue;
25771                     }
25772                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25773                 }
25774                 
25775                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25776             } 
25777             else {
25778                 
25779                 // eack
25780             }
25781         } else {
25782             tagName = false;
25783         }
25784         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25785             return ret;
25786         }
25787         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25788             nopadtext = true;
25789         }
25790         
25791         
25792         // Traverse the tree
25793         i = 0;
25794         var currentElementChild = currentElement.childNodes.item(i);
25795         var allText = true;
25796         var innerHTML  = '';
25797         lastnode = '';
25798         while (currentElementChild) {
25799             // Formatting code (indent the tree so it looks nice on the screen)
25800             var nopad = nopadtext;
25801             if (lastnode == 'SPAN') {
25802                 nopad  = true;
25803             }
25804             // text
25805             if  (currentElementChild.nodeName == '#text') {
25806                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25807                 toadd = nopadtext ? toadd : toadd.trim();
25808                 if (!nopad && toadd.length > 80) {
25809                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25810                 }
25811                 innerHTML  += toadd;
25812                 
25813                 i++;
25814                 currentElementChild = currentElement.childNodes.item(i);
25815                 lastNode = '';
25816                 continue;
25817             }
25818             allText = false;
25819             
25820             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25821                 
25822             // Recursively traverse the tree structure of the child node
25823             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25824             lastnode = currentElementChild.nodeName;
25825             i++;
25826             currentElementChild=currentElement.childNodes.item(i);
25827         }
25828         
25829         ret += innerHTML;
25830         
25831         if (!allText) {
25832                 // The remaining code is mostly for formatting the tree
25833             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25834         }
25835         
25836         
25837         if (tagName) {
25838             ret+= "</"+tagName+">";
25839         }
25840         return ret;
25841         
25842     },
25843         
25844     applyBlacklists : function()
25845     {
25846         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25847         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25848         
25849         this.white = [];
25850         this.black = [];
25851         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25852             if (b.indexOf(tag) > -1) {
25853                 return;
25854             }
25855             this.white.push(tag);
25856             
25857         }, this);
25858         
25859         Roo.each(w, function(tag) {
25860             if (b.indexOf(tag) > -1) {
25861                 return;
25862             }
25863             if (this.white.indexOf(tag) > -1) {
25864                 return;
25865             }
25866             this.white.push(tag);
25867             
25868         }, this);
25869         
25870         
25871         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25872             if (w.indexOf(tag) > -1) {
25873                 return;
25874             }
25875             this.black.push(tag);
25876             
25877         }, this);
25878         
25879         Roo.each(b, function(tag) {
25880             if (w.indexOf(tag) > -1) {
25881                 return;
25882             }
25883             if (this.black.indexOf(tag) > -1) {
25884                 return;
25885             }
25886             this.black.push(tag);
25887             
25888         }, this);
25889         
25890         
25891         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25892         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25893         
25894         this.cwhite = [];
25895         this.cblack = [];
25896         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25897             if (b.indexOf(tag) > -1) {
25898                 return;
25899             }
25900             this.cwhite.push(tag);
25901             
25902         }, this);
25903         
25904         Roo.each(w, function(tag) {
25905             if (b.indexOf(tag) > -1) {
25906                 return;
25907             }
25908             if (this.cwhite.indexOf(tag) > -1) {
25909                 return;
25910             }
25911             this.cwhite.push(tag);
25912             
25913         }, this);
25914         
25915         
25916         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25917             if (w.indexOf(tag) > -1) {
25918                 return;
25919             }
25920             this.cblack.push(tag);
25921             
25922         }, this);
25923         
25924         Roo.each(b, function(tag) {
25925             if (w.indexOf(tag) > -1) {
25926                 return;
25927             }
25928             if (this.cblack.indexOf(tag) > -1) {
25929                 return;
25930             }
25931             this.cblack.push(tag);
25932             
25933         }, this);
25934     },
25935     
25936     setStylesheets : function(stylesheets)
25937     {
25938         if(typeof(stylesheets) == 'string'){
25939             Roo.get(this.iframe.contentDocument.head).createChild({
25940                 tag : 'link',
25941                 rel : 'stylesheet',
25942                 type : 'text/css',
25943                 href : stylesheets
25944             });
25945             
25946             return;
25947         }
25948         var _this = this;
25949      
25950         Roo.each(stylesheets, function(s) {
25951             if(!s.length){
25952                 return;
25953             }
25954             
25955             Roo.get(_this.iframe.contentDocument.head).createChild({
25956                 tag : 'link',
25957                 rel : 'stylesheet',
25958                 type : 'text/css',
25959                 href : s
25960             });
25961         });
25962
25963         
25964     },
25965     
25966     removeStylesheets : function()
25967     {
25968         var _this = this;
25969         
25970         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25971             s.remove();
25972         });
25973     },
25974     
25975     setStyle : function(style)
25976     {
25977         Roo.get(this.iframe.contentDocument.head).createChild({
25978             tag : 'style',
25979             type : 'text/css',
25980             html : style
25981         });
25982
25983         return;
25984     }
25985     
25986     // hide stuff that is not compatible
25987     /**
25988      * @event blur
25989      * @hide
25990      */
25991     /**
25992      * @event change
25993      * @hide
25994      */
25995     /**
25996      * @event focus
25997      * @hide
25998      */
25999     /**
26000      * @event specialkey
26001      * @hide
26002      */
26003     /**
26004      * @cfg {String} fieldClass @hide
26005      */
26006     /**
26007      * @cfg {String} focusClass @hide
26008      */
26009     /**
26010      * @cfg {String} autoCreate @hide
26011      */
26012     /**
26013      * @cfg {String} inputType @hide
26014      */
26015     /**
26016      * @cfg {String} invalidClass @hide
26017      */
26018     /**
26019      * @cfg {String} invalidText @hide
26020      */
26021     /**
26022      * @cfg {String} msgFx @hide
26023      */
26024     /**
26025      * @cfg {String} validateOnBlur @hide
26026      */
26027 });
26028
26029 Roo.HtmlEditorCore.white = [
26030         'area', 'br', 'img', 'input', 'hr', 'wbr',
26031         
26032        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26033        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26034        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26035        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26036        'table',   'ul',         'xmp', 
26037        
26038        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26039       'thead',   'tr', 
26040      
26041       'dir', 'menu', 'ol', 'ul', 'dl',
26042        
26043       'embed',  'object'
26044 ];
26045
26046
26047 Roo.HtmlEditorCore.black = [
26048     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26049         'applet', // 
26050         'base',   'basefont', 'bgsound', 'blink',  'body', 
26051         'frame',  'frameset', 'head',    'html',   'ilayer', 
26052         'iframe', 'layer',  'link',     'meta',    'object',   
26053         'script', 'style' ,'title',  'xml' // clean later..
26054 ];
26055 Roo.HtmlEditorCore.clean = [
26056     'script', 'style', 'title', 'xml'
26057 ];
26058 Roo.HtmlEditorCore.remove = [
26059     'font'
26060 ];
26061 // attributes..
26062
26063 Roo.HtmlEditorCore.ablack = [
26064     'on'
26065 ];
26066     
26067 Roo.HtmlEditorCore.aclean = [ 
26068     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26069 ];
26070
26071 // protocols..
26072 Roo.HtmlEditorCore.pwhite= [
26073         'http',  'https',  'mailto'
26074 ];
26075
26076 // white listed style attributes.
26077 Roo.HtmlEditorCore.cwhite= [
26078       //  'text-align', /// default is to allow most things..
26079       
26080          
26081 //        'font-size'//??
26082 ];
26083
26084 // black listed style attributes.
26085 Roo.HtmlEditorCore.cblack= [
26086       //  'font-size' -- this can be set by the project 
26087 ];
26088
26089
26090 Roo.HtmlEditorCore.swapCodes   =[ 
26091     [    8211, "&#8211;" ], 
26092     [    8212, "&#8212;" ], 
26093     [    8216,  "'" ],  
26094     [    8217, "'" ],  
26095     [    8220, '"' ],  
26096     [    8221, '"' ],  
26097     [    8226, "*" ],  
26098     [    8230, "..." ]
26099 ]; 
26100
26101     /*
26102  * - LGPL
26103  *
26104  * HtmlEditor
26105  * 
26106  */
26107
26108 /**
26109  * @class Roo.bootstrap.HtmlEditor
26110  * @extends Roo.bootstrap.TextArea
26111  * Bootstrap HtmlEditor class
26112
26113  * @constructor
26114  * Create a new HtmlEditor
26115  * @param {Object} config The config object
26116  */
26117
26118 Roo.bootstrap.HtmlEditor = function(config){
26119     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26120     if (!this.toolbars) {
26121         this.toolbars = [];
26122     }
26123     
26124     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26125     this.addEvents({
26126             /**
26127              * @event initialize
26128              * Fires when the editor is fully initialized (including the iframe)
26129              * @param {HtmlEditor} this
26130              */
26131             initialize: true,
26132             /**
26133              * @event activate
26134              * Fires when the editor is first receives the focus. Any insertion must wait
26135              * until after this event.
26136              * @param {HtmlEditor} this
26137              */
26138             activate: true,
26139              /**
26140              * @event beforesync
26141              * Fires before the textarea is updated with content from the editor iframe. Return false
26142              * to cancel the sync.
26143              * @param {HtmlEditor} this
26144              * @param {String} html
26145              */
26146             beforesync: true,
26147              /**
26148              * @event beforepush
26149              * Fires before the iframe editor is updated with content from the textarea. Return false
26150              * to cancel the push.
26151              * @param {HtmlEditor} this
26152              * @param {String} html
26153              */
26154             beforepush: true,
26155              /**
26156              * @event sync
26157              * Fires when the textarea is updated with content from the editor iframe.
26158              * @param {HtmlEditor} this
26159              * @param {String} html
26160              */
26161             sync: true,
26162              /**
26163              * @event push
26164              * Fires when the iframe editor is updated with content from the textarea.
26165              * @param {HtmlEditor} this
26166              * @param {String} html
26167              */
26168             push: true,
26169              /**
26170              * @event editmodechange
26171              * Fires when the editor switches edit modes
26172              * @param {HtmlEditor} this
26173              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26174              */
26175             editmodechange: true,
26176             /**
26177              * @event editorevent
26178              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26179              * @param {HtmlEditor} this
26180              */
26181             editorevent: true,
26182             /**
26183              * @event firstfocus
26184              * Fires when on first focus - needed by toolbars..
26185              * @param {HtmlEditor} this
26186              */
26187             firstfocus: true,
26188             /**
26189              * @event autosave
26190              * Auto save the htmlEditor value as a file into Events
26191              * @param {HtmlEditor} this
26192              */
26193             autosave: true,
26194             /**
26195              * @event savedpreview
26196              * preview the saved version of htmlEditor
26197              * @param {HtmlEditor} this
26198              */
26199             savedpreview: true
26200         });
26201 };
26202
26203
26204 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26205     
26206     
26207       /**
26208      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26209      */
26210     toolbars : false,
26211     
26212      /**
26213     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26214     */
26215     btns : [],
26216    
26217      /**
26218      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26219      *                        Roo.resizable.
26220      */
26221     resizable : false,
26222      /**
26223      * @cfg {Number} height (in pixels)
26224      */   
26225     height: 300,
26226    /**
26227      * @cfg {Number} width (in pixels)
26228      */   
26229     width: false,
26230     
26231     /**
26232      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26233      * 
26234      */
26235     stylesheets: false,
26236     
26237     // id of frame..
26238     frameId: false,
26239     
26240     // private properties
26241     validationEvent : false,
26242     deferHeight: true,
26243     initialized : false,
26244     activated : false,
26245     
26246     onFocus : Roo.emptyFn,
26247     iframePad:3,
26248     hideMode:'offsets',
26249     
26250     tbContainer : false,
26251     
26252     bodyCls : '',
26253     
26254     toolbarContainer :function() {
26255         return this.wrap.select('.x-html-editor-tb',true).first();
26256     },
26257
26258     /**
26259      * Protected method that will not generally be called directly. It
26260      * is called when the editor creates its toolbar. Override this method if you need to
26261      * add custom toolbar buttons.
26262      * @param {HtmlEditor} editor
26263      */
26264     createToolbar : function(){
26265         Roo.log('renewing');
26266         Roo.log("create toolbars");
26267         
26268         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26269         this.toolbars[0].render(this.toolbarContainer());
26270         
26271         return;
26272         
26273 //        if (!editor.toolbars || !editor.toolbars.length) {
26274 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26275 //        }
26276 //        
26277 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26278 //            editor.toolbars[i] = Roo.factory(
26279 //                    typeof(editor.toolbars[i]) == 'string' ?
26280 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26281 //                Roo.bootstrap.HtmlEditor);
26282 //            editor.toolbars[i].init(editor);
26283 //        }
26284     },
26285
26286      
26287     // private
26288     onRender : function(ct, position)
26289     {
26290        // Roo.log("Call onRender: " + this.xtype);
26291         var _t = this;
26292         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26293       
26294         this.wrap = this.inputEl().wrap({
26295             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26296         });
26297         
26298         this.editorcore.onRender(ct, position);
26299          
26300         if (this.resizable) {
26301             this.resizeEl = new Roo.Resizable(this.wrap, {
26302                 pinned : true,
26303                 wrap: true,
26304                 dynamic : true,
26305                 minHeight : this.height,
26306                 height: this.height,
26307                 handles : this.resizable,
26308                 width: this.width,
26309                 listeners : {
26310                     resize : function(r, w, h) {
26311                         _t.onResize(w,h); // -something
26312                     }
26313                 }
26314             });
26315             
26316         }
26317         this.createToolbar(this);
26318        
26319         
26320         if(!this.width && this.resizable){
26321             this.setSize(this.wrap.getSize());
26322         }
26323         if (this.resizeEl) {
26324             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26325             // should trigger onReize..
26326         }
26327         
26328     },
26329
26330     // private
26331     onResize : function(w, h)
26332     {
26333         Roo.log('resize: ' +w + ',' + h );
26334         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26335         var ew = false;
26336         var eh = false;
26337         
26338         if(this.inputEl() ){
26339             if(typeof w == 'number'){
26340                 var aw = w - this.wrap.getFrameWidth('lr');
26341                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26342                 ew = aw;
26343             }
26344             if(typeof h == 'number'){
26345                  var tbh = -11;  // fixme it needs to tool bar size!
26346                 for (var i =0; i < this.toolbars.length;i++) {
26347                     // fixme - ask toolbars for heights?
26348                     tbh += this.toolbars[i].el.getHeight();
26349                     //if (this.toolbars[i].footer) {
26350                     //    tbh += this.toolbars[i].footer.el.getHeight();
26351                     //}
26352                 }
26353               
26354                 
26355                 
26356                 
26357                 
26358                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26359                 ah -= 5; // knock a few pixes off for look..
26360                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26361                 var eh = ah;
26362             }
26363         }
26364         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26365         this.editorcore.onResize(ew,eh);
26366         
26367     },
26368
26369     /**
26370      * Toggles the editor between standard and source edit mode.
26371      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26372      */
26373     toggleSourceEdit : function(sourceEditMode)
26374     {
26375         this.editorcore.toggleSourceEdit(sourceEditMode);
26376         
26377         if(this.editorcore.sourceEditMode){
26378             Roo.log('editor - showing textarea');
26379             
26380 //            Roo.log('in');
26381 //            Roo.log(this.syncValue());
26382             this.syncValue();
26383             this.inputEl().removeClass(['hide', 'x-hidden']);
26384             this.inputEl().dom.removeAttribute('tabIndex');
26385             this.inputEl().focus();
26386         }else{
26387             Roo.log('editor - hiding textarea');
26388 //            Roo.log('out')
26389 //            Roo.log(this.pushValue()); 
26390             this.pushValue();
26391             
26392             this.inputEl().addClass(['hide', 'x-hidden']);
26393             this.inputEl().dom.setAttribute('tabIndex', -1);
26394             //this.deferFocus();
26395         }
26396          
26397         if(this.resizable){
26398             this.setSize(this.wrap.getSize());
26399         }
26400         
26401         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26402     },
26403  
26404     // private (for BoxComponent)
26405     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26406
26407     // private (for BoxComponent)
26408     getResizeEl : function(){
26409         return this.wrap;
26410     },
26411
26412     // private (for BoxComponent)
26413     getPositionEl : function(){
26414         return this.wrap;
26415     },
26416
26417     // private
26418     initEvents : function(){
26419         this.originalValue = this.getValue();
26420     },
26421
26422 //    /**
26423 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26424 //     * @method
26425 //     */
26426 //    markInvalid : Roo.emptyFn,
26427 //    /**
26428 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26429 //     * @method
26430 //     */
26431 //    clearInvalid : Roo.emptyFn,
26432
26433     setValue : function(v){
26434         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26435         this.editorcore.pushValue();
26436     },
26437
26438      
26439     // private
26440     deferFocus : function(){
26441         this.focus.defer(10, this);
26442     },
26443
26444     // doc'ed in Field
26445     focus : function(){
26446         this.editorcore.focus();
26447         
26448     },
26449       
26450
26451     // private
26452     onDestroy : function(){
26453         
26454         
26455         
26456         if(this.rendered){
26457             
26458             for (var i =0; i < this.toolbars.length;i++) {
26459                 // fixme - ask toolbars for heights?
26460                 this.toolbars[i].onDestroy();
26461             }
26462             
26463             this.wrap.dom.innerHTML = '';
26464             this.wrap.remove();
26465         }
26466     },
26467
26468     // private
26469     onFirstFocus : function(){
26470         //Roo.log("onFirstFocus");
26471         this.editorcore.onFirstFocus();
26472          for (var i =0; i < this.toolbars.length;i++) {
26473             this.toolbars[i].onFirstFocus();
26474         }
26475         
26476     },
26477     
26478     // private
26479     syncValue : function()
26480     {   
26481         this.editorcore.syncValue();
26482     },
26483     
26484     pushValue : function()
26485     {   
26486         this.editorcore.pushValue();
26487     }
26488      
26489     
26490     // hide stuff that is not compatible
26491     /**
26492      * @event blur
26493      * @hide
26494      */
26495     /**
26496      * @event change
26497      * @hide
26498      */
26499     /**
26500      * @event focus
26501      * @hide
26502      */
26503     /**
26504      * @event specialkey
26505      * @hide
26506      */
26507     /**
26508      * @cfg {String} fieldClass @hide
26509      */
26510     /**
26511      * @cfg {String} focusClass @hide
26512      */
26513     /**
26514      * @cfg {String} autoCreate @hide
26515      */
26516     /**
26517      * @cfg {String} inputType @hide
26518      */
26519      
26520     /**
26521      * @cfg {String} invalidText @hide
26522      */
26523     /**
26524      * @cfg {String} msgFx @hide
26525      */
26526     /**
26527      * @cfg {String} validateOnBlur @hide
26528      */
26529 });
26530  
26531     
26532    
26533    
26534    
26535       
26536 Roo.namespace('Roo.bootstrap.htmleditor');
26537 /**
26538  * @class Roo.bootstrap.HtmlEditorToolbar1
26539  * Basic Toolbar
26540  * 
26541  * @example
26542  * Usage:
26543  *
26544  new Roo.bootstrap.HtmlEditor({
26545     ....
26546     toolbars : [
26547         new Roo.bootstrap.HtmlEditorToolbar1({
26548             disable : { fonts: 1 , format: 1, ..., ... , ...],
26549             btns : [ .... ]
26550         })
26551     }
26552      
26553  * 
26554  * @cfg {Object} disable List of elements to disable..
26555  * @cfg {Array} btns List of additional buttons.
26556  * 
26557  * 
26558  * NEEDS Extra CSS? 
26559  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26560  */
26561  
26562 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26563 {
26564     
26565     Roo.apply(this, config);
26566     
26567     // default disabled, based on 'good practice'..
26568     this.disable = this.disable || {};
26569     Roo.applyIf(this.disable, {
26570         fontSize : true,
26571         colors : true,
26572         specialElements : true
26573     });
26574     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26575     
26576     this.editor = config.editor;
26577     this.editorcore = config.editor.editorcore;
26578     
26579     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26580     
26581     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26582     // dont call parent... till later.
26583 }
26584 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26585      
26586     bar : true,
26587     
26588     editor : false,
26589     editorcore : false,
26590     
26591     
26592     formats : [
26593         "p" ,  
26594         "h1","h2","h3","h4","h5","h6", 
26595         "pre", "code", 
26596         "abbr", "acronym", "address", "cite", "samp", "var",
26597         'div','span'
26598     ],
26599     
26600     onRender : function(ct, position)
26601     {
26602        // Roo.log("Call onRender: " + this.xtype);
26603         
26604        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26605        Roo.log(this.el);
26606        this.el.dom.style.marginBottom = '0';
26607        var _this = this;
26608        var editorcore = this.editorcore;
26609        var editor= this.editor;
26610        
26611        var children = [];
26612        var btn = function(id,cmd , toggle, handler, html){
26613        
26614             var  event = toggle ? 'toggle' : 'click';
26615        
26616             var a = {
26617                 size : 'sm',
26618                 xtype: 'Button',
26619                 xns: Roo.bootstrap,
26620                 //glyphicon : id,
26621                 fa: id,
26622                 cmd : id || cmd,
26623                 enableToggle:toggle !== false,
26624                 html : html || '',
26625                 pressed : toggle ? false : null,
26626                 listeners : {}
26627             };
26628             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26629                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26630             };
26631             children.push(a);
26632             return a;
26633        }
26634        
26635     //    var cb_box = function...
26636         
26637         var style = {
26638                 xtype: 'Button',
26639                 size : 'sm',
26640                 xns: Roo.bootstrap,
26641                 fa : 'font',
26642                 //html : 'submit'
26643                 menu : {
26644                     xtype: 'Menu',
26645                     xns: Roo.bootstrap,
26646                     items:  []
26647                 }
26648         };
26649         Roo.each(this.formats, function(f) {
26650             style.menu.items.push({
26651                 xtype :'MenuItem',
26652                 xns: Roo.bootstrap,
26653                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26654                 tagname : f,
26655                 listeners : {
26656                     click : function()
26657                     {
26658                         editorcore.insertTag(this.tagname);
26659                         editor.focus();
26660                     }
26661                 }
26662                 
26663             });
26664         });
26665         children.push(style);   
26666         
26667         btn('bold',false,true);
26668         btn('italic',false,true);
26669         btn('align-left', 'justifyleft',true);
26670         btn('align-center', 'justifycenter',true);
26671         btn('align-right' , 'justifyright',true);
26672         btn('link', false, false, function(btn) {
26673             //Roo.log("create link?");
26674             var url = prompt(this.createLinkText, this.defaultLinkValue);
26675             if(url && url != 'http:/'+'/'){
26676                 this.editorcore.relayCmd('createlink', url);
26677             }
26678         }),
26679         btn('list','insertunorderedlist',true);
26680         btn('pencil', false,true, function(btn){
26681                 Roo.log(this);
26682                 this.toggleSourceEdit(btn.pressed);
26683         });
26684         
26685         if (this.editor.btns.length > 0) {
26686             for (var i = 0; i<this.editor.btns.length; i++) {
26687                 children.push(this.editor.btns[i]);
26688             }
26689         }
26690         
26691         /*
26692         var cog = {
26693                 xtype: 'Button',
26694                 size : 'sm',
26695                 xns: Roo.bootstrap,
26696                 glyphicon : 'cog',
26697                 //html : 'submit'
26698                 menu : {
26699                     xtype: 'Menu',
26700                     xns: Roo.bootstrap,
26701                     items:  []
26702                 }
26703         };
26704         
26705         cog.menu.items.push({
26706             xtype :'MenuItem',
26707             xns: Roo.bootstrap,
26708             html : Clean styles,
26709             tagname : f,
26710             listeners : {
26711                 click : function()
26712                 {
26713                     editorcore.insertTag(this.tagname);
26714                     editor.focus();
26715                 }
26716             }
26717             
26718         });
26719        */
26720         
26721          
26722        this.xtype = 'NavSimplebar';
26723         
26724         for(var i=0;i< children.length;i++) {
26725             
26726             this.buttons.add(this.addxtypeChild(children[i]));
26727             
26728         }
26729         
26730         editor.on('editorevent', this.updateToolbar, this);
26731     },
26732     onBtnClick : function(id)
26733     {
26734        this.editorcore.relayCmd(id);
26735        this.editorcore.focus();
26736     },
26737     
26738     /**
26739      * Protected method that will not generally be called directly. It triggers
26740      * a toolbar update by reading the markup state of the current selection in the editor.
26741      */
26742     updateToolbar: function(){
26743
26744         if(!this.editorcore.activated){
26745             this.editor.onFirstFocus(); // is this neeed?
26746             return;
26747         }
26748
26749         var btns = this.buttons; 
26750         var doc = this.editorcore.doc;
26751         btns.get('bold').setActive(doc.queryCommandState('bold'));
26752         btns.get('italic').setActive(doc.queryCommandState('italic'));
26753         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26754         
26755         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26756         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26757         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26758         
26759         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26760         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26761          /*
26762         
26763         var ans = this.editorcore.getAllAncestors();
26764         if (this.formatCombo) {
26765             
26766             
26767             var store = this.formatCombo.store;
26768             this.formatCombo.setValue("");
26769             for (var i =0; i < ans.length;i++) {
26770                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26771                     // select it..
26772                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26773                     break;
26774                 }
26775             }
26776         }
26777         
26778         
26779         
26780         // hides menus... - so this cant be on a menu...
26781         Roo.bootstrap.MenuMgr.hideAll();
26782         */
26783         Roo.bootstrap.MenuMgr.hideAll();
26784         //this.editorsyncValue();
26785     },
26786     onFirstFocus: function() {
26787         this.buttons.each(function(item){
26788            item.enable();
26789         });
26790     },
26791     toggleSourceEdit : function(sourceEditMode){
26792         
26793           
26794         if(sourceEditMode){
26795             Roo.log("disabling buttons");
26796            this.buttons.each( function(item){
26797                 if(item.cmd != 'pencil'){
26798                     item.disable();
26799                 }
26800             });
26801           
26802         }else{
26803             Roo.log("enabling buttons");
26804             if(this.editorcore.initialized){
26805                 this.buttons.each( function(item){
26806                     item.enable();
26807                 });
26808             }
26809             
26810         }
26811         Roo.log("calling toggole on editor");
26812         // tell the editor that it's been pressed..
26813         this.editor.toggleSourceEdit(sourceEditMode);
26814        
26815     }
26816 });
26817
26818
26819
26820
26821  
26822 /*
26823  * - LGPL
26824  */
26825
26826 /**
26827  * @class Roo.bootstrap.Markdown
26828  * @extends Roo.bootstrap.TextArea
26829  * Bootstrap Showdown editable area
26830  * @cfg {string} content
26831  * 
26832  * @constructor
26833  * Create a new Showdown
26834  */
26835
26836 Roo.bootstrap.Markdown = function(config){
26837     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26838    
26839 };
26840
26841 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26842     
26843     editing :false,
26844     
26845     initEvents : function()
26846     {
26847         
26848         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26849         this.markdownEl = this.el.createChild({
26850             cls : 'roo-markdown-area'
26851         });
26852         this.inputEl().addClass('d-none');
26853         if (this.getValue() == '') {
26854             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26855             
26856         } else {
26857             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26858         }
26859         this.markdownEl.on('click', this.toggleTextEdit, this);
26860         this.on('blur', this.toggleTextEdit, this);
26861         this.on('specialkey', this.resizeTextArea, this);
26862     },
26863     
26864     toggleTextEdit : function()
26865     {
26866         var sh = this.markdownEl.getHeight();
26867         this.inputEl().addClass('d-none');
26868         this.markdownEl.addClass('d-none');
26869         if (!this.editing) {
26870             // show editor?
26871             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26872             this.inputEl().removeClass('d-none');
26873             this.inputEl().focus();
26874             this.editing = true;
26875             return;
26876         }
26877         // show showdown...
26878         this.updateMarkdown();
26879         this.markdownEl.removeClass('d-none');
26880         this.editing = false;
26881         return;
26882     },
26883     updateMarkdown : function()
26884     {
26885         if (this.getValue() == '') {
26886             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26887             return;
26888         }
26889  
26890         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26891     },
26892     
26893     resizeTextArea: function () {
26894         
26895         var sh = 100;
26896         Roo.log([sh, this.getValue().split("\n").length * 30]);
26897         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26898     },
26899     setValue : function(val)
26900     {
26901         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26902         if (!this.editing) {
26903             this.updateMarkdown();
26904         }
26905         
26906     },
26907     focus : function()
26908     {
26909         if (!this.editing) {
26910             this.toggleTextEdit();
26911         }
26912         
26913     }
26914
26915
26916 });
26917 /**
26918  * @class Roo.bootstrap.Table.AbstractSelectionModel
26919  * @extends Roo.util.Observable
26920  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26921  * implemented by descendant classes.  This class should not be directly instantiated.
26922  * @constructor
26923  */
26924 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26925     this.locked = false;
26926     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26927 };
26928
26929
26930 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26931     /** @ignore Called by the grid automatically. Do not call directly. */
26932     init : function(grid){
26933         this.grid = grid;
26934         this.initEvents();
26935     },
26936
26937     /**
26938      * Locks the selections.
26939      */
26940     lock : function(){
26941         this.locked = true;
26942     },
26943
26944     /**
26945      * Unlocks the selections.
26946      */
26947     unlock : function(){
26948         this.locked = false;
26949     },
26950
26951     /**
26952      * Returns true if the selections are locked.
26953      * @return {Boolean}
26954      */
26955     isLocked : function(){
26956         return this.locked;
26957     },
26958     
26959     
26960     initEvents : function ()
26961     {
26962         
26963     }
26964 });
26965 /**
26966  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26967  * @class Roo.bootstrap.Table.RowSelectionModel
26968  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26969  * It supports multiple selections and keyboard selection/navigation. 
26970  * @constructor
26971  * @param {Object} config
26972  */
26973
26974 Roo.bootstrap.Table.RowSelectionModel = function(config){
26975     Roo.apply(this, config);
26976     this.selections = new Roo.util.MixedCollection(false, function(o){
26977         return o.id;
26978     });
26979
26980     this.last = false;
26981     this.lastActive = false;
26982
26983     this.addEvents({
26984         /**
26985              * @event selectionchange
26986              * Fires when the selection changes
26987              * @param {SelectionModel} this
26988              */
26989             "selectionchange" : true,
26990         /**
26991              * @event afterselectionchange
26992              * Fires after the selection changes (eg. by key press or clicking)
26993              * @param {SelectionModel} this
26994              */
26995             "afterselectionchange" : true,
26996         /**
26997              * @event beforerowselect
26998              * Fires when a row is selected being selected, return false to cancel.
26999              * @param {SelectionModel} this
27000              * @param {Number} rowIndex The selected index
27001              * @param {Boolean} keepExisting False if other selections will be cleared
27002              */
27003             "beforerowselect" : true,
27004         /**
27005              * @event rowselect
27006              * Fires when a row is selected.
27007              * @param {SelectionModel} this
27008              * @param {Number} rowIndex The selected index
27009              * @param {Roo.data.Record} r The record
27010              */
27011             "rowselect" : true,
27012         /**
27013              * @event rowdeselect
27014              * Fires when a row is deselected.
27015              * @param {SelectionModel} this
27016              * @param {Number} rowIndex The selected index
27017              */
27018         "rowdeselect" : true
27019     });
27020     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27021     this.locked = false;
27022  };
27023
27024 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27025     /**
27026      * @cfg {Boolean} singleSelect
27027      * True to allow selection of only one row at a time (defaults to false)
27028      */
27029     singleSelect : false,
27030
27031     // private
27032     initEvents : function()
27033     {
27034
27035         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27036         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27037         //}else{ // allow click to work like normal
27038          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27039         //}
27040         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27041         this.grid.on("rowclick", this.handleMouseDown, this);
27042         
27043         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27044             "up" : function(e){
27045                 if(!e.shiftKey){
27046                     this.selectPrevious(e.shiftKey);
27047                 }else if(this.last !== false && this.lastActive !== false){
27048                     var last = this.last;
27049                     this.selectRange(this.last,  this.lastActive-1);
27050                     this.grid.getView().focusRow(this.lastActive);
27051                     if(last !== false){
27052                         this.last = last;
27053                     }
27054                 }else{
27055                     this.selectFirstRow();
27056                 }
27057                 this.fireEvent("afterselectionchange", this);
27058             },
27059             "down" : function(e){
27060                 if(!e.shiftKey){
27061                     this.selectNext(e.shiftKey);
27062                 }else if(this.last !== false && this.lastActive !== false){
27063                     var last = this.last;
27064                     this.selectRange(this.last,  this.lastActive+1);
27065                     this.grid.getView().focusRow(this.lastActive);
27066                     if(last !== false){
27067                         this.last = last;
27068                     }
27069                 }else{
27070                     this.selectFirstRow();
27071                 }
27072                 this.fireEvent("afterselectionchange", this);
27073             },
27074             scope: this
27075         });
27076         this.grid.store.on('load', function(){
27077             this.selections.clear();
27078         },this);
27079         /*
27080         var view = this.grid.view;
27081         view.on("refresh", this.onRefresh, this);
27082         view.on("rowupdated", this.onRowUpdated, this);
27083         view.on("rowremoved", this.onRemove, this);
27084         */
27085     },
27086
27087     // private
27088     onRefresh : function()
27089     {
27090         var ds = this.grid.store, i, v = this.grid.view;
27091         var s = this.selections;
27092         s.each(function(r){
27093             if((i = ds.indexOfId(r.id)) != -1){
27094                 v.onRowSelect(i);
27095             }else{
27096                 s.remove(r);
27097             }
27098         });
27099     },
27100
27101     // private
27102     onRemove : function(v, index, r){
27103         this.selections.remove(r);
27104     },
27105
27106     // private
27107     onRowUpdated : function(v, index, r){
27108         if(this.isSelected(r)){
27109             v.onRowSelect(index);
27110         }
27111     },
27112
27113     /**
27114      * Select records.
27115      * @param {Array} records The records to select
27116      * @param {Boolean} keepExisting (optional) True to keep existing selections
27117      */
27118     selectRecords : function(records, keepExisting)
27119     {
27120         if(!keepExisting){
27121             this.clearSelections();
27122         }
27123             var ds = this.grid.store;
27124         for(var i = 0, len = records.length; i < len; i++){
27125             this.selectRow(ds.indexOf(records[i]), true);
27126         }
27127     },
27128
27129     /**
27130      * Gets the number of selected rows.
27131      * @return {Number}
27132      */
27133     getCount : function(){
27134         return this.selections.length;
27135     },
27136
27137     /**
27138      * Selects the first row in the grid.
27139      */
27140     selectFirstRow : function(){
27141         this.selectRow(0);
27142     },
27143
27144     /**
27145      * Select the last row.
27146      * @param {Boolean} keepExisting (optional) True to keep existing selections
27147      */
27148     selectLastRow : function(keepExisting){
27149         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27150         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27151     },
27152
27153     /**
27154      * Selects the row immediately following the last selected row.
27155      * @param {Boolean} keepExisting (optional) True to keep existing selections
27156      */
27157     selectNext : function(keepExisting)
27158     {
27159             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27160             this.selectRow(this.last+1, keepExisting);
27161             this.grid.getView().focusRow(this.last);
27162         }
27163     },
27164
27165     /**
27166      * Selects the row that precedes the last selected row.
27167      * @param {Boolean} keepExisting (optional) True to keep existing selections
27168      */
27169     selectPrevious : function(keepExisting){
27170         if(this.last){
27171             this.selectRow(this.last-1, keepExisting);
27172             this.grid.getView().focusRow(this.last);
27173         }
27174     },
27175
27176     /**
27177      * Returns the selected records
27178      * @return {Array} Array of selected records
27179      */
27180     getSelections : function(){
27181         return [].concat(this.selections.items);
27182     },
27183
27184     /**
27185      * Returns the first selected record.
27186      * @return {Record}
27187      */
27188     getSelected : function(){
27189         return this.selections.itemAt(0);
27190     },
27191
27192
27193     /**
27194      * Clears all selections.
27195      */
27196     clearSelections : function(fast)
27197     {
27198         if(this.locked) {
27199             return;
27200         }
27201         if(fast !== true){
27202                 var ds = this.grid.store;
27203             var s = this.selections;
27204             s.each(function(r){
27205                 this.deselectRow(ds.indexOfId(r.id));
27206             }, this);
27207             s.clear();
27208         }else{
27209             this.selections.clear();
27210         }
27211         this.last = false;
27212     },
27213
27214
27215     /**
27216      * Selects all rows.
27217      */
27218     selectAll : function(){
27219         if(this.locked) {
27220             return;
27221         }
27222         this.selections.clear();
27223         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27224             this.selectRow(i, true);
27225         }
27226     },
27227
27228     /**
27229      * Returns True if there is a selection.
27230      * @return {Boolean}
27231      */
27232     hasSelection : function(){
27233         return this.selections.length > 0;
27234     },
27235
27236     /**
27237      * Returns True if the specified row is selected.
27238      * @param {Number/Record} record The record or index of the record to check
27239      * @return {Boolean}
27240      */
27241     isSelected : function(index){
27242             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27243         return (r && this.selections.key(r.id) ? true : false);
27244     },
27245
27246     /**
27247      * Returns True if the specified record id is selected.
27248      * @param {String} id The id of record to check
27249      * @return {Boolean}
27250      */
27251     isIdSelected : function(id){
27252         return (this.selections.key(id) ? true : false);
27253     },
27254
27255
27256     // private
27257     handleMouseDBClick : function(e, t){
27258         
27259     },
27260     // private
27261     handleMouseDown : function(e, t)
27262     {
27263             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27264         if(this.isLocked() || rowIndex < 0 ){
27265             return;
27266         };
27267         if(e.shiftKey && this.last !== false){
27268             var last = this.last;
27269             this.selectRange(last, rowIndex, e.ctrlKey);
27270             this.last = last; // reset the last
27271             t.focus();
27272     
27273         }else{
27274             var isSelected = this.isSelected(rowIndex);
27275             //Roo.log("select row:" + rowIndex);
27276             if(isSelected){
27277                 this.deselectRow(rowIndex);
27278             } else {
27279                         this.selectRow(rowIndex, true);
27280             }
27281     
27282             /*
27283                 if(e.button !== 0 && isSelected){
27284                 alert('rowIndex 2: ' + rowIndex);
27285                     view.focusRow(rowIndex);
27286                 }else if(e.ctrlKey && isSelected){
27287                     this.deselectRow(rowIndex);
27288                 }else if(!isSelected){
27289                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27290                     view.focusRow(rowIndex);
27291                 }
27292             */
27293         }
27294         this.fireEvent("afterselectionchange", this);
27295     },
27296     // private
27297     handleDragableRowClick :  function(grid, rowIndex, e) 
27298     {
27299         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27300             this.selectRow(rowIndex, false);
27301             grid.view.focusRow(rowIndex);
27302              this.fireEvent("afterselectionchange", this);
27303         }
27304     },
27305     
27306     /**
27307      * Selects multiple rows.
27308      * @param {Array} rows Array of the indexes of the row to select
27309      * @param {Boolean} keepExisting (optional) True to keep existing selections
27310      */
27311     selectRows : function(rows, keepExisting){
27312         if(!keepExisting){
27313             this.clearSelections();
27314         }
27315         for(var i = 0, len = rows.length; i < len; i++){
27316             this.selectRow(rows[i], true);
27317         }
27318     },
27319
27320     /**
27321      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27322      * @param {Number} startRow The index of the first row in the range
27323      * @param {Number} endRow The index of the last row in the range
27324      * @param {Boolean} keepExisting (optional) True to retain existing selections
27325      */
27326     selectRange : function(startRow, endRow, keepExisting){
27327         if(this.locked) {
27328             return;
27329         }
27330         if(!keepExisting){
27331             this.clearSelections();
27332         }
27333         if(startRow <= endRow){
27334             for(var i = startRow; i <= endRow; i++){
27335                 this.selectRow(i, true);
27336             }
27337         }else{
27338             for(var i = startRow; i >= endRow; i--){
27339                 this.selectRow(i, true);
27340             }
27341         }
27342     },
27343
27344     /**
27345      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27346      * @param {Number} startRow The index of the first row in the range
27347      * @param {Number} endRow The index of the last row in the range
27348      */
27349     deselectRange : function(startRow, endRow, preventViewNotify){
27350         if(this.locked) {
27351             return;
27352         }
27353         for(var i = startRow; i <= endRow; i++){
27354             this.deselectRow(i, preventViewNotify);
27355         }
27356     },
27357
27358     /**
27359      * Selects a row.
27360      * @param {Number} row The index of the row to select
27361      * @param {Boolean} keepExisting (optional) True to keep existing selections
27362      */
27363     selectRow : function(index, keepExisting, preventViewNotify)
27364     {
27365             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27366             return;
27367         }
27368         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27369             if(!keepExisting || this.singleSelect){
27370                 this.clearSelections();
27371             }
27372             
27373             var r = this.grid.store.getAt(index);
27374             //console.log('selectRow - record id :' + r.id);
27375             
27376             this.selections.add(r);
27377             this.last = this.lastActive = index;
27378             if(!preventViewNotify){
27379                 var proxy = new Roo.Element(
27380                                 this.grid.getRowDom(index)
27381                 );
27382                 proxy.addClass('bg-info info');
27383             }
27384             this.fireEvent("rowselect", this, index, r);
27385             this.fireEvent("selectionchange", this);
27386         }
27387     },
27388
27389     /**
27390      * Deselects a row.
27391      * @param {Number} row The index of the row to deselect
27392      */
27393     deselectRow : function(index, preventViewNotify)
27394     {
27395         if(this.locked) {
27396             return;
27397         }
27398         if(this.last == index){
27399             this.last = false;
27400         }
27401         if(this.lastActive == index){
27402             this.lastActive = false;
27403         }
27404         
27405         var r = this.grid.store.getAt(index);
27406         if (!r) {
27407             return;
27408         }
27409         
27410         this.selections.remove(r);
27411         //.console.log('deselectRow - record id :' + r.id);
27412         if(!preventViewNotify){
27413         
27414             var proxy = new Roo.Element(
27415                 this.grid.getRowDom(index)
27416             );
27417             proxy.removeClass('bg-info info');
27418         }
27419         this.fireEvent("rowdeselect", this, index);
27420         this.fireEvent("selectionchange", this);
27421     },
27422
27423     // private
27424     restoreLast : function(){
27425         if(this._last){
27426             this.last = this._last;
27427         }
27428     },
27429
27430     // private
27431     acceptsNav : function(row, col, cm){
27432         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27433     },
27434
27435     // private
27436     onEditorKey : function(field, e){
27437         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27438         if(k == e.TAB){
27439             e.stopEvent();
27440             ed.completeEdit();
27441             if(e.shiftKey){
27442                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27443             }else{
27444                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27445             }
27446         }else if(k == e.ENTER && !e.ctrlKey){
27447             e.stopEvent();
27448             ed.completeEdit();
27449             if(e.shiftKey){
27450                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27451             }else{
27452                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27453             }
27454         }else if(k == e.ESC){
27455             ed.cancelEdit();
27456         }
27457         if(newCell){
27458             g.startEditing(newCell[0], newCell[1]);
27459         }
27460     }
27461 });
27462 /*
27463  * Based on:
27464  * Ext JS Library 1.1.1
27465  * Copyright(c) 2006-2007, Ext JS, LLC.
27466  *
27467  * Originally Released Under LGPL - original licence link has changed is not relivant.
27468  *
27469  * Fork - LGPL
27470  * <script type="text/javascript">
27471  */
27472  
27473 /**
27474  * @class Roo.bootstrap.PagingToolbar
27475  * @extends Roo.bootstrap.NavSimplebar
27476  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27477  * @constructor
27478  * Create a new PagingToolbar
27479  * @param {Object} config The config object
27480  * @param {Roo.data.Store} store
27481  */
27482 Roo.bootstrap.PagingToolbar = function(config)
27483 {
27484     // old args format still supported... - xtype is prefered..
27485         // created from xtype...
27486     
27487     this.ds = config.dataSource;
27488     
27489     if (config.store && !this.ds) {
27490         this.store= Roo.factory(config.store, Roo.data);
27491         this.ds = this.store;
27492         this.ds.xmodule = this.xmodule || false;
27493     }
27494     
27495     this.toolbarItems = [];
27496     if (config.items) {
27497         this.toolbarItems = config.items;
27498     }
27499     
27500     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27501     
27502     this.cursor = 0;
27503     
27504     if (this.ds) { 
27505         this.bind(this.ds);
27506     }
27507     
27508     if (Roo.bootstrap.version == 4) {
27509         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27510     } else {
27511         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27512     }
27513     
27514 };
27515
27516 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27517     /**
27518      * @cfg {Roo.data.Store} dataSource
27519      * The underlying data store providing the paged data
27520      */
27521     /**
27522      * @cfg {String/HTMLElement/Element} container
27523      * container The id or element that will contain the toolbar
27524      */
27525     /**
27526      * @cfg {Boolean} displayInfo
27527      * True to display the displayMsg (defaults to false)
27528      */
27529     /**
27530      * @cfg {Number} pageSize
27531      * The number of records to display per page (defaults to 20)
27532      */
27533     pageSize: 20,
27534     /**
27535      * @cfg {String} displayMsg
27536      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27537      */
27538     displayMsg : 'Displaying {0} - {1} of {2}',
27539     /**
27540      * @cfg {String} emptyMsg
27541      * The message to display when no records are found (defaults to "No data to display")
27542      */
27543     emptyMsg : 'No data to display',
27544     /**
27545      * Customizable piece of the default paging text (defaults to "Page")
27546      * @type String
27547      */
27548     beforePageText : "Page",
27549     /**
27550      * Customizable piece of the default paging text (defaults to "of %0")
27551      * @type String
27552      */
27553     afterPageText : "of {0}",
27554     /**
27555      * Customizable piece of the default paging text (defaults to "First Page")
27556      * @type String
27557      */
27558     firstText : "First Page",
27559     /**
27560      * Customizable piece of the default paging text (defaults to "Previous Page")
27561      * @type String
27562      */
27563     prevText : "Previous Page",
27564     /**
27565      * Customizable piece of the default paging text (defaults to "Next Page")
27566      * @type String
27567      */
27568     nextText : "Next Page",
27569     /**
27570      * Customizable piece of the default paging text (defaults to "Last Page")
27571      * @type String
27572      */
27573     lastText : "Last Page",
27574     /**
27575      * Customizable piece of the default paging text (defaults to "Refresh")
27576      * @type String
27577      */
27578     refreshText : "Refresh",
27579
27580     buttons : false,
27581     // private
27582     onRender : function(ct, position) 
27583     {
27584         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27585         this.navgroup.parentId = this.id;
27586         this.navgroup.onRender(this.el, null);
27587         // add the buttons to the navgroup
27588         
27589         if(this.displayInfo){
27590             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27591             this.displayEl = this.el.select('.x-paging-info', true).first();
27592 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27593 //            this.displayEl = navel.el.select('span',true).first();
27594         }
27595         
27596         var _this = this;
27597         
27598         if(this.buttons){
27599             Roo.each(_this.buttons, function(e){ // this might need to use render????
27600                Roo.factory(e).render(_this.el);
27601             });
27602         }
27603             
27604         Roo.each(_this.toolbarItems, function(e) {
27605             _this.navgroup.addItem(e);
27606         });
27607         
27608         
27609         this.first = this.navgroup.addItem({
27610             tooltip: this.firstText,
27611             cls: "prev btn-outline-secondary",
27612             html : ' <i class="fa fa-step-backward"></i>',
27613             disabled: true,
27614             preventDefault: true,
27615             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27616         });
27617         
27618         this.prev =  this.navgroup.addItem({
27619             tooltip: this.prevText,
27620             cls: "prev btn-outline-secondary",
27621             html : ' <i class="fa fa-backward"></i>',
27622             disabled: true,
27623             preventDefault: true,
27624             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27625         });
27626     //this.addSeparator();
27627         
27628         
27629         var field = this.navgroup.addItem( {
27630             tagtype : 'span',
27631             cls : 'x-paging-position  btn-outline-secondary',
27632              disabled: true,
27633             html : this.beforePageText  +
27634                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27635                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27636          } ); //?? escaped?
27637         
27638         this.field = field.el.select('input', true).first();
27639         this.field.on("keydown", this.onPagingKeydown, this);
27640         this.field.on("focus", function(){this.dom.select();});
27641     
27642     
27643         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27644         //this.field.setHeight(18);
27645         //this.addSeparator();
27646         this.next = this.navgroup.addItem({
27647             tooltip: this.nextText,
27648             cls: "next btn-outline-secondary",
27649             html : ' <i class="fa fa-forward"></i>',
27650             disabled: true,
27651             preventDefault: true,
27652             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27653         });
27654         this.last = this.navgroup.addItem({
27655             tooltip: this.lastText,
27656             html : ' <i class="fa fa-step-forward"></i>',
27657             cls: "next btn-outline-secondary",
27658             disabled: true,
27659             preventDefault: true,
27660             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27661         });
27662     //this.addSeparator();
27663         this.loading = this.navgroup.addItem({
27664             tooltip: this.refreshText,
27665             cls: "btn-outline-secondary",
27666             html : ' <i class="fa fa-refresh"></i>',
27667             preventDefault: true,
27668             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27669         });
27670         
27671     },
27672
27673     // private
27674     updateInfo : function(){
27675         if(this.displayEl){
27676             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27677             var msg = count == 0 ?
27678                 this.emptyMsg :
27679                 String.format(
27680                     this.displayMsg,
27681                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27682                 );
27683             this.displayEl.update(msg);
27684         }
27685     },
27686
27687     // private
27688     onLoad : function(ds, r, o)
27689     {
27690         this.cursor = o.params && o.params.start ? o.params.start : 0;
27691         
27692         var d = this.getPageData(),
27693             ap = d.activePage,
27694             ps = d.pages;
27695         
27696         
27697         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27698         this.field.dom.value = ap;
27699         this.first.setDisabled(ap == 1);
27700         this.prev.setDisabled(ap == 1);
27701         this.next.setDisabled(ap == ps);
27702         this.last.setDisabled(ap == ps);
27703         this.loading.enable();
27704         this.updateInfo();
27705     },
27706
27707     // private
27708     getPageData : function(){
27709         var total = this.ds.getTotalCount();
27710         return {
27711             total : total,
27712             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27713             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27714         };
27715     },
27716
27717     // private
27718     onLoadError : function(){
27719         this.loading.enable();
27720     },
27721
27722     // private
27723     onPagingKeydown : function(e){
27724         var k = e.getKey();
27725         var d = this.getPageData();
27726         if(k == e.RETURN){
27727             var v = this.field.dom.value, pageNum;
27728             if(!v || isNaN(pageNum = parseInt(v, 10))){
27729                 this.field.dom.value = d.activePage;
27730                 return;
27731             }
27732             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27733             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27734             e.stopEvent();
27735         }
27736         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))
27737         {
27738           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27739           this.field.dom.value = pageNum;
27740           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27741           e.stopEvent();
27742         }
27743         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27744         {
27745           var v = this.field.dom.value, pageNum; 
27746           var increment = (e.shiftKey) ? 10 : 1;
27747           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27748                 increment *= -1;
27749           }
27750           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27751             this.field.dom.value = d.activePage;
27752             return;
27753           }
27754           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27755           {
27756             this.field.dom.value = parseInt(v, 10) + increment;
27757             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27758             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27759           }
27760           e.stopEvent();
27761         }
27762     },
27763
27764     // private
27765     beforeLoad : function(){
27766         if(this.loading){
27767             this.loading.disable();
27768         }
27769     },
27770
27771     // private
27772     onClick : function(which){
27773         
27774         var ds = this.ds;
27775         if (!ds) {
27776             return;
27777         }
27778         
27779         switch(which){
27780             case "first":
27781                 ds.load({params:{start: 0, limit: this.pageSize}});
27782             break;
27783             case "prev":
27784                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27785             break;
27786             case "next":
27787                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27788             break;
27789             case "last":
27790                 var total = ds.getTotalCount();
27791                 var extra = total % this.pageSize;
27792                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27793                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27794             break;
27795             case "refresh":
27796                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27797             break;
27798         }
27799     },
27800
27801     /**
27802      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27803      * @param {Roo.data.Store} store The data store to unbind
27804      */
27805     unbind : function(ds){
27806         ds.un("beforeload", this.beforeLoad, this);
27807         ds.un("load", this.onLoad, this);
27808         ds.un("loadexception", this.onLoadError, this);
27809         ds.un("remove", this.updateInfo, this);
27810         ds.un("add", this.updateInfo, this);
27811         this.ds = undefined;
27812     },
27813
27814     /**
27815      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27816      * @param {Roo.data.Store} store The data store to bind
27817      */
27818     bind : function(ds){
27819         ds.on("beforeload", this.beforeLoad, this);
27820         ds.on("load", this.onLoad, this);
27821         ds.on("loadexception", this.onLoadError, this);
27822         ds.on("remove", this.updateInfo, this);
27823         ds.on("add", this.updateInfo, this);
27824         this.ds = ds;
27825     }
27826 });/*
27827  * - LGPL
27828  *
27829  * element
27830  * 
27831  */
27832
27833 /**
27834  * @class Roo.bootstrap.MessageBar
27835  * @extends Roo.bootstrap.Component
27836  * Bootstrap MessageBar class
27837  * @cfg {String} html contents of the MessageBar
27838  * @cfg {String} weight (info | success | warning | danger) default info
27839  * @cfg {String} beforeClass insert the bar before the given class
27840  * @cfg {Boolean} closable (true | false) default false
27841  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27842  * 
27843  * @constructor
27844  * Create a new Element
27845  * @param {Object} config The config object
27846  */
27847
27848 Roo.bootstrap.MessageBar = function(config){
27849     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27850 };
27851
27852 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27853     
27854     html: '',
27855     weight: 'info',
27856     closable: false,
27857     fixed: false,
27858     beforeClass: 'bootstrap-sticky-wrap',
27859     
27860     getAutoCreate : function(){
27861         
27862         var cfg = {
27863             tag: 'div',
27864             cls: 'alert alert-dismissable alert-' + this.weight,
27865             cn: [
27866                 {
27867                     tag: 'span',
27868                     cls: 'message',
27869                     html: this.html || ''
27870                 }
27871             ]
27872         };
27873         
27874         if(this.fixed){
27875             cfg.cls += ' alert-messages-fixed';
27876         }
27877         
27878         if(this.closable){
27879             cfg.cn.push({
27880                 tag: 'button',
27881                 cls: 'close',
27882                 html: 'x'
27883             });
27884         }
27885         
27886         return cfg;
27887     },
27888     
27889     onRender : function(ct, position)
27890     {
27891         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27892         
27893         if(!this.el){
27894             var cfg = Roo.apply({},  this.getAutoCreate());
27895             cfg.id = Roo.id();
27896             
27897             if (this.cls) {
27898                 cfg.cls += ' ' + this.cls;
27899             }
27900             if (this.style) {
27901                 cfg.style = this.style;
27902             }
27903             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27904             
27905             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27906         }
27907         
27908         this.el.select('>button.close').on('click', this.hide, this);
27909         
27910     },
27911     
27912     show : function()
27913     {
27914         if (!this.rendered) {
27915             this.render();
27916         }
27917         
27918         this.el.show();
27919         
27920         this.fireEvent('show', this);
27921         
27922     },
27923     
27924     hide : function()
27925     {
27926         if (!this.rendered) {
27927             this.render();
27928         }
27929         
27930         this.el.hide();
27931         
27932         this.fireEvent('hide', this);
27933     },
27934     
27935     update : function()
27936     {
27937 //        var e = this.el.dom.firstChild;
27938 //        
27939 //        if(this.closable){
27940 //            e = e.nextSibling;
27941 //        }
27942 //        
27943 //        e.data = this.html || '';
27944
27945         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27946     }
27947    
27948 });
27949
27950  
27951
27952      /*
27953  * - LGPL
27954  *
27955  * Graph
27956  * 
27957  */
27958
27959
27960 /**
27961  * @class Roo.bootstrap.Graph
27962  * @extends Roo.bootstrap.Component
27963  * Bootstrap Graph class
27964 > Prameters
27965  -sm {number} sm 4
27966  -md {number} md 5
27967  @cfg {String} graphtype  bar | vbar | pie
27968  @cfg {number} g_x coodinator | centre x (pie)
27969  @cfg {number} g_y coodinator | centre y (pie)
27970  @cfg {number} g_r radius (pie)
27971  @cfg {number} g_height height of the chart (respected by all elements in the set)
27972  @cfg {number} g_width width of the chart (respected by all elements in the set)
27973  @cfg {Object} title The title of the chart
27974     
27975  -{Array}  values
27976  -opts (object) options for the chart 
27977      o {
27978      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27979      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27980      o vgutter (number)
27981      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.
27982      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27983      o to
27984      o stretch (boolean)
27985      o }
27986  -opts (object) options for the pie
27987      o{
27988      o cut
27989      o startAngle (number)
27990      o endAngle (number)
27991      } 
27992  *
27993  * @constructor
27994  * Create a new Input
27995  * @param {Object} config The config object
27996  */
27997
27998 Roo.bootstrap.Graph = function(config){
27999     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28000     
28001     this.addEvents({
28002         // img events
28003         /**
28004          * @event click
28005          * The img click event for the img.
28006          * @param {Roo.EventObject} e
28007          */
28008         "click" : true
28009     });
28010 };
28011
28012 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28013     
28014     sm: 4,
28015     md: 5,
28016     graphtype: 'bar',
28017     g_height: 250,
28018     g_width: 400,
28019     g_x: 50,
28020     g_y: 50,
28021     g_r: 30,
28022     opts:{
28023         //g_colors: this.colors,
28024         g_type: 'soft',
28025         g_gutter: '20%'
28026
28027     },
28028     title : false,
28029
28030     getAutoCreate : function(){
28031         
28032         var cfg = {
28033             tag: 'div',
28034             html : null
28035         };
28036         
28037         
28038         return  cfg;
28039     },
28040
28041     onRender : function(ct,position){
28042         
28043         
28044         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28045         
28046         if (typeof(Raphael) == 'undefined') {
28047             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28048             return;
28049         }
28050         
28051         this.raphael = Raphael(this.el.dom);
28052         
28053                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28054                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28055                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28056                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28057                 /*
28058                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28059                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28060                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28061                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28062                 
28063                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28064                 r.barchart(330, 10, 300, 220, data1);
28065                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28066                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28067                 */
28068                 
28069                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28070                 // r.barchart(30, 30, 560, 250,  xdata, {
28071                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28072                 //     axis : "0 0 1 1",
28073                 //     axisxlabels :  xdata
28074                 //     //yvalues : cols,
28075                    
28076                 // });
28077 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28078 //        
28079 //        this.load(null,xdata,{
28080 //                axis : "0 0 1 1",
28081 //                axisxlabels :  xdata
28082 //                });
28083
28084     },
28085
28086     load : function(graphtype,xdata,opts)
28087     {
28088         this.raphael.clear();
28089         if(!graphtype) {
28090             graphtype = this.graphtype;
28091         }
28092         if(!opts){
28093             opts = this.opts;
28094         }
28095         var r = this.raphael,
28096             fin = function () {
28097                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28098             },
28099             fout = function () {
28100                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28101             },
28102             pfin = function() {
28103                 this.sector.stop();
28104                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28105
28106                 if (this.label) {
28107                     this.label[0].stop();
28108                     this.label[0].attr({ r: 7.5 });
28109                     this.label[1].attr({ "font-weight": 800 });
28110                 }
28111             },
28112             pfout = function() {
28113                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28114
28115                 if (this.label) {
28116                     this.label[0].animate({ r: 5 }, 500, "bounce");
28117                     this.label[1].attr({ "font-weight": 400 });
28118                 }
28119             };
28120
28121         switch(graphtype){
28122             case 'bar':
28123                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28124                 break;
28125             case 'hbar':
28126                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28127                 break;
28128             case 'pie':
28129 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28130 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28131 //            
28132                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28133                 
28134                 break;
28135
28136         }
28137         
28138         if(this.title){
28139             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28140         }
28141         
28142     },
28143     
28144     setTitle: function(o)
28145     {
28146         this.title = o;
28147     },
28148     
28149     initEvents: function() {
28150         
28151         if(!this.href){
28152             this.el.on('click', this.onClick, this);
28153         }
28154     },
28155     
28156     onClick : function(e)
28157     {
28158         Roo.log('img onclick');
28159         this.fireEvent('click', this, e);
28160     }
28161    
28162 });
28163
28164  
28165 /*
28166  * - LGPL
28167  *
28168  * numberBox
28169  * 
28170  */
28171 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28172
28173 /**
28174  * @class Roo.bootstrap.dash.NumberBox
28175  * @extends Roo.bootstrap.Component
28176  * Bootstrap NumberBox class
28177  * @cfg {String} headline Box headline
28178  * @cfg {String} content Box content
28179  * @cfg {String} icon Box icon
28180  * @cfg {String} footer Footer text
28181  * @cfg {String} fhref Footer href
28182  * 
28183  * @constructor
28184  * Create a new NumberBox
28185  * @param {Object} config The config object
28186  */
28187
28188
28189 Roo.bootstrap.dash.NumberBox = function(config){
28190     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28191     
28192 };
28193
28194 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28195     
28196     headline : '',
28197     content : '',
28198     icon : '',
28199     footer : '',
28200     fhref : '',
28201     ficon : '',
28202     
28203     getAutoCreate : function(){
28204         
28205         var cfg = {
28206             tag : 'div',
28207             cls : 'small-box ',
28208             cn : [
28209                 {
28210                     tag : 'div',
28211                     cls : 'inner',
28212                     cn :[
28213                         {
28214                             tag : 'h3',
28215                             cls : 'roo-headline',
28216                             html : this.headline
28217                         },
28218                         {
28219                             tag : 'p',
28220                             cls : 'roo-content',
28221                             html : this.content
28222                         }
28223                     ]
28224                 }
28225             ]
28226         };
28227         
28228         if(this.icon){
28229             cfg.cn.push({
28230                 tag : 'div',
28231                 cls : 'icon',
28232                 cn :[
28233                     {
28234                         tag : 'i',
28235                         cls : 'ion ' + this.icon
28236                     }
28237                 ]
28238             });
28239         }
28240         
28241         if(this.footer){
28242             var footer = {
28243                 tag : 'a',
28244                 cls : 'small-box-footer',
28245                 href : this.fhref || '#',
28246                 html : this.footer
28247             };
28248             
28249             cfg.cn.push(footer);
28250             
28251         }
28252         
28253         return  cfg;
28254     },
28255
28256     onRender : function(ct,position){
28257         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28258
28259
28260        
28261                 
28262     },
28263
28264     setHeadline: function (value)
28265     {
28266         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28267     },
28268     
28269     setFooter: function (value, href)
28270     {
28271         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28272         
28273         if(href){
28274             this.el.select('a.small-box-footer',true).first().attr('href', href);
28275         }
28276         
28277     },
28278
28279     setContent: function (value)
28280     {
28281         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28282     },
28283
28284     initEvents: function() 
28285     {   
28286         
28287     }
28288     
28289 });
28290
28291  
28292 /*
28293  * - LGPL
28294  *
28295  * TabBox
28296  * 
28297  */
28298 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28299
28300 /**
28301  * @class Roo.bootstrap.dash.TabBox
28302  * @extends Roo.bootstrap.Component
28303  * Bootstrap TabBox class
28304  * @cfg {String} title Title of the TabBox
28305  * @cfg {String} icon Icon of the TabBox
28306  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28307  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28308  * 
28309  * @constructor
28310  * Create a new TabBox
28311  * @param {Object} config The config object
28312  */
28313
28314
28315 Roo.bootstrap.dash.TabBox = function(config){
28316     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28317     this.addEvents({
28318         // raw events
28319         /**
28320          * @event addpane
28321          * When a pane is added
28322          * @param {Roo.bootstrap.dash.TabPane} pane
28323          */
28324         "addpane" : true,
28325         /**
28326          * @event activatepane
28327          * When a pane is activated
28328          * @param {Roo.bootstrap.dash.TabPane} pane
28329          */
28330         "activatepane" : true
28331         
28332          
28333     });
28334     
28335     this.panes = [];
28336 };
28337
28338 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28339
28340     title : '',
28341     icon : false,
28342     showtabs : true,
28343     tabScrollable : false,
28344     
28345     getChildContainer : function()
28346     {
28347         return this.el.select('.tab-content', true).first();
28348     },
28349     
28350     getAutoCreate : function(){
28351         
28352         var header = {
28353             tag: 'li',
28354             cls: 'pull-left header',
28355             html: this.title,
28356             cn : []
28357         };
28358         
28359         if(this.icon){
28360             header.cn.push({
28361                 tag: 'i',
28362                 cls: 'fa ' + this.icon
28363             });
28364         }
28365         
28366         var h = {
28367             tag: 'ul',
28368             cls: 'nav nav-tabs pull-right',
28369             cn: [
28370                 header
28371             ]
28372         };
28373         
28374         if(this.tabScrollable){
28375             h = {
28376                 tag: 'div',
28377                 cls: 'tab-header',
28378                 cn: [
28379                     {
28380                         tag: 'ul',
28381                         cls: 'nav nav-tabs pull-right',
28382                         cn: [
28383                             header
28384                         ]
28385                     }
28386                 ]
28387             };
28388         }
28389         
28390         var cfg = {
28391             tag: 'div',
28392             cls: 'nav-tabs-custom',
28393             cn: [
28394                 h,
28395                 {
28396                     tag: 'div',
28397                     cls: 'tab-content no-padding',
28398                     cn: []
28399                 }
28400             ]
28401         };
28402
28403         return  cfg;
28404     },
28405     initEvents : function()
28406     {
28407         //Roo.log('add add pane handler');
28408         this.on('addpane', this.onAddPane, this);
28409     },
28410      /**
28411      * Updates the box title
28412      * @param {String} html to set the title to.
28413      */
28414     setTitle : function(value)
28415     {
28416         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28417     },
28418     onAddPane : function(pane)
28419     {
28420         this.panes.push(pane);
28421         //Roo.log('addpane');
28422         //Roo.log(pane);
28423         // tabs are rendere left to right..
28424         if(!this.showtabs){
28425             return;
28426         }
28427         
28428         var ctr = this.el.select('.nav-tabs', true).first();
28429          
28430          
28431         var existing = ctr.select('.nav-tab',true);
28432         var qty = existing.getCount();;
28433         
28434         
28435         var tab = ctr.createChild({
28436             tag : 'li',
28437             cls : 'nav-tab' + (qty ? '' : ' active'),
28438             cn : [
28439                 {
28440                     tag : 'a',
28441                     href:'#',
28442                     html : pane.title
28443                 }
28444             ]
28445         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28446         pane.tab = tab;
28447         
28448         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28449         if (!qty) {
28450             pane.el.addClass('active');
28451         }
28452         
28453                 
28454     },
28455     onTabClick : function(ev,un,ob,pane)
28456     {
28457         //Roo.log('tab - prev default');
28458         ev.preventDefault();
28459         
28460         
28461         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28462         pane.tab.addClass('active');
28463         //Roo.log(pane.title);
28464         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28465         // technically we should have a deactivate event.. but maybe add later.
28466         // and it should not de-activate the selected tab...
28467         this.fireEvent('activatepane', pane);
28468         pane.el.addClass('active');
28469         pane.fireEvent('activate');
28470         
28471         
28472     },
28473     
28474     getActivePane : function()
28475     {
28476         var r = false;
28477         Roo.each(this.panes, function(p) {
28478             if(p.el.hasClass('active')){
28479                 r = p;
28480                 return false;
28481             }
28482             
28483             return;
28484         });
28485         
28486         return r;
28487     }
28488     
28489     
28490 });
28491
28492  
28493 /*
28494  * - LGPL
28495  *
28496  * Tab pane
28497  * 
28498  */
28499 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28500 /**
28501  * @class Roo.bootstrap.TabPane
28502  * @extends Roo.bootstrap.Component
28503  * Bootstrap TabPane class
28504  * @cfg {Boolean} active (false | true) Default false
28505  * @cfg {String} title title of panel
28506
28507  * 
28508  * @constructor
28509  * Create a new TabPane
28510  * @param {Object} config The config object
28511  */
28512
28513 Roo.bootstrap.dash.TabPane = function(config){
28514     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28515     
28516     this.addEvents({
28517         // raw events
28518         /**
28519          * @event activate
28520          * When a pane is activated
28521          * @param {Roo.bootstrap.dash.TabPane} pane
28522          */
28523         "activate" : true
28524          
28525     });
28526 };
28527
28528 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28529     
28530     active : false,
28531     title : '',
28532     
28533     // the tabBox that this is attached to.
28534     tab : false,
28535      
28536     getAutoCreate : function() 
28537     {
28538         var cfg = {
28539             tag: 'div',
28540             cls: 'tab-pane'
28541         };
28542         
28543         if(this.active){
28544             cfg.cls += ' active';
28545         }
28546         
28547         return cfg;
28548     },
28549     initEvents  : function()
28550     {
28551         //Roo.log('trigger add pane handler');
28552         this.parent().fireEvent('addpane', this)
28553     },
28554     
28555      /**
28556      * Updates the tab title 
28557      * @param {String} html to set the title to.
28558      */
28559     setTitle: function(str)
28560     {
28561         if (!this.tab) {
28562             return;
28563         }
28564         this.title = str;
28565         this.tab.select('a', true).first().dom.innerHTML = str;
28566         
28567     }
28568     
28569     
28570     
28571 });
28572
28573  
28574
28575
28576  /*
28577  * - LGPL
28578  *
28579  * menu
28580  * 
28581  */
28582 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28583
28584 /**
28585  * @class Roo.bootstrap.menu.Menu
28586  * @extends Roo.bootstrap.Component
28587  * Bootstrap Menu class - container for Menu
28588  * @cfg {String} html Text of the menu
28589  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28590  * @cfg {String} icon Font awesome icon
28591  * @cfg {String} pos Menu align to (top | bottom) default bottom
28592  * 
28593  * 
28594  * @constructor
28595  * Create a new Menu
28596  * @param {Object} config The config object
28597  */
28598
28599
28600 Roo.bootstrap.menu.Menu = function(config){
28601     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28602     
28603     this.addEvents({
28604         /**
28605          * @event beforeshow
28606          * Fires before this menu is displayed
28607          * @param {Roo.bootstrap.menu.Menu} this
28608          */
28609         beforeshow : true,
28610         /**
28611          * @event beforehide
28612          * Fires before this menu is hidden
28613          * @param {Roo.bootstrap.menu.Menu} this
28614          */
28615         beforehide : true,
28616         /**
28617          * @event show
28618          * Fires after this menu is displayed
28619          * @param {Roo.bootstrap.menu.Menu} this
28620          */
28621         show : true,
28622         /**
28623          * @event hide
28624          * Fires after this menu is hidden
28625          * @param {Roo.bootstrap.menu.Menu} this
28626          */
28627         hide : true,
28628         /**
28629          * @event click
28630          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28631          * @param {Roo.bootstrap.menu.Menu} this
28632          * @param {Roo.EventObject} e
28633          */
28634         click : true
28635     });
28636     
28637 };
28638
28639 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28640     
28641     submenu : false,
28642     html : '',
28643     weight : 'default',
28644     icon : false,
28645     pos : 'bottom',
28646     
28647     
28648     getChildContainer : function() {
28649         if(this.isSubMenu){
28650             return this.el;
28651         }
28652         
28653         return this.el.select('ul.dropdown-menu', true).first();  
28654     },
28655     
28656     getAutoCreate : function()
28657     {
28658         var text = [
28659             {
28660                 tag : 'span',
28661                 cls : 'roo-menu-text',
28662                 html : this.html
28663             }
28664         ];
28665         
28666         if(this.icon){
28667             text.unshift({
28668                 tag : 'i',
28669                 cls : 'fa ' + this.icon
28670             })
28671         }
28672         
28673         
28674         var cfg = {
28675             tag : 'div',
28676             cls : 'btn-group',
28677             cn : [
28678                 {
28679                     tag : 'button',
28680                     cls : 'dropdown-button btn btn-' + this.weight,
28681                     cn : text
28682                 },
28683                 {
28684                     tag : 'button',
28685                     cls : 'dropdown-toggle btn btn-' + this.weight,
28686                     cn : [
28687                         {
28688                             tag : 'span',
28689                             cls : 'caret'
28690                         }
28691                     ]
28692                 },
28693                 {
28694                     tag : 'ul',
28695                     cls : 'dropdown-menu'
28696                 }
28697             ]
28698             
28699         };
28700         
28701         if(this.pos == 'top'){
28702             cfg.cls += ' dropup';
28703         }
28704         
28705         if(this.isSubMenu){
28706             cfg = {
28707                 tag : 'ul',
28708                 cls : 'dropdown-menu'
28709             }
28710         }
28711         
28712         return cfg;
28713     },
28714     
28715     onRender : function(ct, position)
28716     {
28717         this.isSubMenu = ct.hasClass('dropdown-submenu');
28718         
28719         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28720     },
28721     
28722     initEvents : function() 
28723     {
28724         if(this.isSubMenu){
28725             return;
28726         }
28727         
28728         this.hidden = true;
28729         
28730         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28731         this.triggerEl.on('click', this.onTriggerPress, this);
28732         
28733         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28734         this.buttonEl.on('click', this.onClick, this);
28735         
28736     },
28737     
28738     list : function()
28739     {
28740         if(this.isSubMenu){
28741             return this.el;
28742         }
28743         
28744         return this.el.select('ul.dropdown-menu', true).first();
28745     },
28746     
28747     onClick : function(e)
28748     {
28749         this.fireEvent("click", this, e);
28750     },
28751     
28752     onTriggerPress  : function(e)
28753     {   
28754         if (this.isVisible()) {
28755             this.hide();
28756         } else {
28757             this.show();
28758         }
28759     },
28760     
28761     isVisible : function(){
28762         return !this.hidden;
28763     },
28764     
28765     show : function()
28766     {
28767         this.fireEvent("beforeshow", this);
28768         
28769         this.hidden = false;
28770         this.el.addClass('open');
28771         
28772         Roo.get(document).on("mouseup", this.onMouseUp, this);
28773         
28774         this.fireEvent("show", this);
28775         
28776         
28777     },
28778     
28779     hide : function()
28780     {
28781         this.fireEvent("beforehide", this);
28782         
28783         this.hidden = true;
28784         this.el.removeClass('open');
28785         
28786         Roo.get(document).un("mouseup", this.onMouseUp);
28787         
28788         this.fireEvent("hide", this);
28789     },
28790     
28791     onMouseUp : function()
28792     {
28793         this.hide();
28794     }
28795     
28796 });
28797
28798  
28799  /*
28800  * - LGPL
28801  *
28802  * menu item
28803  * 
28804  */
28805 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28806
28807 /**
28808  * @class Roo.bootstrap.menu.Item
28809  * @extends Roo.bootstrap.Component
28810  * Bootstrap MenuItem class
28811  * @cfg {Boolean} submenu (true | false) default false
28812  * @cfg {String} html text of the item
28813  * @cfg {String} href the link
28814  * @cfg {Boolean} disable (true | false) default false
28815  * @cfg {Boolean} preventDefault (true | false) default true
28816  * @cfg {String} icon Font awesome icon
28817  * @cfg {String} pos Submenu align to (left | right) default right 
28818  * 
28819  * 
28820  * @constructor
28821  * Create a new Item
28822  * @param {Object} config The config object
28823  */
28824
28825
28826 Roo.bootstrap.menu.Item = function(config){
28827     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28828     this.addEvents({
28829         /**
28830          * @event mouseover
28831          * Fires when the mouse is hovering over this menu
28832          * @param {Roo.bootstrap.menu.Item} this
28833          * @param {Roo.EventObject} e
28834          */
28835         mouseover : true,
28836         /**
28837          * @event mouseout
28838          * Fires when the mouse exits this menu
28839          * @param {Roo.bootstrap.menu.Item} this
28840          * @param {Roo.EventObject} e
28841          */
28842         mouseout : true,
28843         // raw events
28844         /**
28845          * @event click
28846          * The raw click event for the entire grid.
28847          * @param {Roo.EventObject} e
28848          */
28849         click : true
28850     });
28851 };
28852
28853 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28854     
28855     submenu : false,
28856     href : '',
28857     html : '',
28858     preventDefault: true,
28859     disable : false,
28860     icon : false,
28861     pos : 'right',
28862     
28863     getAutoCreate : function()
28864     {
28865         var text = [
28866             {
28867                 tag : 'span',
28868                 cls : 'roo-menu-item-text',
28869                 html : this.html
28870             }
28871         ];
28872         
28873         if(this.icon){
28874             text.unshift({
28875                 tag : 'i',
28876                 cls : 'fa ' + this.icon
28877             })
28878         }
28879         
28880         var cfg = {
28881             tag : 'li',
28882             cn : [
28883                 {
28884                     tag : 'a',
28885                     href : this.href || '#',
28886                     cn : text
28887                 }
28888             ]
28889         };
28890         
28891         if(this.disable){
28892             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28893         }
28894         
28895         if(this.submenu){
28896             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28897             
28898             if(this.pos == 'left'){
28899                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28900             }
28901         }
28902         
28903         return cfg;
28904     },
28905     
28906     initEvents : function() 
28907     {
28908         this.el.on('mouseover', this.onMouseOver, this);
28909         this.el.on('mouseout', this.onMouseOut, this);
28910         
28911         this.el.select('a', true).first().on('click', this.onClick, this);
28912         
28913     },
28914     
28915     onClick : function(e)
28916     {
28917         if(this.preventDefault){
28918             e.preventDefault();
28919         }
28920         
28921         this.fireEvent("click", this, e);
28922     },
28923     
28924     onMouseOver : function(e)
28925     {
28926         if(this.submenu && this.pos == 'left'){
28927             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28928         }
28929         
28930         this.fireEvent("mouseover", this, e);
28931     },
28932     
28933     onMouseOut : function(e)
28934     {
28935         this.fireEvent("mouseout", this, e);
28936     }
28937 });
28938
28939  
28940
28941  /*
28942  * - LGPL
28943  *
28944  * menu separator
28945  * 
28946  */
28947 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28948
28949 /**
28950  * @class Roo.bootstrap.menu.Separator
28951  * @extends Roo.bootstrap.Component
28952  * Bootstrap Separator class
28953  * 
28954  * @constructor
28955  * Create a new Separator
28956  * @param {Object} config The config object
28957  */
28958
28959
28960 Roo.bootstrap.menu.Separator = function(config){
28961     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28962 };
28963
28964 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28965     
28966     getAutoCreate : function(){
28967         var cfg = {
28968             tag : 'li',
28969             cls: 'dropdown-divider divider'
28970         };
28971         
28972         return cfg;
28973     }
28974    
28975 });
28976
28977  
28978
28979  /*
28980  * - LGPL
28981  *
28982  * Tooltip
28983  * 
28984  */
28985
28986 /**
28987  * @class Roo.bootstrap.Tooltip
28988  * Bootstrap Tooltip class
28989  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28990  * to determine which dom element triggers the tooltip.
28991  * 
28992  * It needs to add support for additional attributes like tooltip-position
28993  * 
28994  * @constructor
28995  * Create a new Toolti
28996  * @param {Object} config The config object
28997  */
28998
28999 Roo.bootstrap.Tooltip = function(config){
29000     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29001     
29002     this.alignment = Roo.bootstrap.Tooltip.alignment;
29003     
29004     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29005         this.alignment = config.alignment;
29006     }
29007     
29008 };
29009
29010 Roo.apply(Roo.bootstrap.Tooltip, {
29011     /**
29012      * @function init initialize tooltip monitoring.
29013      * @static
29014      */
29015     currentEl : false,
29016     currentTip : false,
29017     currentRegion : false,
29018     
29019     //  init : delay?
29020     
29021     init : function()
29022     {
29023         Roo.get(document).on('mouseover', this.enter ,this);
29024         Roo.get(document).on('mouseout', this.leave, this);
29025          
29026         
29027         this.currentTip = new Roo.bootstrap.Tooltip();
29028     },
29029     
29030     enter : function(ev)
29031     {
29032         var dom = ev.getTarget();
29033         
29034         //Roo.log(['enter',dom]);
29035         var el = Roo.fly(dom);
29036         if (this.currentEl) {
29037             //Roo.log(dom);
29038             //Roo.log(this.currentEl);
29039             //Roo.log(this.currentEl.contains(dom));
29040             if (this.currentEl == el) {
29041                 return;
29042             }
29043             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29044                 return;
29045             }
29046
29047         }
29048         
29049         if (this.currentTip.el) {
29050             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29051         }    
29052         //Roo.log(ev);
29053         
29054         if(!el || el.dom == document){
29055             return;
29056         }
29057         
29058         var bindEl = el;
29059         
29060         // you can not look for children, as if el is the body.. then everythign is the child..
29061         if (!el.attr('tooltip')) { //
29062             if (!el.select("[tooltip]").elements.length) {
29063                 return;
29064             }
29065             // is the mouse over this child...?
29066             bindEl = el.select("[tooltip]").first();
29067             var xy = ev.getXY();
29068             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29069                 //Roo.log("not in region.");
29070                 return;
29071             }
29072             //Roo.log("child element over..");
29073             
29074         }
29075         this.currentEl = bindEl;
29076         this.currentTip.bind(bindEl);
29077         this.currentRegion = Roo.lib.Region.getRegion(dom);
29078         this.currentTip.enter();
29079         
29080     },
29081     leave : function(ev)
29082     {
29083         var dom = ev.getTarget();
29084         //Roo.log(['leave',dom]);
29085         if (!this.currentEl) {
29086             return;
29087         }
29088         
29089         
29090         if (dom != this.currentEl.dom) {
29091             return;
29092         }
29093         var xy = ev.getXY();
29094         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29095             return;
29096         }
29097         // only activate leave if mouse cursor is outside... bounding box..
29098         
29099         
29100         
29101         
29102         if (this.currentTip) {
29103             this.currentTip.leave();
29104         }
29105         //Roo.log('clear currentEl');
29106         this.currentEl = false;
29107         
29108         
29109     },
29110     alignment : {
29111         'left' : ['r-l', [-2,0], 'right'],
29112         'right' : ['l-r', [2,0], 'left'],
29113         'bottom' : ['t-b', [0,2], 'top'],
29114         'top' : [ 'b-t', [0,-2], 'bottom']
29115     }
29116     
29117 });
29118
29119
29120 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29121     
29122     
29123     bindEl : false,
29124     
29125     delay : null, // can be { show : 300 , hide: 500}
29126     
29127     timeout : null,
29128     
29129     hoverState : null, //???
29130     
29131     placement : 'bottom', 
29132     
29133     alignment : false,
29134     
29135     getAutoCreate : function(){
29136     
29137         var cfg = {
29138            cls : 'tooltip',   
29139            role : 'tooltip',
29140            cn : [
29141                 {
29142                     cls : 'tooltip-arrow arrow'
29143                 },
29144                 {
29145                     cls : 'tooltip-inner'
29146                 }
29147            ]
29148         };
29149         
29150         return cfg;
29151     },
29152     bind : function(el)
29153     {
29154         this.bindEl = el;
29155     },
29156     
29157     initEvents : function()
29158     {
29159         this.arrowEl = this.el.select('.arrow', true).first();
29160         this.innerEl = this.el.select('.tooltip-inner', true).first();
29161     },
29162     
29163     enter : function () {
29164        
29165         if (this.timeout != null) {
29166             clearTimeout(this.timeout);
29167         }
29168         
29169         this.hoverState = 'in';
29170          //Roo.log("enter - show");
29171         if (!this.delay || !this.delay.show) {
29172             this.show();
29173             return;
29174         }
29175         var _t = this;
29176         this.timeout = setTimeout(function () {
29177             if (_t.hoverState == 'in') {
29178                 _t.show();
29179             }
29180         }, this.delay.show);
29181     },
29182     leave : function()
29183     {
29184         clearTimeout(this.timeout);
29185     
29186         this.hoverState = 'out';
29187          if (!this.delay || !this.delay.hide) {
29188             this.hide();
29189             return;
29190         }
29191        
29192         var _t = this;
29193         this.timeout = setTimeout(function () {
29194             //Roo.log("leave - timeout");
29195             
29196             if (_t.hoverState == 'out') {
29197                 _t.hide();
29198                 Roo.bootstrap.Tooltip.currentEl = false;
29199             }
29200         }, delay);
29201     },
29202     
29203     show : function (msg)
29204     {
29205         if (!this.el) {
29206             this.render(document.body);
29207         }
29208         // set content.
29209         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29210         
29211         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29212         
29213         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29214         
29215         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29216                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29217         
29218         var placement = typeof this.placement == 'function' ?
29219             this.placement.call(this, this.el, on_el) :
29220             this.placement;
29221             
29222         var autoToken = /\s?auto?\s?/i;
29223         var autoPlace = autoToken.test(placement);
29224         if (autoPlace) {
29225             placement = placement.replace(autoToken, '') || 'top';
29226         }
29227         
29228         //this.el.detach()
29229         //this.el.setXY([0,0]);
29230         this.el.show();
29231         //this.el.dom.style.display='block';
29232         
29233         //this.el.appendTo(on_el);
29234         
29235         var p = this.getPosition();
29236         var box = this.el.getBox();
29237         
29238         if (autoPlace) {
29239             // fixme..
29240         }
29241         
29242         var align = this.alignment[placement];
29243         
29244         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29245         
29246         if(placement == 'top' || placement == 'bottom'){
29247             if(xy[0] < 0){
29248                 placement = 'right';
29249             }
29250             
29251             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29252                 placement = 'left';
29253             }
29254             
29255             var scroll = Roo.select('body', true).first().getScroll();
29256             
29257             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29258                 placement = 'top';
29259             }
29260             
29261             align = this.alignment[placement];
29262             
29263             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29264             
29265         }
29266         
29267         this.el.alignTo(this.bindEl, align[0],align[1]);
29268         //var arrow = this.el.select('.arrow',true).first();
29269         //arrow.set(align[2], 
29270         
29271         this.el.addClass(placement);
29272         this.el.addClass("bs-tooltip-"+ placement);
29273         
29274         this.el.addClass('in fade show');
29275         
29276         this.hoverState = null;
29277         
29278         if (this.el.hasClass('fade')) {
29279             // fade it?
29280         }
29281         
29282         
29283         
29284         
29285         
29286     },
29287     hide : function()
29288     {
29289          
29290         if (!this.el) {
29291             return;
29292         }
29293         //this.el.setXY([0,0]);
29294         this.el.removeClass(['show', 'in']);
29295         //this.el.hide();
29296         
29297     }
29298     
29299 });
29300  
29301
29302  /*
29303  * - LGPL
29304  *
29305  * Location Picker
29306  * 
29307  */
29308
29309 /**
29310  * @class Roo.bootstrap.LocationPicker
29311  * @extends Roo.bootstrap.Component
29312  * Bootstrap LocationPicker class
29313  * @cfg {Number} latitude Position when init default 0
29314  * @cfg {Number} longitude Position when init default 0
29315  * @cfg {Number} zoom default 15
29316  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29317  * @cfg {Boolean} mapTypeControl default false
29318  * @cfg {Boolean} disableDoubleClickZoom default false
29319  * @cfg {Boolean} scrollwheel default true
29320  * @cfg {Boolean} streetViewControl default false
29321  * @cfg {Number} radius default 0
29322  * @cfg {String} locationName
29323  * @cfg {Boolean} draggable default true
29324  * @cfg {Boolean} enableAutocomplete default false
29325  * @cfg {Boolean} enableReverseGeocode default true
29326  * @cfg {String} markerTitle
29327  * 
29328  * @constructor
29329  * Create a new LocationPicker
29330  * @param {Object} config The config object
29331  */
29332
29333
29334 Roo.bootstrap.LocationPicker = function(config){
29335     
29336     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29337     
29338     this.addEvents({
29339         /**
29340          * @event initial
29341          * Fires when the picker initialized.
29342          * @param {Roo.bootstrap.LocationPicker} this
29343          * @param {Google Location} location
29344          */
29345         initial : true,
29346         /**
29347          * @event positionchanged
29348          * Fires when the picker position changed.
29349          * @param {Roo.bootstrap.LocationPicker} this
29350          * @param {Google Location} location
29351          */
29352         positionchanged : true,
29353         /**
29354          * @event resize
29355          * Fires when the map resize.
29356          * @param {Roo.bootstrap.LocationPicker} this
29357          */
29358         resize : true,
29359         /**
29360          * @event show
29361          * Fires when the map show.
29362          * @param {Roo.bootstrap.LocationPicker} this
29363          */
29364         show : true,
29365         /**
29366          * @event hide
29367          * Fires when the map hide.
29368          * @param {Roo.bootstrap.LocationPicker} this
29369          */
29370         hide : true,
29371         /**
29372          * @event mapClick
29373          * Fires when click the map.
29374          * @param {Roo.bootstrap.LocationPicker} this
29375          * @param {Map event} e
29376          */
29377         mapClick : true,
29378         /**
29379          * @event mapRightClick
29380          * Fires when right click the map.
29381          * @param {Roo.bootstrap.LocationPicker} this
29382          * @param {Map event} e
29383          */
29384         mapRightClick : true,
29385         /**
29386          * @event markerClick
29387          * Fires when click the marker.
29388          * @param {Roo.bootstrap.LocationPicker} this
29389          * @param {Map event} e
29390          */
29391         markerClick : true,
29392         /**
29393          * @event markerRightClick
29394          * Fires when right click the marker.
29395          * @param {Roo.bootstrap.LocationPicker} this
29396          * @param {Map event} e
29397          */
29398         markerRightClick : true,
29399         /**
29400          * @event OverlayViewDraw
29401          * Fires when OverlayView Draw
29402          * @param {Roo.bootstrap.LocationPicker} this
29403          */
29404         OverlayViewDraw : true,
29405         /**
29406          * @event OverlayViewOnAdd
29407          * Fires when OverlayView Draw
29408          * @param {Roo.bootstrap.LocationPicker} this
29409          */
29410         OverlayViewOnAdd : true,
29411         /**
29412          * @event OverlayViewOnRemove
29413          * Fires when OverlayView Draw
29414          * @param {Roo.bootstrap.LocationPicker} this
29415          */
29416         OverlayViewOnRemove : true,
29417         /**
29418          * @event OverlayViewShow
29419          * Fires when OverlayView Draw
29420          * @param {Roo.bootstrap.LocationPicker} this
29421          * @param {Pixel} cpx
29422          */
29423         OverlayViewShow : true,
29424         /**
29425          * @event OverlayViewHide
29426          * Fires when OverlayView Draw
29427          * @param {Roo.bootstrap.LocationPicker} this
29428          */
29429         OverlayViewHide : true,
29430         /**
29431          * @event loadexception
29432          * Fires when load google lib failed.
29433          * @param {Roo.bootstrap.LocationPicker} this
29434          */
29435         loadexception : true
29436     });
29437         
29438 };
29439
29440 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29441     
29442     gMapContext: false,
29443     
29444     latitude: 0,
29445     longitude: 0,
29446     zoom: 15,
29447     mapTypeId: false,
29448     mapTypeControl: false,
29449     disableDoubleClickZoom: false,
29450     scrollwheel: true,
29451     streetViewControl: false,
29452     radius: 0,
29453     locationName: '',
29454     draggable: true,
29455     enableAutocomplete: false,
29456     enableReverseGeocode: true,
29457     markerTitle: '',
29458     
29459     getAutoCreate: function()
29460     {
29461
29462         var cfg = {
29463             tag: 'div',
29464             cls: 'roo-location-picker'
29465         };
29466         
29467         return cfg
29468     },
29469     
29470     initEvents: function(ct, position)
29471     {       
29472         if(!this.el.getWidth() || this.isApplied()){
29473             return;
29474         }
29475         
29476         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29477         
29478         this.initial();
29479     },
29480     
29481     initial: function()
29482     {
29483         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29484             this.fireEvent('loadexception', this);
29485             return;
29486         }
29487         
29488         if(!this.mapTypeId){
29489             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29490         }
29491         
29492         this.gMapContext = this.GMapContext();
29493         
29494         this.initOverlayView();
29495         
29496         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29497         
29498         var _this = this;
29499                 
29500         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29501             _this.setPosition(_this.gMapContext.marker.position);
29502         });
29503         
29504         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29505             _this.fireEvent('mapClick', this, event);
29506             
29507         });
29508
29509         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29510             _this.fireEvent('mapRightClick', this, event);
29511             
29512         });
29513         
29514         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29515             _this.fireEvent('markerClick', this, event);
29516             
29517         });
29518
29519         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29520             _this.fireEvent('markerRightClick', this, event);
29521             
29522         });
29523         
29524         this.setPosition(this.gMapContext.location);
29525         
29526         this.fireEvent('initial', this, this.gMapContext.location);
29527     },
29528     
29529     initOverlayView: function()
29530     {
29531         var _this = this;
29532         
29533         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29534             
29535             draw: function()
29536             {
29537                 _this.fireEvent('OverlayViewDraw', _this);
29538             },
29539             
29540             onAdd: function()
29541             {
29542                 _this.fireEvent('OverlayViewOnAdd', _this);
29543             },
29544             
29545             onRemove: function()
29546             {
29547                 _this.fireEvent('OverlayViewOnRemove', _this);
29548             },
29549             
29550             show: function(cpx)
29551             {
29552                 _this.fireEvent('OverlayViewShow', _this, cpx);
29553             },
29554             
29555             hide: function()
29556             {
29557                 _this.fireEvent('OverlayViewHide', _this);
29558             }
29559             
29560         });
29561     },
29562     
29563     fromLatLngToContainerPixel: function(event)
29564     {
29565         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29566     },
29567     
29568     isApplied: function() 
29569     {
29570         return this.getGmapContext() == false ? false : true;
29571     },
29572     
29573     getGmapContext: function() 
29574     {
29575         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29576     },
29577     
29578     GMapContext: function() 
29579     {
29580         var position = new google.maps.LatLng(this.latitude, this.longitude);
29581         
29582         var _map = new google.maps.Map(this.el.dom, {
29583             center: position,
29584             zoom: this.zoom,
29585             mapTypeId: this.mapTypeId,
29586             mapTypeControl: this.mapTypeControl,
29587             disableDoubleClickZoom: this.disableDoubleClickZoom,
29588             scrollwheel: this.scrollwheel,
29589             streetViewControl: this.streetViewControl,
29590             locationName: this.locationName,
29591             draggable: this.draggable,
29592             enableAutocomplete: this.enableAutocomplete,
29593             enableReverseGeocode: this.enableReverseGeocode
29594         });
29595         
29596         var _marker = new google.maps.Marker({
29597             position: position,
29598             map: _map,
29599             title: this.markerTitle,
29600             draggable: this.draggable
29601         });
29602         
29603         return {
29604             map: _map,
29605             marker: _marker,
29606             circle: null,
29607             location: position,
29608             radius: this.radius,
29609             locationName: this.locationName,
29610             addressComponents: {
29611                 formatted_address: null,
29612                 addressLine1: null,
29613                 addressLine2: null,
29614                 streetName: null,
29615                 streetNumber: null,
29616                 city: null,
29617                 district: null,
29618                 state: null,
29619                 stateOrProvince: null
29620             },
29621             settings: this,
29622             domContainer: this.el.dom,
29623             geodecoder: new google.maps.Geocoder()
29624         };
29625     },
29626     
29627     drawCircle: function(center, radius, options) 
29628     {
29629         if (this.gMapContext.circle != null) {
29630             this.gMapContext.circle.setMap(null);
29631         }
29632         if (radius > 0) {
29633             radius *= 1;
29634             options = Roo.apply({}, options, {
29635                 strokeColor: "#0000FF",
29636                 strokeOpacity: .35,
29637                 strokeWeight: 2,
29638                 fillColor: "#0000FF",
29639                 fillOpacity: .2
29640             });
29641             
29642             options.map = this.gMapContext.map;
29643             options.radius = radius;
29644             options.center = center;
29645             this.gMapContext.circle = new google.maps.Circle(options);
29646             return this.gMapContext.circle;
29647         }
29648         
29649         return null;
29650     },
29651     
29652     setPosition: function(location) 
29653     {
29654         this.gMapContext.location = location;
29655         this.gMapContext.marker.setPosition(location);
29656         this.gMapContext.map.panTo(location);
29657         this.drawCircle(location, this.gMapContext.radius, {});
29658         
29659         var _this = this;
29660         
29661         if (this.gMapContext.settings.enableReverseGeocode) {
29662             this.gMapContext.geodecoder.geocode({
29663                 latLng: this.gMapContext.location
29664             }, function(results, status) {
29665                 
29666                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29667                     _this.gMapContext.locationName = results[0].formatted_address;
29668                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29669                     
29670                     _this.fireEvent('positionchanged', this, location);
29671                 }
29672             });
29673             
29674             return;
29675         }
29676         
29677         this.fireEvent('positionchanged', this, location);
29678     },
29679     
29680     resize: function()
29681     {
29682         google.maps.event.trigger(this.gMapContext.map, "resize");
29683         
29684         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29685         
29686         this.fireEvent('resize', this);
29687     },
29688     
29689     setPositionByLatLng: function(latitude, longitude)
29690     {
29691         this.setPosition(new google.maps.LatLng(latitude, longitude));
29692     },
29693     
29694     getCurrentPosition: function() 
29695     {
29696         return {
29697             latitude: this.gMapContext.location.lat(),
29698             longitude: this.gMapContext.location.lng()
29699         };
29700     },
29701     
29702     getAddressName: function() 
29703     {
29704         return this.gMapContext.locationName;
29705     },
29706     
29707     getAddressComponents: function() 
29708     {
29709         return this.gMapContext.addressComponents;
29710     },
29711     
29712     address_component_from_google_geocode: function(address_components) 
29713     {
29714         var result = {};
29715         
29716         for (var i = 0; i < address_components.length; i++) {
29717             var component = address_components[i];
29718             if (component.types.indexOf("postal_code") >= 0) {
29719                 result.postalCode = component.short_name;
29720             } else if (component.types.indexOf("street_number") >= 0) {
29721                 result.streetNumber = component.short_name;
29722             } else if (component.types.indexOf("route") >= 0) {
29723                 result.streetName = component.short_name;
29724             } else if (component.types.indexOf("neighborhood") >= 0) {
29725                 result.city = component.short_name;
29726             } else if (component.types.indexOf("locality") >= 0) {
29727                 result.city = component.short_name;
29728             } else if (component.types.indexOf("sublocality") >= 0) {
29729                 result.district = component.short_name;
29730             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29731                 result.stateOrProvince = component.short_name;
29732             } else if (component.types.indexOf("country") >= 0) {
29733                 result.country = component.short_name;
29734             }
29735         }
29736         
29737         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29738         result.addressLine2 = "";
29739         return result;
29740     },
29741     
29742     setZoomLevel: function(zoom)
29743     {
29744         this.gMapContext.map.setZoom(zoom);
29745     },
29746     
29747     show: function()
29748     {
29749         if(!this.el){
29750             return;
29751         }
29752         
29753         this.el.show();
29754         
29755         this.resize();
29756         
29757         this.fireEvent('show', this);
29758     },
29759     
29760     hide: function()
29761     {
29762         if(!this.el){
29763             return;
29764         }
29765         
29766         this.el.hide();
29767         
29768         this.fireEvent('hide', this);
29769     }
29770     
29771 });
29772
29773 Roo.apply(Roo.bootstrap.LocationPicker, {
29774     
29775     OverlayView : function(map, options)
29776     {
29777         options = options || {};
29778         
29779         this.setMap(map);
29780     }
29781     
29782     
29783 });/**
29784  * @class Roo.bootstrap.Alert
29785  * @extends Roo.bootstrap.Component
29786  * Bootstrap Alert class - shows an alert area box
29787  * eg
29788  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29789   Enter a valid email address
29790 </div>
29791  * @licence LGPL
29792  * @cfg {String} title The title of alert
29793  * @cfg {String} html The content of alert
29794  * @cfg {String} weight (  success | info | warning | danger )
29795  * @cfg {String} faicon font-awesomeicon
29796  * 
29797  * @constructor
29798  * Create a new alert
29799  * @param {Object} config The config object
29800  */
29801
29802
29803 Roo.bootstrap.Alert = function(config){
29804     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29805     
29806 };
29807
29808 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29809     
29810     title: '',
29811     html: '',
29812     weight: false,
29813     faicon: false,
29814     
29815     getAutoCreate : function()
29816     {
29817         
29818         var cfg = {
29819             tag : 'div',
29820             cls : 'alert',
29821             cn : [
29822                 {
29823                     tag : 'i',
29824                     cls : 'roo-alert-icon'
29825                     
29826                 },
29827                 {
29828                     tag : 'b',
29829                     cls : 'roo-alert-title',
29830                     html : this.title
29831                 },
29832                 {
29833                     tag : 'span',
29834                     cls : 'roo-alert-text',
29835                     html : this.html
29836                 }
29837             ]
29838         };
29839         
29840         if(this.faicon){
29841             cfg.cn[0].cls += ' fa ' + this.faicon;
29842         }
29843         
29844         if(this.weight){
29845             cfg.cls += ' alert-' + this.weight;
29846         }
29847         
29848         return cfg;
29849     },
29850     
29851     initEvents: function() 
29852     {
29853         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29854     },
29855     
29856     setTitle : function(str)
29857     {
29858         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29859     },
29860     
29861     setText : function(str)
29862     {
29863         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29864     },
29865     
29866     setWeight : function(weight)
29867     {
29868         if(this.weight){
29869             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29870         }
29871         
29872         this.weight = weight;
29873         
29874         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29875     },
29876     
29877     setIcon : function(icon)
29878     {
29879         if(this.faicon){
29880             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29881         }
29882         
29883         this.faicon = icon;
29884         
29885         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29886     },
29887     
29888     hide: function() 
29889     {
29890         this.el.hide();   
29891     },
29892     
29893     show: function() 
29894     {  
29895         this.el.show();   
29896     }
29897     
29898 });
29899
29900  
29901 /*
29902 * Licence: LGPL
29903 */
29904
29905 /**
29906  * @class Roo.bootstrap.UploadCropbox
29907  * @extends Roo.bootstrap.Component
29908  * Bootstrap UploadCropbox class
29909  * @cfg {String} emptyText show when image has been loaded
29910  * @cfg {String} rotateNotify show when image too small to rotate
29911  * @cfg {Number} errorTimeout default 3000
29912  * @cfg {Number} minWidth default 300
29913  * @cfg {Number} minHeight default 300
29914  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29915  * @cfg {Boolean} isDocument (true|false) default false
29916  * @cfg {String} url action url
29917  * @cfg {String} paramName default 'imageUpload'
29918  * @cfg {String} method default POST
29919  * @cfg {Boolean} loadMask (true|false) default true
29920  * @cfg {Boolean} loadingText default 'Loading...'
29921  * 
29922  * @constructor
29923  * Create a new UploadCropbox
29924  * @param {Object} config The config object
29925  */
29926
29927 Roo.bootstrap.UploadCropbox = function(config){
29928     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29929     
29930     this.addEvents({
29931         /**
29932          * @event beforeselectfile
29933          * Fire before select file
29934          * @param {Roo.bootstrap.UploadCropbox} this
29935          */
29936         "beforeselectfile" : true,
29937         /**
29938          * @event initial
29939          * Fire after initEvent
29940          * @param {Roo.bootstrap.UploadCropbox} this
29941          */
29942         "initial" : true,
29943         /**
29944          * @event crop
29945          * Fire after initEvent
29946          * @param {Roo.bootstrap.UploadCropbox} this
29947          * @param {String} data
29948          */
29949         "crop" : true,
29950         /**
29951          * @event prepare
29952          * Fire when preparing the file data
29953          * @param {Roo.bootstrap.UploadCropbox} this
29954          * @param {Object} file
29955          */
29956         "prepare" : true,
29957         /**
29958          * @event exception
29959          * Fire when get exception
29960          * @param {Roo.bootstrap.UploadCropbox} this
29961          * @param {XMLHttpRequest} xhr
29962          */
29963         "exception" : true,
29964         /**
29965          * @event beforeloadcanvas
29966          * Fire before load the canvas
29967          * @param {Roo.bootstrap.UploadCropbox} this
29968          * @param {String} src
29969          */
29970         "beforeloadcanvas" : true,
29971         /**
29972          * @event trash
29973          * Fire when trash image
29974          * @param {Roo.bootstrap.UploadCropbox} this
29975          */
29976         "trash" : true,
29977         /**
29978          * @event download
29979          * Fire when download the image
29980          * @param {Roo.bootstrap.UploadCropbox} this
29981          */
29982         "download" : true,
29983         /**
29984          * @event footerbuttonclick
29985          * Fire when footerbuttonclick
29986          * @param {Roo.bootstrap.UploadCropbox} this
29987          * @param {String} type
29988          */
29989         "footerbuttonclick" : true,
29990         /**
29991          * @event resize
29992          * Fire when resize
29993          * @param {Roo.bootstrap.UploadCropbox} this
29994          */
29995         "resize" : true,
29996         /**
29997          * @event rotate
29998          * Fire when rotate the image
29999          * @param {Roo.bootstrap.UploadCropbox} this
30000          * @param {String} pos
30001          */
30002         "rotate" : true,
30003         /**
30004          * @event inspect
30005          * Fire when inspect the file
30006          * @param {Roo.bootstrap.UploadCropbox} this
30007          * @param {Object} file
30008          */
30009         "inspect" : true,
30010         /**
30011          * @event upload
30012          * Fire when xhr upload the file
30013          * @param {Roo.bootstrap.UploadCropbox} this
30014          * @param {Object} data
30015          */
30016         "upload" : true,
30017         /**
30018          * @event arrange
30019          * Fire when arrange the file data
30020          * @param {Roo.bootstrap.UploadCropbox} this
30021          * @param {Object} formData
30022          */
30023         "arrange" : true
30024     });
30025     
30026     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30027 };
30028
30029 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30030     
30031     emptyText : 'Click to upload image',
30032     rotateNotify : 'Image is too small to rotate',
30033     errorTimeout : 3000,
30034     scale : 0,
30035     baseScale : 1,
30036     rotate : 0,
30037     dragable : false,
30038     pinching : false,
30039     mouseX : 0,
30040     mouseY : 0,
30041     cropData : false,
30042     minWidth : 300,
30043     minHeight : 300,
30044     file : false,
30045     exif : {},
30046     baseRotate : 1,
30047     cropType : 'image/jpeg',
30048     buttons : false,
30049     canvasLoaded : false,
30050     isDocument : false,
30051     method : 'POST',
30052     paramName : 'imageUpload',
30053     loadMask : true,
30054     loadingText : 'Loading...',
30055     maskEl : false,
30056     
30057     getAutoCreate : function()
30058     {
30059         var cfg = {
30060             tag : 'div',
30061             cls : 'roo-upload-cropbox',
30062             cn : [
30063                 {
30064                     tag : 'input',
30065                     cls : 'roo-upload-cropbox-selector',
30066                     type : 'file'
30067                 },
30068                 {
30069                     tag : 'div',
30070                     cls : 'roo-upload-cropbox-body',
30071                     style : 'cursor:pointer',
30072                     cn : [
30073                         {
30074                             tag : 'div',
30075                             cls : 'roo-upload-cropbox-preview'
30076                         },
30077                         {
30078                             tag : 'div',
30079                             cls : 'roo-upload-cropbox-thumb'
30080                         },
30081                         {
30082                             tag : 'div',
30083                             cls : 'roo-upload-cropbox-empty-notify',
30084                             html : this.emptyText
30085                         },
30086                         {
30087                             tag : 'div',
30088                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30089                             html : this.rotateNotify
30090                         }
30091                     ]
30092                 },
30093                 {
30094                     tag : 'div',
30095                     cls : 'roo-upload-cropbox-footer',
30096                     cn : {
30097                         tag : 'div',
30098                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30099                         cn : []
30100                     }
30101                 }
30102             ]
30103         };
30104         
30105         return cfg;
30106     },
30107     
30108     onRender : function(ct, position)
30109     {
30110         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30111         
30112         if (this.buttons.length) {
30113             
30114             Roo.each(this.buttons, function(bb) {
30115                 
30116                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30117                 
30118                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30119                 
30120             }, this);
30121         }
30122         
30123         if(this.loadMask){
30124             this.maskEl = this.el;
30125         }
30126     },
30127     
30128     initEvents : function()
30129     {
30130         this.urlAPI = (window.createObjectURL && window) || 
30131                                 (window.URL && URL.revokeObjectURL && URL) || 
30132                                 (window.webkitURL && webkitURL);
30133                         
30134         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30135         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30136         
30137         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30138         this.selectorEl.hide();
30139         
30140         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30141         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30142         
30143         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30144         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30145         this.thumbEl.hide();
30146         
30147         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30148         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30149         
30150         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30151         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30152         this.errorEl.hide();
30153         
30154         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30155         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30156         this.footerEl.hide();
30157         
30158         this.setThumbBoxSize();
30159         
30160         this.bind();
30161         
30162         this.resize();
30163         
30164         this.fireEvent('initial', this);
30165     },
30166
30167     bind : function()
30168     {
30169         var _this = this;
30170         
30171         window.addEventListener("resize", function() { _this.resize(); } );
30172         
30173         this.bodyEl.on('click', this.beforeSelectFile, this);
30174         
30175         if(Roo.isTouch){
30176             this.bodyEl.on('touchstart', this.onTouchStart, this);
30177             this.bodyEl.on('touchmove', this.onTouchMove, this);
30178             this.bodyEl.on('touchend', this.onTouchEnd, this);
30179         }
30180         
30181         if(!Roo.isTouch){
30182             this.bodyEl.on('mousedown', this.onMouseDown, this);
30183             this.bodyEl.on('mousemove', this.onMouseMove, this);
30184             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30185             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30186             Roo.get(document).on('mouseup', this.onMouseUp, this);
30187         }
30188         
30189         this.selectorEl.on('change', this.onFileSelected, this);
30190     },
30191     
30192     reset : function()
30193     {    
30194         this.scale = 0;
30195         this.baseScale = 1;
30196         this.rotate = 0;
30197         this.baseRotate = 1;
30198         this.dragable = false;
30199         this.pinching = false;
30200         this.mouseX = 0;
30201         this.mouseY = 0;
30202         this.cropData = false;
30203         this.notifyEl.dom.innerHTML = this.emptyText;
30204         
30205         this.selectorEl.dom.value = '';
30206         
30207     },
30208     
30209     resize : function()
30210     {
30211         if(this.fireEvent('resize', this) != false){
30212             this.setThumbBoxPosition();
30213             this.setCanvasPosition();
30214         }
30215     },
30216     
30217     onFooterButtonClick : function(e, el, o, type)
30218     {
30219         switch (type) {
30220             case 'rotate-left' :
30221                 this.onRotateLeft(e);
30222                 break;
30223             case 'rotate-right' :
30224                 this.onRotateRight(e);
30225                 break;
30226             case 'picture' :
30227                 this.beforeSelectFile(e);
30228                 break;
30229             case 'trash' :
30230                 this.trash(e);
30231                 break;
30232             case 'crop' :
30233                 this.crop(e);
30234                 break;
30235             case 'download' :
30236                 this.download(e);
30237                 break;
30238             default :
30239                 break;
30240         }
30241         
30242         this.fireEvent('footerbuttonclick', this, type);
30243     },
30244     
30245     beforeSelectFile : function(e)
30246     {
30247         e.preventDefault();
30248         
30249         if(this.fireEvent('beforeselectfile', this) != false){
30250             this.selectorEl.dom.click();
30251         }
30252     },
30253     
30254     onFileSelected : function(e)
30255     {
30256         e.preventDefault();
30257         
30258         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30259             return;
30260         }
30261         
30262         var file = this.selectorEl.dom.files[0];
30263         
30264         if(this.fireEvent('inspect', this, file) != false){
30265             this.prepare(file);
30266         }
30267         
30268     },
30269     
30270     trash : function(e)
30271     {
30272         this.fireEvent('trash', this);
30273     },
30274     
30275     download : function(e)
30276     {
30277         this.fireEvent('download', this);
30278     },
30279     
30280     loadCanvas : function(src)
30281     {   
30282         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30283             
30284             this.reset();
30285             
30286             this.imageEl = document.createElement('img');
30287             
30288             var _this = this;
30289             
30290             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30291             
30292             this.imageEl.src = src;
30293         }
30294     },
30295     
30296     onLoadCanvas : function()
30297     {   
30298         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30299         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30300         
30301         this.bodyEl.un('click', this.beforeSelectFile, this);
30302         
30303         this.notifyEl.hide();
30304         this.thumbEl.show();
30305         this.footerEl.show();
30306         
30307         this.baseRotateLevel();
30308         
30309         if(this.isDocument){
30310             this.setThumbBoxSize();
30311         }
30312         
30313         this.setThumbBoxPosition();
30314         
30315         this.baseScaleLevel();
30316         
30317         this.draw();
30318         
30319         this.resize();
30320         
30321         this.canvasLoaded = true;
30322         
30323         if(this.loadMask){
30324             this.maskEl.unmask();
30325         }
30326         
30327     },
30328     
30329     setCanvasPosition : function()
30330     {   
30331         if(!this.canvasEl){
30332             return;
30333         }
30334         
30335         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30336         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30337         
30338         this.previewEl.setLeft(pw);
30339         this.previewEl.setTop(ph);
30340         
30341     },
30342     
30343     onMouseDown : function(e)
30344     {   
30345         e.stopEvent();
30346         
30347         this.dragable = true;
30348         this.pinching = false;
30349         
30350         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30351             this.dragable = false;
30352             return;
30353         }
30354         
30355         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30356         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30357         
30358     },
30359     
30360     onMouseMove : function(e)
30361     {   
30362         e.stopEvent();
30363         
30364         if(!this.canvasLoaded){
30365             return;
30366         }
30367         
30368         if (!this.dragable){
30369             return;
30370         }
30371         
30372         var minX = Math.ceil(this.thumbEl.getLeft(true));
30373         var minY = Math.ceil(this.thumbEl.getTop(true));
30374         
30375         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30376         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30377         
30378         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30379         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30380         
30381         x = x - this.mouseX;
30382         y = y - this.mouseY;
30383         
30384         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30385         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30386         
30387         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30388         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30389         
30390         this.previewEl.setLeft(bgX);
30391         this.previewEl.setTop(bgY);
30392         
30393         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30394         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30395     },
30396     
30397     onMouseUp : function(e)
30398     {   
30399         e.stopEvent();
30400         
30401         this.dragable = false;
30402     },
30403     
30404     onMouseWheel : function(e)
30405     {   
30406         e.stopEvent();
30407         
30408         this.startScale = this.scale;
30409         
30410         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30411         
30412         if(!this.zoomable()){
30413             this.scale = this.startScale;
30414             return;
30415         }
30416         
30417         this.draw();
30418         
30419         return;
30420     },
30421     
30422     zoomable : function()
30423     {
30424         var minScale = this.thumbEl.getWidth() / this.minWidth;
30425         
30426         if(this.minWidth < this.minHeight){
30427             minScale = this.thumbEl.getHeight() / this.minHeight;
30428         }
30429         
30430         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30431         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30432         
30433         if(
30434                 this.isDocument &&
30435                 (this.rotate == 0 || this.rotate == 180) && 
30436                 (
30437                     width > this.imageEl.OriginWidth || 
30438                     height > this.imageEl.OriginHeight ||
30439                     (width < this.minWidth && height < this.minHeight)
30440                 )
30441         ){
30442             return false;
30443         }
30444         
30445         if(
30446                 this.isDocument &&
30447                 (this.rotate == 90 || this.rotate == 270) && 
30448                 (
30449                     width > this.imageEl.OriginWidth || 
30450                     height > this.imageEl.OriginHeight ||
30451                     (width < this.minHeight && height < this.minWidth)
30452                 )
30453         ){
30454             return false;
30455         }
30456         
30457         if(
30458                 !this.isDocument &&
30459                 (this.rotate == 0 || this.rotate == 180) && 
30460                 (
30461                     width < this.minWidth || 
30462                     width > this.imageEl.OriginWidth || 
30463                     height < this.minHeight || 
30464                     height > this.imageEl.OriginHeight
30465                 )
30466         ){
30467             return false;
30468         }
30469         
30470         if(
30471                 !this.isDocument &&
30472                 (this.rotate == 90 || this.rotate == 270) && 
30473                 (
30474                     width < this.minHeight || 
30475                     width > this.imageEl.OriginWidth || 
30476                     height < this.minWidth || 
30477                     height > this.imageEl.OriginHeight
30478                 )
30479         ){
30480             return false;
30481         }
30482         
30483         return true;
30484         
30485     },
30486     
30487     onRotateLeft : function(e)
30488     {   
30489         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30490             
30491             var minScale = this.thumbEl.getWidth() / this.minWidth;
30492             
30493             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30494             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30495             
30496             this.startScale = this.scale;
30497             
30498             while (this.getScaleLevel() < minScale){
30499             
30500                 this.scale = this.scale + 1;
30501                 
30502                 if(!this.zoomable()){
30503                     break;
30504                 }
30505                 
30506                 if(
30507                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30508                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30509                 ){
30510                     continue;
30511                 }
30512                 
30513                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30514
30515                 this.draw();
30516                 
30517                 return;
30518             }
30519             
30520             this.scale = this.startScale;
30521             
30522             this.onRotateFail();
30523             
30524             return false;
30525         }
30526         
30527         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30528
30529         if(this.isDocument){
30530             this.setThumbBoxSize();
30531             this.setThumbBoxPosition();
30532             this.setCanvasPosition();
30533         }
30534         
30535         this.draw();
30536         
30537         this.fireEvent('rotate', this, 'left');
30538         
30539     },
30540     
30541     onRotateRight : function(e)
30542     {
30543         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30544             
30545             var minScale = this.thumbEl.getWidth() / this.minWidth;
30546         
30547             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30548             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30549             
30550             this.startScale = this.scale;
30551             
30552             while (this.getScaleLevel() < minScale){
30553             
30554                 this.scale = this.scale + 1;
30555                 
30556                 if(!this.zoomable()){
30557                     break;
30558                 }
30559                 
30560                 if(
30561                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30562                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30563                 ){
30564                     continue;
30565                 }
30566                 
30567                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30568
30569                 this.draw();
30570                 
30571                 return;
30572             }
30573             
30574             this.scale = this.startScale;
30575             
30576             this.onRotateFail();
30577             
30578             return false;
30579         }
30580         
30581         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30582
30583         if(this.isDocument){
30584             this.setThumbBoxSize();
30585             this.setThumbBoxPosition();
30586             this.setCanvasPosition();
30587         }
30588         
30589         this.draw();
30590         
30591         this.fireEvent('rotate', this, 'right');
30592     },
30593     
30594     onRotateFail : function()
30595     {
30596         this.errorEl.show(true);
30597         
30598         var _this = this;
30599         
30600         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30601     },
30602     
30603     draw : function()
30604     {
30605         this.previewEl.dom.innerHTML = '';
30606         
30607         var canvasEl = document.createElement("canvas");
30608         
30609         var contextEl = canvasEl.getContext("2d");
30610         
30611         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30612         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30613         var center = this.imageEl.OriginWidth / 2;
30614         
30615         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30616             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30617             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30618             center = this.imageEl.OriginHeight / 2;
30619         }
30620         
30621         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30622         
30623         contextEl.translate(center, center);
30624         contextEl.rotate(this.rotate * Math.PI / 180);
30625
30626         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30627         
30628         this.canvasEl = document.createElement("canvas");
30629         
30630         this.contextEl = this.canvasEl.getContext("2d");
30631         
30632         switch (this.rotate) {
30633             case 0 :
30634                 
30635                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30636                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30637                 
30638                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30639                 
30640                 break;
30641             case 90 : 
30642                 
30643                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30644                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30645                 
30646                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30647                     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);
30648                     break;
30649                 }
30650                 
30651                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30652                 
30653                 break;
30654             case 180 :
30655                 
30656                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30657                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30658                 
30659                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30660                     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);
30661                     break;
30662                 }
30663                 
30664                 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);
30665                 
30666                 break;
30667             case 270 :
30668                 
30669                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30670                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30671         
30672                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30673                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30674                     break;
30675                 }
30676                 
30677                 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);
30678                 
30679                 break;
30680             default : 
30681                 break;
30682         }
30683         
30684         this.previewEl.appendChild(this.canvasEl);
30685         
30686         this.setCanvasPosition();
30687     },
30688     
30689     crop : function()
30690     {
30691         if(!this.canvasLoaded){
30692             return;
30693         }
30694         
30695         var imageCanvas = document.createElement("canvas");
30696         
30697         var imageContext = imageCanvas.getContext("2d");
30698         
30699         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30700         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30701         
30702         var center = imageCanvas.width / 2;
30703         
30704         imageContext.translate(center, center);
30705         
30706         imageContext.rotate(this.rotate * Math.PI / 180);
30707         
30708         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30709         
30710         var canvas = document.createElement("canvas");
30711         
30712         var context = canvas.getContext("2d");
30713                 
30714         canvas.width = this.minWidth;
30715         canvas.height = this.minHeight;
30716
30717         switch (this.rotate) {
30718             case 0 :
30719                 
30720                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30721                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30722                 
30723                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30724                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30725                 
30726                 var targetWidth = this.minWidth - 2 * x;
30727                 var targetHeight = this.minHeight - 2 * y;
30728                 
30729                 var scale = 1;
30730                 
30731                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30732                     scale = targetWidth / width;
30733                 }
30734                 
30735                 if(x > 0 && y == 0){
30736                     scale = targetHeight / height;
30737                 }
30738                 
30739                 if(x > 0 && y > 0){
30740                     scale = targetWidth / width;
30741                     
30742                     if(width < height){
30743                         scale = targetHeight / height;
30744                     }
30745                 }
30746                 
30747                 context.scale(scale, scale);
30748                 
30749                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30750                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30751
30752                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30753                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30754
30755                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30756                 
30757                 break;
30758             case 90 : 
30759                 
30760                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30761                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30762                 
30763                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30764                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30765                 
30766                 var targetWidth = this.minWidth - 2 * x;
30767                 var targetHeight = this.minHeight - 2 * y;
30768                 
30769                 var scale = 1;
30770                 
30771                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30772                     scale = targetWidth / width;
30773                 }
30774                 
30775                 if(x > 0 && y == 0){
30776                     scale = targetHeight / height;
30777                 }
30778                 
30779                 if(x > 0 && y > 0){
30780                     scale = targetWidth / width;
30781                     
30782                     if(width < height){
30783                         scale = targetHeight / height;
30784                     }
30785                 }
30786                 
30787                 context.scale(scale, scale);
30788                 
30789                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30790                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30791
30792                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30793                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30794                 
30795                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30796                 
30797                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30798                 
30799                 break;
30800             case 180 :
30801                 
30802                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30803                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30804                 
30805                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30806                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30807                 
30808                 var targetWidth = this.minWidth - 2 * x;
30809                 var targetHeight = this.minHeight - 2 * y;
30810                 
30811                 var scale = 1;
30812                 
30813                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30814                     scale = targetWidth / width;
30815                 }
30816                 
30817                 if(x > 0 && y == 0){
30818                     scale = targetHeight / height;
30819                 }
30820                 
30821                 if(x > 0 && y > 0){
30822                     scale = targetWidth / width;
30823                     
30824                     if(width < height){
30825                         scale = targetHeight / height;
30826                     }
30827                 }
30828                 
30829                 context.scale(scale, scale);
30830                 
30831                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30832                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30833
30834                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30835                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30836
30837                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30838                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30839                 
30840                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30841                 
30842                 break;
30843             case 270 :
30844                 
30845                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30846                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30847                 
30848                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30849                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30850                 
30851                 var targetWidth = this.minWidth - 2 * x;
30852                 var targetHeight = this.minHeight - 2 * y;
30853                 
30854                 var scale = 1;
30855                 
30856                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30857                     scale = targetWidth / width;
30858                 }
30859                 
30860                 if(x > 0 && y == 0){
30861                     scale = targetHeight / height;
30862                 }
30863                 
30864                 if(x > 0 && y > 0){
30865                     scale = targetWidth / width;
30866                     
30867                     if(width < height){
30868                         scale = targetHeight / height;
30869                     }
30870                 }
30871                 
30872                 context.scale(scale, scale);
30873                 
30874                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30875                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30876
30877                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30878                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30879                 
30880                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30881                 
30882                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30883                 
30884                 break;
30885             default : 
30886                 break;
30887         }
30888         
30889         this.cropData = canvas.toDataURL(this.cropType);
30890         
30891         if(this.fireEvent('crop', this, this.cropData) !== false){
30892             this.process(this.file, this.cropData);
30893         }
30894         
30895         return;
30896         
30897     },
30898     
30899     setThumbBoxSize : function()
30900     {
30901         var width, height;
30902         
30903         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30904             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30905             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30906             
30907             this.minWidth = width;
30908             this.minHeight = height;
30909             
30910             if(this.rotate == 90 || this.rotate == 270){
30911                 this.minWidth = height;
30912                 this.minHeight = width;
30913             }
30914         }
30915         
30916         height = 300;
30917         width = Math.ceil(this.minWidth * height / this.minHeight);
30918         
30919         if(this.minWidth > this.minHeight){
30920             width = 300;
30921             height = Math.ceil(this.minHeight * width / this.minWidth);
30922         }
30923         
30924         this.thumbEl.setStyle({
30925             width : width + 'px',
30926             height : height + 'px'
30927         });
30928
30929         return;
30930             
30931     },
30932     
30933     setThumbBoxPosition : function()
30934     {
30935         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30936         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30937         
30938         this.thumbEl.setLeft(x);
30939         this.thumbEl.setTop(y);
30940         
30941     },
30942     
30943     baseRotateLevel : function()
30944     {
30945         this.baseRotate = 1;
30946         
30947         if(
30948                 typeof(this.exif) != 'undefined' &&
30949                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30950                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30951         ){
30952             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30953         }
30954         
30955         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30956         
30957     },
30958     
30959     baseScaleLevel : function()
30960     {
30961         var width, height;
30962         
30963         if(this.isDocument){
30964             
30965             if(this.baseRotate == 6 || this.baseRotate == 8){
30966             
30967                 height = this.thumbEl.getHeight();
30968                 this.baseScale = height / this.imageEl.OriginWidth;
30969
30970                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30971                     width = this.thumbEl.getWidth();
30972                     this.baseScale = width / this.imageEl.OriginHeight;
30973                 }
30974
30975                 return;
30976             }
30977
30978             height = this.thumbEl.getHeight();
30979             this.baseScale = height / this.imageEl.OriginHeight;
30980
30981             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30982                 width = this.thumbEl.getWidth();
30983                 this.baseScale = width / this.imageEl.OriginWidth;
30984             }
30985
30986             return;
30987         }
30988         
30989         if(this.baseRotate == 6 || this.baseRotate == 8){
30990             
30991             width = this.thumbEl.getHeight();
30992             this.baseScale = width / this.imageEl.OriginHeight;
30993             
30994             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30995                 height = this.thumbEl.getWidth();
30996                 this.baseScale = height / this.imageEl.OriginHeight;
30997             }
30998             
30999             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31000                 height = this.thumbEl.getWidth();
31001                 this.baseScale = height / this.imageEl.OriginHeight;
31002                 
31003                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31004                     width = this.thumbEl.getHeight();
31005                     this.baseScale = width / this.imageEl.OriginWidth;
31006                 }
31007             }
31008             
31009             return;
31010         }
31011         
31012         width = this.thumbEl.getWidth();
31013         this.baseScale = width / this.imageEl.OriginWidth;
31014         
31015         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31016             height = this.thumbEl.getHeight();
31017             this.baseScale = height / this.imageEl.OriginHeight;
31018         }
31019         
31020         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31021             
31022             height = this.thumbEl.getHeight();
31023             this.baseScale = height / this.imageEl.OriginHeight;
31024             
31025             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31026                 width = this.thumbEl.getWidth();
31027                 this.baseScale = width / this.imageEl.OriginWidth;
31028             }
31029             
31030         }
31031         
31032         return;
31033     },
31034     
31035     getScaleLevel : function()
31036     {
31037         return this.baseScale * Math.pow(1.1, this.scale);
31038     },
31039     
31040     onTouchStart : function(e)
31041     {
31042         if(!this.canvasLoaded){
31043             this.beforeSelectFile(e);
31044             return;
31045         }
31046         
31047         var touches = e.browserEvent.touches;
31048         
31049         if(!touches){
31050             return;
31051         }
31052         
31053         if(touches.length == 1){
31054             this.onMouseDown(e);
31055             return;
31056         }
31057         
31058         if(touches.length != 2){
31059             return;
31060         }
31061         
31062         var coords = [];
31063         
31064         for(var i = 0, finger; finger = touches[i]; i++){
31065             coords.push(finger.pageX, finger.pageY);
31066         }
31067         
31068         var x = Math.pow(coords[0] - coords[2], 2);
31069         var y = Math.pow(coords[1] - coords[3], 2);
31070         
31071         this.startDistance = Math.sqrt(x + y);
31072         
31073         this.startScale = this.scale;
31074         
31075         this.pinching = true;
31076         this.dragable = false;
31077         
31078     },
31079     
31080     onTouchMove : function(e)
31081     {
31082         if(!this.pinching && !this.dragable){
31083             return;
31084         }
31085         
31086         var touches = e.browserEvent.touches;
31087         
31088         if(!touches){
31089             return;
31090         }
31091         
31092         if(this.dragable){
31093             this.onMouseMove(e);
31094             return;
31095         }
31096         
31097         var coords = [];
31098         
31099         for(var i = 0, finger; finger = touches[i]; i++){
31100             coords.push(finger.pageX, finger.pageY);
31101         }
31102         
31103         var x = Math.pow(coords[0] - coords[2], 2);
31104         var y = Math.pow(coords[1] - coords[3], 2);
31105         
31106         this.endDistance = Math.sqrt(x + y);
31107         
31108         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31109         
31110         if(!this.zoomable()){
31111             this.scale = this.startScale;
31112             return;
31113         }
31114         
31115         this.draw();
31116         
31117     },
31118     
31119     onTouchEnd : function(e)
31120     {
31121         this.pinching = false;
31122         this.dragable = false;
31123         
31124     },
31125     
31126     process : function(file, crop)
31127     {
31128         if(this.loadMask){
31129             this.maskEl.mask(this.loadingText);
31130         }
31131         
31132         this.xhr = new XMLHttpRequest();
31133         
31134         file.xhr = this.xhr;
31135
31136         this.xhr.open(this.method, this.url, true);
31137         
31138         var headers = {
31139             "Accept": "application/json",
31140             "Cache-Control": "no-cache",
31141             "X-Requested-With": "XMLHttpRequest"
31142         };
31143         
31144         for (var headerName in headers) {
31145             var headerValue = headers[headerName];
31146             if (headerValue) {
31147                 this.xhr.setRequestHeader(headerName, headerValue);
31148             }
31149         }
31150         
31151         var _this = this;
31152         
31153         this.xhr.onload = function()
31154         {
31155             _this.xhrOnLoad(_this.xhr);
31156         }
31157         
31158         this.xhr.onerror = function()
31159         {
31160             _this.xhrOnError(_this.xhr);
31161         }
31162         
31163         var formData = new FormData();
31164
31165         formData.append('returnHTML', 'NO');
31166         
31167         if(crop){
31168             formData.append('crop', crop);
31169         }
31170         
31171         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31172             formData.append(this.paramName, file, file.name);
31173         }
31174         
31175         if(typeof(file.filename) != 'undefined'){
31176             formData.append('filename', file.filename);
31177         }
31178         
31179         if(typeof(file.mimetype) != 'undefined'){
31180             formData.append('mimetype', file.mimetype);
31181         }
31182         
31183         if(this.fireEvent('arrange', this, formData) != false){
31184             this.xhr.send(formData);
31185         };
31186     },
31187     
31188     xhrOnLoad : function(xhr)
31189     {
31190         if(this.loadMask){
31191             this.maskEl.unmask();
31192         }
31193         
31194         if (xhr.readyState !== 4) {
31195             this.fireEvent('exception', this, xhr);
31196             return;
31197         }
31198
31199         var response = Roo.decode(xhr.responseText);
31200         
31201         if(!response.success){
31202             this.fireEvent('exception', this, xhr);
31203             return;
31204         }
31205         
31206         var response = Roo.decode(xhr.responseText);
31207         
31208         this.fireEvent('upload', this, response);
31209         
31210     },
31211     
31212     xhrOnError : function()
31213     {
31214         if(this.loadMask){
31215             this.maskEl.unmask();
31216         }
31217         
31218         Roo.log('xhr on error');
31219         
31220         var response = Roo.decode(xhr.responseText);
31221           
31222         Roo.log(response);
31223         
31224     },
31225     
31226     prepare : function(file)
31227     {   
31228         if(this.loadMask){
31229             this.maskEl.mask(this.loadingText);
31230         }
31231         
31232         this.file = false;
31233         this.exif = {};
31234         
31235         if(typeof(file) === 'string'){
31236             this.loadCanvas(file);
31237             return;
31238         }
31239         
31240         if(!file || !this.urlAPI){
31241             return;
31242         }
31243         
31244         this.file = file;
31245         this.cropType = file.type;
31246         
31247         var _this = this;
31248         
31249         if(this.fireEvent('prepare', this, this.file) != false){
31250             
31251             var reader = new FileReader();
31252             
31253             reader.onload = function (e) {
31254                 if (e.target.error) {
31255                     Roo.log(e.target.error);
31256                     return;
31257                 }
31258                 
31259                 var buffer = e.target.result,
31260                     dataView = new DataView(buffer),
31261                     offset = 2,
31262                     maxOffset = dataView.byteLength - 4,
31263                     markerBytes,
31264                     markerLength;
31265                 
31266                 if (dataView.getUint16(0) === 0xffd8) {
31267                     while (offset < maxOffset) {
31268                         markerBytes = dataView.getUint16(offset);
31269                         
31270                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31271                             markerLength = dataView.getUint16(offset + 2) + 2;
31272                             if (offset + markerLength > dataView.byteLength) {
31273                                 Roo.log('Invalid meta data: Invalid segment size.');
31274                                 break;
31275                             }
31276                             
31277                             if(markerBytes == 0xffe1){
31278                                 _this.parseExifData(
31279                                     dataView,
31280                                     offset,
31281                                     markerLength
31282                                 );
31283                             }
31284                             
31285                             offset += markerLength;
31286                             
31287                             continue;
31288                         }
31289                         
31290                         break;
31291                     }
31292                     
31293                 }
31294                 
31295                 var url = _this.urlAPI.createObjectURL(_this.file);
31296                 
31297                 _this.loadCanvas(url);
31298                 
31299                 return;
31300             }
31301             
31302             reader.readAsArrayBuffer(this.file);
31303             
31304         }
31305         
31306     },
31307     
31308     parseExifData : function(dataView, offset, length)
31309     {
31310         var tiffOffset = offset + 10,
31311             littleEndian,
31312             dirOffset;
31313     
31314         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31315             // No Exif data, might be XMP data instead
31316             return;
31317         }
31318         
31319         // Check for the ASCII code for "Exif" (0x45786966):
31320         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31321             // No Exif data, might be XMP data instead
31322             return;
31323         }
31324         if (tiffOffset + 8 > dataView.byteLength) {
31325             Roo.log('Invalid Exif data: Invalid segment size.');
31326             return;
31327         }
31328         // Check for the two null bytes:
31329         if (dataView.getUint16(offset + 8) !== 0x0000) {
31330             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31331             return;
31332         }
31333         // Check the byte alignment:
31334         switch (dataView.getUint16(tiffOffset)) {
31335         case 0x4949:
31336             littleEndian = true;
31337             break;
31338         case 0x4D4D:
31339             littleEndian = false;
31340             break;
31341         default:
31342             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31343             return;
31344         }
31345         // Check for the TIFF tag marker (0x002A):
31346         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31347             Roo.log('Invalid Exif data: Missing TIFF marker.');
31348             return;
31349         }
31350         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31351         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31352         
31353         this.parseExifTags(
31354             dataView,
31355             tiffOffset,
31356             tiffOffset + dirOffset,
31357             littleEndian
31358         );
31359     },
31360     
31361     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31362     {
31363         var tagsNumber,
31364             dirEndOffset,
31365             i;
31366         if (dirOffset + 6 > dataView.byteLength) {
31367             Roo.log('Invalid Exif data: Invalid directory offset.');
31368             return;
31369         }
31370         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31371         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31372         if (dirEndOffset + 4 > dataView.byteLength) {
31373             Roo.log('Invalid Exif data: Invalid directory size.');
31374             return;
31375         }
31376         for (i = 0; i < tagsNumber; i += 1) {
31377             this.parseExifTag(
31378                 dataView,
31379                 tiffOffset,
31380                 dirOffset + 2 + 12 * i, // tag offset
31381                 littleEndian
31382             );
31383         }
31384         // Return the offset to the next directory:
31385         return dataView.getUint32(dirEndOffset, littleEndian);
31386     },
31387     
31388     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31389     {
31390         var tag = dataView.getUint16(offset, littleEndian);
31391         
31392         this.exif[tag] = this.getExifValue(
31393             dataView,
31394             tiffOffset,
31395             offset,
31396             dataView.getUint16(offset + 2, littleEndian), // tag type
31397             dataView.getUint32(offset + 4, littleEndian), // tag length
31398             littleEndian
31399         );
31400     },
31401     
31402     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31403     {
31404         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31405             tagSize,
31406             dataOffset,
31407             values,
31408             i,
31409             str,
31410             c;
31411     
31412         if (!tagType) {
31413             Roo.log('Invalid Exif data: Invalid tag type.');
31414             return;
31415         }
31416         
31417         tagSize = tagType.size * length;
31418         // Determine if the value is contained in the dataOffset bytes,
31419         // or if the value at the dataOffset is a pointer to the actual data:
31420         dataOffset = tagSize > 4 ?
31421                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31422         if (dataOffset + tagSize > dataView.byteLength) {
31423             Roo.log('Invalid Exif data: Invalid data offset.');
31424             return;
31425         }
31426         if (length === 1) {
31427             return tagType.getValue(dataView, dataOffset, littleEndian);
31428         }
31429         values = [];
31430         for (i = 0; i < length; i += 1) {
31431             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31432         }
31433         
31434         if (tagType.ascii) {
31435             str = '';
31436             // Concatenate the chars:
31437             for (i = 0; i < values.length; i += 1) {
31438                 c = values[i];
31439                 // Ignore the terminating NULL byte(s):
31440                 if (c === '\u0000') {
31441                     break;
31442                 }
31443                 str += c;
31444             }
31445             return str;
31446         }
31447         return values;
31448     }
31449     
31450 });
31451
31452 Roo.apply(Roo.bootstrap.UploadCropbox, {
31453     tags : {
31454         'Orientation': 0x0112
31455     },
31456     
31457     Orientation: {
31458             1: 0, //'top-left',
31459 //            2: 'top-right',
31460             3: 180, //'bottom-right',
31461 //            4: 'bottom-left',
31462 //            5: 'left-top',
31463             6: 90, //'right-top',
31464 //            7: 'right-bottom',
31465             8: 270 //'left-bottom'
31466     },
31467     
31468     exifTagTypes : {
31469         // byte, 8-bit unsigned int:
31470         1: {
31471             getValue: function (dataView, dataOffset) {
31472                 return dataView.getUint8(dataOffset);
31473             },
31474             size: 1
31475         },
31476         // ascii, 8-bit byte:
31477         2: {
31478             getValue: function (dataView, dataOffset) {
31479                 return String.fromCharCode(dataView.getUint8(dataOffset));
31480             },
31481             size: 1,
31482             ascii: true
31483         },
31484         // short, 16 bit int:
31485         3: {
31486             getValue: function (dataView, dataOffset, littleEndian) {
31487                 return dataView.getUint16(dataOffset, littleEndian);
31488             },
31489             size: 2
31490         },
31491         // long, 32 bit int:
31492         4: {
31493             getValue: function (dataView, dataOffset, littleEndian) {
31494                 return dataView.getUint32(dataOffset, littleEndian);
31495             },
31496             size: 4
31497         },
31498         // rational = two long values, first is numerator, second is denominator:
31499         5: {
31500             getValue: function (dataView, dataOffset, littleEndian) {
31501                 return dataView.getUint32(dataOffset, littleEndian) /
31502                     dataView.getUint32(dataOffset + 4, littleEndian);
31503             },
31504             size: 8
31505         },
31506         // slong, 32 bit signed int:
31507         9: {
31508             getValue: function (dataView, dataOffset, littleEndian) {
31509                 return dataView.getInt32(dataOffset, littleEndian);
31510             },
31511             size: 4
31512         },
31513         // srational, two slongs, first is numerator, second is denominator:
31514         10: {
31515             getValue: function (dataView, dataOffset, littleEndian) {
31516                 return dataView.getInt32(dataOffset, littleEndian) /
31517                     dataView.getInt32(dataOffset + 4, littleEndian);
31518             },
31519             size: 8
31520         }
31521     },
31522     
31523     footer : {
31524         STANDARD : [
31525             {
31526                 tag : 'div',
31527                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31528                 action : 'rotate-left',
31529                 cn : [
31530                     {
31531                         tag : 'button',
31532                         cls : 'btn btn-default',
31533                         html : '<i class="fa fa-undo"></i>'
31534                     }
31535                 ]
31536             },
31537             {
31538                 tag : 'div',
31539                 cls : 'btn-group roo-upload-cropbox-picture',
31540                 action : 'picture',
31541                 cn : [
31542                     {
31543                         tag : 'button',
31544                         cls : 'btn btn-default',
31545                         html : '<i class="fa fa-picture-o"></i>'
31546                     }
31547                 ]
31548             },
31549             {
31550                 tag : 'div',
31551                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31552                 action : 'rotate-right',
31553                 cn : [
31554                     {
31555                         tag : 'button',
31556                         cls : 'btn btn-default',
31557                         html : '<i class="fa fa-repeat"></i>'
31558                     }
31559                 ]
31560             }
31561         ],
31562         DOCUMENT : [
31563             {
31564                 tag : 'div',
31565                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31566                 action : 'rotate-left',
31567                 cn : [
31568                     {
31569                         tag : 'button',
31570                         cls : 'btn btn-default',
31571                         html : '<i class="fa fa-undo"></i>'
31572                     }
31573                 ]
31574             },
31575             {
31576                 tag : 'div',
31577                 cls : 'btn-group roo-upload-cropbox-download',
31578                 action : 'download',
31579                 cn : [
31580                     {
31581                         tag : 'button',
31582                         cls : 'btn btn-default',
31583                         html : '<i class="fa fa-download"></i>'
31584                     }
31585                 ]
31586             },
31587             {
31588                 tag : 'div',
31589                 cls : 'btn-group roo-upload-cropbox-crop',
31590                 action : 'crop',
31591                 cn : [
31592                     {
31593                         tag : 'button',
31594                         cls : 'btn btn-default',
31595                         html : '<i class="fa fa-crop"></i>'
31596                     }
31597                 ]
31598             },
31599             {
31600                 tag : 'div',
31601                 cls : 'btn-group roo-upload-cropbox-trash',
31602                 action : 'trash',
31603                 cn : [
31604                     {
31605                         tag : 'button',
31606                         cls : 'btn btn-default',
31607                         html : '<i class="fa fa-trash"></i>'
31608                     }
31609                 ]
31610             },
31611             {
31612                 tag : 'div',
31613                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31614                 action : 'rotate-right',
31615                 cn : [
31616                     {
31617                         tag : 'button',
31618                         cls : 'btn btn-default',
31619                         html : '<i class="fa fa-repeat"></i>'
31620                     }
31621                 ]
31622             }
31623         ],
31624         ROTATOR : [
31625             {
31626                 tag : 'div',
31627                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31628                 action : 'rotate-left',
31629                 cn : [
31630                     {
31631                         tag : 'button',
31632                         cls : 'btn btn-default',
31633                         html : '<i class="fa fa-undo"></i>'
31634                     }
31635                 ]
31636             },
31637             {
31638                 tag : 'div',
31639                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31640                 action : 'rotate-right',
31641                 cn : [
31642                     {
31643                         tag : 'button',
31644                         cls : 'btn btn-default',
31645                         html : '<i class="fa fa-repeat"></i>'
31646                     }
31647                 ]
31648             }
31649         ]
31650     }
31651 });
31652
31653 /*
31654 * Licence: LGPL
31655 */
31656
31657 /**
31658  * @class Roo.bootstrap.DocumentManager
31659  * @extends Roo.bootstrap.Component
31660  * Bootstrap DocumentManager class
31661  * @cfg {String} paramName default 'imageUpload'
31662  * @cfg {String} toolTipName default 'filename'
31663  * @cfg {String} method default POST
31664  * @cfg {String} url action url
31665  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31666  * @cfg {Boolean} multiple multiple upload default true
31667  * @cfg {Number} thumbSize default 300
31668  * @cfg {String} fieldLabel
31669  * @cfg {Number} labelWidth default 4
31670  * @cfg {String} labelAlign (left|top) default left
31671  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31672 * @cfg {Number} labellg set the width of label (1-12)
31673  * @cfg {Number} labelmd set the width of label (1-12)
31674  * @cfg {Number} labelsm set the width of label (1-12)
31675  * @cfg {Number} labelxs set the width of label (1-12)
31676  * 
31677  * @constructor
31678  * Create a new DocumentManager
31679  * @param {Object} config The config object
31680  */
31681
31682 Roo.bootstrap.DocumentManager = function(config){
31683     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31684     
31685     this.files = [];
31686     this.delegates = [];
31687     
31688     this.addEvents({
31689         /**
31690          * @event initial
31691          * Fire when initial the DocumentManager
31692          * @param {Roo.bootstrap.DocumentManager} this
31693          */
31694         "initial" : true,
31695         /**
31696          * @event inspect
31697          * inspect selected file
31698          * @param {Roo.bootstrap.DocumentManager} this
31699          * @param {File} file
31700          */
31701         "inspect" : true,
31702         /**
31703          * @event exception
31704          * Fire when xhr load exception
31705          * @param {Roo.bootstrap.DocumentManager} this
31706          * @param {XMLHttpRequest} xhr
31707          */
31708         "exception" : true,
31709         /**
31710          * @event afterupload
31711          * Fire when xhr load exception
31712          * @param {Roo.bootstrap.DocumentManager} this
31713          * @param {XMLHttpRequest} xhr
31714          */
31715         "afterupload" : true,
31716         /**
31717          * @event prepare
31718          * prepare the form data
31719          * @param {Roo.bootstrap.DocumentManager} this
31720          * @param {Object} formData
31721          */
31722         "prepare" : true,
31723         /**
31724          * @event remove
31725          * Fire when remove the file
31726          * @param {Roo.bootstrap.DocumentManager} this
31727          * @param {Object} file
31728          */
31729         "remove" : true,
31730         /**
31731          * @event refresh
31732          * Fire after refresh the file
31733          * @param {Roo.bootstrap.DocumentManager} this
31734          */
31735         "refresh" : true,
31736         /**
31737          * @event click
31738          * Fire after click the image
31739          * @param {Roo.bootstrap.DocumentManager} this
31740          * @param {Object} file
31741          */
31742         "click" : true,
31743         /**
31744          * @event edit
31745          * Fire when upload a image and editable set to true
31746          * @param {Roo.bootstrap.DocumentManager} this
31747          * @param {Object} file
31748          */
31749         "edit" : true,
31750         /**
31751          * @event beforeselectfile
31752          * Fire before select file
31753          * @param {Roo.bootstrap.DocumentManager} this
31754          */
31755         "beforeselectfile" : true,
31756         /**
31757          * @event process
31758          * Fire before process file
31759          * @param {Roo.bootstrap.DocumentManager} this
31760          * @param {Object} file
31761          */
31762         "process" : true,
31763         /**
31764          * @event previewrendered
31765          * Fire when preview rendered
31766          * @param {Roo.bootstrap.DocumentManager} this
31767          * @param {Object} file
31768          */
31769         "previewrendered" : true,
31770         /**
31771          */
31772         "previewResize" : true
31773         
31774     });
31775 };
31776
31777 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31778     
31779     boxes : 0,
31780     inputName : '',
31781     thumbSize : 300,
31782     multiple : true,
31783     files : false,
31784     method : 'POST',
31785     url : '',
31786     paramName : 'imageUpload',
31787     toolTipName : 'filename',
31788     fieldLabel : '',
31789     labelWidth : 4,
31790     labelAlign : 'left',
31791     editable : true,
31792     delegates : false,
31793     xhr : false, 
31794     
31795     labellg : 0,
31796     labelmd : 0,
31797     labelsm : 0,
31798     labelxs : 0,
31799     
31800     getAutoCreate : function()
31801     {   
31802         var managerWidget = {
31803             tag : 'div',
31804             cls : 'roo-document-manager',
31805             cn : [
31806                 {
31807                     tag : 'input',
31808                     cls : 'roo-document-manager-selector',
31809                     type : 'file'
31810                 },
31811                 {
31812                     tag : 'div',
31813                     cls : 'roo-document-manager-uploader',
31814                     cn : [
31815                         {
31816                             tag : 'div',
31817                             cls : 'roo-document-manager-upload-btn',
31818                             html : '<i class="fa fa-plus"></i>'
31819                         }
31820                     ]
31821                     
31822                 }
31823             ]
31824         };
31825         
31826         var content = [
31827             {
31828                 tag : 'div',
31829                 cls : 'column col-md-12',
31830                 cn : managerWidget
31831             }
31832         ];
31833         
31834         if(this.fieldLabel.length){
31835             
31836             content = [
31837                 {
31838                     tag : 'div',
31839                     cls : 'column col-md-12',
31840                     html : this.fieldLabel
31841                 },
31842                 {
31843                     tag : 'div',
31844                     cls : 'column col-md-12',
31845                     cn : managerWidget
31846                 }
31847             ];
31848
31849             if(this.labelAlign == 'left'){
31850                 content = [
31851                     {
31852                         tag : 'div',
31853                         cls : 'column',
31854                         html : this.fieldLabel
31855                     },
31856                     {
31857                         tag : 'div',
31858                         cls : 'column',
31859                         cn : managerWidget
31860                     }
31861                 ];
31862                 
31863                 if(this.labelWidth > 12){
31864                     content[0].style = "width: " + this.labelWidth + 'px';
31865                 }
31866
31867                 if(this.labelWidth < 13 && this.labelmd == 0){
31868                     this.labelmd = this.labelWidth;
31869                 }
31870
31871                 if(this.labellg > 0){
31872                     content[0].cls += ' col-lg-' + this.labellg;
31873                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31874                 }
31875
31876                 if(this.labelmd > 0){
31877                     content[0].cls += ' col-md-' + this.labelmd;
31878                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31879                 }
31880
31881                 if(this.labelsm > 0){
31882                     content[0].cls += ' col-sm-' + this.labelsm;
31883                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31884                 }
31885
31886                 if(this.labelxs > 0){
31887                     content[0].cls += ' col-xs-' + this.labelxs;
31888                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31889                 }
31890                 
31891             }
31892         }
31893         
31894         var cfg = {
31895             tag : 'div',
31896             cls : 'row clearfix',
31897             cn : content
31898         };
31899         
31900         return cfg;
31901         
31902     },
31903     
31904     initEvents : function()
31905     {
31906         this.managerEl = this.el.select('.roo-document-manager', true).first();
31907         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31908         
31909         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31910         this.selectorEl.hide();
31911         
31912         if(this.multiple){
31913             this.selectorEl.attr('multiple', 'multiple');
31914         }
31915         
31916         this.selectorEl.on('change', this.onFileSelected, this);
31917         
31918         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31919         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31920         
31921         this.uploader.on('click', this.onUploaderClick, this);
31922         
31923         this.renderProgressDialog();
31924         
31925         var _this = this;
31926         
31927         window.addEventListener("resize", function() { _this.refresh(); } );
31928         
31929         this.fireEvent('initial', this);
31930     },
31931     
31932     renderProgressDialog : function()
31933     {
31934         var _this = this;
31935         
31936         this.progressDialog = new Roo.bootstrap.Modal({
31937             cls : 'roo-document-manager-progress-dialog',
31938             allow_close : false,
31939             animate : false,
31940             title : '',
31941             buttons : [
31942                 {
31943                     name  :'cancel',
31944                     weight : 'danger',
31945                     html : 'Cancel'
31946                 }
31947             ], 
31948             listeners : { 
31949                 btnclick : function() {
31950                     _this.uploadCancel();
31951                     this.hide();
31952                 }
31953             }
31954         });
31955          
31956         this.progressDialog.render(Roo.get(document.body));
31957          
31958         this.progress = new Roo.bootstrap.Progress({
31959             cls : 'roo-document-manager-progress',
31960             active : true,
31961             striped : true
31962         });
31963         
31964         this.progress.render(this.progressDialog.getChildContainer());
31965         
31966         this.progressBar = new Roo.bootstrap.ProgressBar({
31967             cls : 'roo-document-manager-progress-bar',
31968             aria_valuenow : 0,
31969             aria_valuemin : 0,
31970             aria_valuemax : 12,
31971             panel : 'success'
31972         });
31973         
31974         this.progressBar.render(this.progress.getChildContainer());
31975     },
31976     
31977     onUploaderClick : function(e)
31978     {
31979         e.preventDefault();
31980      
31981         if(this.fireEvent('beforeselectfile', this) != false){
31982             this.selectorEl.dom.click();
31983         }
31984         
31985     },
31986     
31987     onFileSelected : function(e)
31988     {
31989         e.preventDefault();
31990         
31991         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31992             return;
31993         }
31994         
31995         Roo.each(this.selectorEl.dom.files, function(file){
31996             if(this.fireEvent('inspect', this, file) != false){
31997                 this.files.push(file);
31998             }
31999         }, this);
32000         
32001         this.queue();
32002         
32003     },
32004     
32005     queue : function()
32006     {
32007         this.selectorEl.dom.value = '';
32008         
32009         if(!this.files || !this.files.length){
32010             return;
32011         }
32012         
32013         if(this.boxes > 0 && this.files.length > this.boxes){
32014             this.files = this.files.slice(0, this.boxes);
32015         }
32016         
32017         this.uploader.show();
32018         
32019         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32020             this.uploader.hide();
32021         }
32022         
32023         var _this = this;
32024         
32025         var files = [];
32026         
32027         var docs = [];
32028         
32029         Roo.each(this.files, function(file){
32030             
32031             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32032                 var f = this.renderPreview(file);
32033                 files.push(f);
32034                 return;
32035             }
32036             
32037             if(file.type.indexOf('image') != -1){
32038                 this.delegates.push(
32039                     (function(){
32040                         _this.process(file);
32041                     }).createDelegate(this)
32042                 );
32043         
32044                 return;
32045             }
32046             
32047             docs.push(
32048                 (function(){
32049                     _this.process(file);
32050                 }).createDelegate(this)
32051             );
32052             
32053         }, this);
32054         
32055         this.files = files;
32056         
32057         this.delegates = this.delegates.concat(docs);
32058         
32059         if(!this.delegates.length){
32060             this.refresh();
32061             return;
32062         }
32063         
32064         this.progressBar.aria_valuemax = this.delegates.length;
32065         
32066         this.arrange();
32067         
32068         return;
32069     },
32070     
32071     arrange : function()
32072     {
32073         if(!this.delegates.length){
32074             this.progressDialog.hide();
32075             this.refresh();
32076             return;
32077         }
32078         
32079         var delegate = this.delegates.shift();
32080         
32081         this.progressDialog.show();
32082         
32083         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32084         
32085         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32086         
32087         delegate();
32088     },
32089     
32090     refresh : function()
32091     {
32092         this.uploader.show();
32093         
32094         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32095             this.uploader.hide();
32096         }
32097         
32098         Roo.isTouch ? this.closable(false) : this.closable(true);
32099         
32100         this.fireEvent('refresh', this);
32101     },
32102     
32103     onRemove : function(e, el, o)
32104     {
32105         e.preventDefault();
32106         
32107         this.fireEvent('remove', this, o);
32108         
32109     },
32110     
32111     remove : function(o)
32112     {
32113         var files = [];
32114         
32115         Roo.each(this.files, function(file){
32116             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32117                 files.push(file);
32118                 return;
32119             }
32120
32121             o.target.remove();
32122
32123         }, this);
32124         
32125         this.files = files;
32126         
32127         this.refresh();
32128     },
32129     
32130     clear : function()
32131     {
32132         Roo.each(this.files, function(file){
32133             if(!file.target){
32134                 return;
32135             }
32136             
32137             file.target.remove();
32138
32139         }, this);
32140         
32141         this.files = [];
32142         
32143         this.refresh();
32144     },
32145     
32146     onClick : function(e, el, o)
32147     {
32148         e.preventDefault();
32149         
32150         this.fireEvent('click', this, o);
32151         
32152     },
32153     
32154     closable : function(closable)
32155     {
32156         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32157             
32158             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32159             
32160             if(closable){
32161                 el.show();
32162                 return;
32163             }
32164             
32165             el.hide();
32166             
32167         }, this);
32168     },
32169     
32170     xhrOnLoad : function(xhr)
32171     {
32172         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32173             el.remove();
32174         }, this);
32175         
32176         if (xhr.readyState !== 4) {
32177             this.arrange();
32178             this.fireEvent('exception', this, xhr);
32179             return;
32180         }
32181
32182         var response = Roo.decode(xhr.responseText);
32183         
32184         if(!response.success){
32185             this.arrange();
32186             this.fireEvent('exception', this, xhr);
32187             return;
32188         }
32189         
32190         var file = this.renderPreview(response.data);
32191         
32192         this.files.push(file);
32193         
32194         this.arrange();
32195         
32196         this.fireEvent('afterupload', this, xhr);
32197         
32198     },
32199     
32200     xhrOnError : function(xhr)
32201     {
32202         Roo.log('xhr on error');
32203         
32204         var response = Roo.decode(xhr.responseText);
32205           
32206         Roo.log(response);
32207         
32208         this.arrange();
32209     },
32210     
32211     process : function(file)
32212     {
32213         if(this.fireEvent('process', this, file) !== false){
32214             if(this.editable && file.type.indexOf('image') != -1){
32215                 this.fireEvent('edit', this, file);
32216                 return;
32217             }
32218
32219             this.uploadStart(file, false);
32220
32221             return;
32222         }
32223         
32224     },
32225     
32226     uploadStart : function(file, crop)
32227     {
32228         this.xhr = new XMLHttpRequest();
32229         
32230         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32231             this.arrange();
32232             return;
32233         }
32234         
32235         file.xhr = this.xhr;
32236             
32237         this.managerEl.createChild({
32238             tag : 'div',
32239             cls : 'roo-document-manager-loading',
32240             cn : [
32241                 {
32242                     tag : 'div',
32243                     tooltip : file.name,
32244                     cls : 'roo-document-manager-thumb',
32245                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32246                 }
32247             ]
32248
32249         });
32250
32251         this.xhr.open(this.method, this.url, true);
32252         
32253         var headers = {
32254             "Accept": "application/json",
32255             "Cache-Control": "no-cache",
32256             "X-Requested-With": "XMLHttpRequest"
32257         };
32258         
32259         for (var headerName in headers) {
32260             var headerValue = headers[headerName];
32261             if (headerValue) {
32262                 this.xhr.setRequestHeader(headerName, headerValue);
32263             }
32264         }
32265         
32266         var _this = this;
32267         
32268         this.xhr.onload = function()
32269         {
32270             _this.xhrOnLoad(_this.xhr);
32271         }
32272         
32273         this.xhr.onerror = function()
32274         {
32275             _this.xhrOnError(_this.xhr);
32276         }
32277         
32278         var formData = new FormData();
32279
32280         formData.append('returnHTML', 'NO');
32281         
32282         if(crop){
32283             formData.append('crop', crop);
32284         }
32285         
32286         formData.append(this.paramName, file, file.name);
32287         
32288         var options = {
32289             file : file, 
32290             manually : false
32291         };
32292         
32293         if(this.fireEvent('prepare', this, formData, options) != false){
32294             
32295             if(options.manually){
32296                 return;
32297             }
32298             
32299             this.xhr.send(formData);
32300             return;
32301         };
32302         
32303         this.uploadCancel();
32304     },
32305     
32306     uploadCancel : function()
32307     {
32308         if (this.xhr) {
32309             this.xhr.abort();
32310         }
32311         
32312         this.delegates = [];
32313         
32314         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32315             el.remove();
32316         }, this);
32317         
32318         this.arrange();
32319     },
32320     
32321     renderPreview : function(file)
32322     {
32323         if(typeof(file.target) != 'undefined' && file.target){
32324             return file;
32325         }
32326         
32327         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32328         
32329         var previewEl = this.managerEl.createChild({
32330             tag : 'div',
32331             cls : 'roo-document-manager-preview',
32332             cn : [
32333                 {
32334                     tag : 'div',
32335                     tooltip : file[this.toolTipName],
32336                     cls : 'roo-document-manager-thumb',
32337                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32338                 },
32339                 {
32340                     tag : 'button',
32341                     cls : 'close',
32342                     html : '<i class="fa fa-times-circle"></i>'
32343                 }
32344             ]
32345         });
32346
32347         var close = previewEl.select('button.close', true).first();
32348
32349         close.on('click', this.onRemove, this, file);
32350
32351         file.target = previewEl;
32352
32353         var image = previewEl.select('img', true).first();
32354         
32355         var _this = this;
32356         
32357         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32358         
32359         image.on('click', this.onClick, this, file);
32360         
32361         this.fireEvent('previewrendered', this, file);
32362         
32363         return file;
32364         
32365     },
32366     
32367     onPreviewLoad : function(file, image)
32368     {
32369         if(typeof(file.target) == 'undefined' || !file.target){
32370             return;
32371         }
32372         
32373         var width = image.dom.naturalWidth || image.dom.width;
32374         var height = image.dom.naturalHeight || image.dom.height;
32375         
32376         if(!this.previewResize) {
32377             return;
32378         }
32379         
32380         if(width > height){
32381             file.target.addClass('wide');
32382             return;
32383         }
32384         
32385         file.target.addClass('tall');
32386         return;
32387         
32388     },
32389     
32390     uploadFromSource : function(file, crop)
32391     {
32392         this.xhr = new XMLHttpRequest();
32393         
32394         this.managerEl.createChild({
32395             tag : 'div',
32396             cls : 'roo-document-manager-loading',
32397             cn : [
32398                 {
32399                     tag : 'div',
32400                     tooltip : file.name,
32401                     cls : 'roo-document-manager-thumb',
32402                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32403                 }
32404             ]
32405
32406         });
32407
32408         this.xhr.open(this.method, this.url, true);
32409         
32410         var headers = {
32411             "Accept": "application/json",
32412             "Cache-Control": "no-cache",
32413             "X-Requested-With": "XMLHttpRequest"
32414         };
32415         
32416         for (var headerName in headers) {
32417             var headerValue = headers[headerName];
32418             if (headerValue) {
32419                 this.xhr.setRequestHeader(headerName, headerValue);
32420             }
32421         }
32422         
32423         var _this = this;
32424         
32425         this.xhr.onload = function()
32426         {
32427             _this.xhrOnLoad(_this.xhr);
32428         }
32429         
32430         this.xhr.onerror = function()
32431         {
32432             _this.xhrOnError(_this.xhr);
32433         }
32434         
32435         var formData = new FormData();
32436
32437         formData.append('returnHTML', 'NO');
32438         
32439         formData.append('crop', crop);
32440         
32441         if(typeof(file.filename) != 'undefined'){
32442             formData.append('filename', file.filename);
32443         }
32444         
32445         if(typeof(file.mimetype) != 'undefined'){
32446             formData.append('mimetype', file.mimetype);
32447         }
32448         
32449         Roo.log(formData);
32450         
32451         if(this.fireEvent('prepare', this, formData) != false){
32452             this.xhr.send(formData);
32453         };
32454     }
32455 });
32456
32457 /*
32458 * Licence: LGPL
32459 */
32460
32461 /**
32462  * @class Roo.bootstrap.DocumentViewer
32463  * @extends Roo.bootstrap.Component
32464  * Bootstrap DocumentViewer class
32465  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32466  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32467  * 
32468  * @constructor
32469  * Create a new DocumentViewer
32470  * @param {Object} config The config object
32471  */
32472
32473 Roo.bootstrap.DocumentViewer = function(config){
32474     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32475     
32476     this.addEvents({
32477         /**
32478          * @event initial
32479          * Fire after initEvent
32480          * @param {Roo.bootstrap.DocumentViewer} this
32481          */
32482         "initial" : true,
32483         /**
32484          * @event click
32485          * Fire after click
32486          * @param {Roo.bootstrap.DocumentViewer} this
32487          */
32488         "click" : true,
32489         /**
32490          * @event download
32491          * Fire after download button
32492          * @param {Roo.bootstrap.DocumentViewer} this
32493          */
32494         "download" : true,
32495         /**
32496          * @event trash
32497          * Fire after trash button
32498          * @param {Roo.bootstrap.DocumentViewer} this
32499          */
32500         "trash" : true
32501         
32502     });
32503 };
32504
32505 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32506     
32507     showDownload : true,
32508     
32509     showTrash : true,
32510     
32511     getAutoCreate : function()
32512     {
32513         var cfg = {
32514             tag : 'div',
32515             cls : 'roo-document-viewer',
32516             cn : [
32517                 {
32518                     tag : 'div',
32519                     cls : 'roo-document-viewer-body',
32520                     cn : [
32521                         {
32522                             tag : 'div',
32523                             cls : 'roo-document-viewer-thumb',
32524                             cn : [
32525                                 {
32526                                     tag : 'img',
32527                                     cls : 'roo-document-viewer-image'
32528                                 }
32529                             ]
32530                         }
32531                     ]
32532                 },
32533                 {
32534                     tag : 'div',
32535                     cls : 'roo-document-viewer-footer',
32536                     cn : {
32537                         tag : 'div',
32538                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32539                         cn : [
32540                             {
32541                                 tag : 'div',
32542                                 cls : 'btn-group roo-document-viewer-download',
32543                                 cn : [
32544                                     {
32545                                         tag : 'button',
32546                                         cls : 'btn btn-default',
32547                                         html : '<i class="fa fa-download"></i>'
32548                                     }
32549                                 ]
32550                             },
32551                             {
32552                                 tag : 'div',
32553                                 cls : 'btn-group roo-document-viewer-trash',
32554                                 cn : [
32555                                     {
32556                                         tag : 'button',
32557                                         cls : 'btn btn-default',
32558                                         html : '<i class="fa fa-trash"></i>'
32559                                     }
32560                                 ]
32561                             }
32562                         ]
32563                     }
32564                 }
32565             ]
32566         };
32567         
32568         return cfg;
32569     },
32570     
32571     initEvents : function()
32572     {
32573         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32574         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32575         
32576         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32577         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32578         
32579         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32580         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32581         
32582         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32583         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32584         
32585         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32586         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32587         
32588         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32589         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32590         
32591         this.bodyEl.on('click', this.onClick, this);
32592         this.downloadBtn.on('click', this.onDownload, this);
32593         this.trashBtn.on('click', this.onTrash, this);
32594         
32595         this.downloadBtn.hide();
32596         this.trashBtn.hide();
32597         
32598         if(this.showDownload){
32599             this.downloadBtn.show();
32600         }
32601         
32602         if(this.showTrash){
32603             this.trashBtn.show();
32604         }
32605         
32606         if(!this.showDownload && !this.showTrash) {
32607             this.footerEl.hide();
32608         }
32609         
32610     },
32611     
32612     initial : function()
32613     {
32614         this.fireEvent('initial', this);
32615         
32616     },
32617     
32618     onClick : function(e)
32619     {
32620         e.preventDefault();
32621         
32622         this.fireEvent('click', this);
32623     },
32624     
32625     onDownload : function(e)
32626     {
32627         e.preventDefault();
32628         
32629         this.fireEvent('download', this);
32630     },
32631     
32632     onTrash : function(e)
32633     {
32634         e.preventDefault();
32635         
32636         this.fireEvent('trash', this);
32637     }
32638     
32639 });
32640 /*
32641  * - LGPL
32642  *
32643  * nav progress bar
32644  * 
32645  */
32646
32647 /**
32648  * @class Roo.bootstrap.NavProgressBar
32649  * @extends Roo.bootstrap.Component
32650  * Bootstrap NavProgressBar class
32651  * 
32652  * @constructor
32653  * Create a new nav progress bar
32654  * @param {Object} config The config object
32655  */
32656
32657 Roo.bootstrap.NavProgressBar = function(config){
32658     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32659
32660     this.bullets = this.bullets || [];
32661    
32662 //    Roo.bootstrap.NavProgressBar.register(this);
32663      this.addEvents({
32664         /**
32665              * @event changed
32666              * Fires when the active item changes
32667              * @param {Roo.bootstrap.NavProgressBar} this
32668              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32669              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32670          */
32671         'changed': true
32672      });
32673     
32674 };
32675
32676 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32677     
32678     bullets : [],
32679     barItems : [],
32680     
32681     getAutoCreate : function()
32682     {
32683         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32684         
32685         cfg = {
32686             tag : 'div',
32687             cls : 'roo-navigation-bar-group',
32688             cn : [
32689                 {
32690                     tag : 'div',
32691                     cls : 'roo-navigation-top-bar'
32692                 },
32693                 {
32694                     tag : 'div',
32695                     cls : 'roo-navigation-bullets-bar',
32696                     cn : [
32697                         {
32698                             tag : 'ul',
32699                             cls : 'roo-navigation-bar'
32700                         }
32701                     ]
32702                 },
32703                 
32704                 {
32705                     tag : 'div',
32706                     cls : 'roo-navigation-bottom-bar'
32707                 }
32708             ]
32709             
32710         };
32711         
32712         return cfg;
32713         
32714     },
32715     
32716     initEvents: function() 
32717     {
32718         
32719     },
32720     
32721     onRender : function(ct, position) 
32722     {
32723         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32724         
32725         if(this.bullets.length){
32726             Roo.each(this.bullets, function(b){
32727                this.addItem(b);
32728             }, this);
32729         }
32730         
32731         this.format();
32732         
32733     },
32734     
32735     addItem : function(cfg)
32736     {
32737         var item = new Roo.bootstrap.NavProgressItem(cfg);
32738         
32739         item.parentId = this.id;
32740         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32741         
32742         if(cfg.html){
32743             var top = new Roo.bootstrap.Element({
32744                 tag : 'div',
32745                 cls : 'roo-navigation-bar-text'
32746             });
32747             
32748             var bottom = new Roo.bootstrap.Element({
32749                 tag : 'div',
32750                 cls : 'roo-navigation-bar-text'
32751             });
32752             
32753             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32754             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32755             
32756             var topText = new Roo.bootstrap.Element({
32757                 tag : 'span',
32758                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32759             });
32760             
32761             var bottomText = new Roo.bootstrap.Element({
32762                 tag : 'span',
32763                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32764             });
32765             
32766             topText.onRender(top.el, null);
32767             bottomText.onRender(bottom.el, null);
32768             
32769             item.topEl = top;
32770             item.bottomEl = bottom;
32771         }
32772         
32773         this.barItems.push(item);
32774         
32775         return item;
32776     },
32777     
32778     getActive : function()
32779     {
32780         var active = false;
32781         
32782         Roo.each(this.barItems, function(v){
32783             
32784             if (!v.isActive()) {
32785                 return;
32786             }
32787             
32788             active = v;
32789             return false;
32790             
32791         });
32792         
32793         return active;
32794     },
32795     
32796     setActiveItem : function(item)
32797     {
32798         var prev = false;
32799         
32800         Roo.each(this.barItems, function(v){
32801             if (v.rid == item.rid) {
32802                 return ;
32803             }
32804             
32805             if (v.isActive()) {
32806                 v.setActive(false);
32807                 prev = v;
32808             }
32809         });
32810
32811         item.setActive(true);
32812         
32813         this.fireEvent('changed', this, item, prev);
32814     },
32815     
32816     getBarItem: function(rid)
32817     {
32818         var ret = false;
32819         
32820         Roo.each(this.barItems, function(e) {
32821             if (e.rid != rid) {
32822                 return;
32823             }
32824             
32825             ret =  e;
32826             return false;
32827         });
32828         
32829         return ret;
32830     },
32831     
32832     indexOfItem : function(item)
32833     {
32834         var index = false;
32835         
32836         Roo.each(this.barItems, function(v, i){
32837             
32838             if (v.rid != item.rid) {
32839                 return;
32840             }
32841             
32842             index = i;
32843             return false
32844         });
32845         
32846         return index;
32847     },
32848     
32849     setActiveNext : function()
32850     {
32851         var i = this.indexOfItem(this.getActive());
32852         
32853         if (i > this.barItems.length) {
32854             return;
32855         }
32856         
32857         this.setActiveItem(this.barItems[i+1]);
32858     },
32859     
32860     setActivePrev : function()
32861     {
32862         var i = this.indexOfItem(this.getActive());
32863         
32864         if (i  < 1) {
32865             return;
32866         }
32867         
32868         this.setActiveItem(this.barItems[i-1]);
32869     },
32870     
32871     format : function()
32872     {
32873         if(!this.barItems.length){
32874             return;
32875         }
32876      
32877         var width = 100 / this.barItems.length;
32878         
32879         Roo.each(this.barItems, function(i){
32880             i.el.setStyle('width', width + '%');
32881             i.topEl.el.setStyle('width', width + '%');
32882             i.bottomEl.el.setStyle('width', width + '%');
32883         }, this);
32884         
32885     }
32886     
32887 });
32888 /*
32889  * - LGPL
32890  *
32891  * Nav Progress Item
32892  * 
32893  */
32894
32895 /**
32896  * @class Roo.bootstrap.NavProgressItem
32897  * @extends Roo.bootstrap.Component
32898  * Bootstrap NavProgressItem class
32899  * @cfg {String} rid the reference id
32900  * @cfg {Boolean} active (true|false) Is item active default false
32901  * @cfg {Boolean} disabled (true|false) Is item active default false
32902  * @cfg {String} html
32903  * @cfg {String} position (top|bottom) text position default bottom
32904  * @cfg {String} icon show icon instead of number
32905  * 
32906  * @constructor
32907  * Create a new NavProgressItem
32908  * @param {Object} config The config object
32909  */
32910 Roo.bootstrap.NavProgressItem = function(config){
32911     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32912     this.addEvents({
32913         // raw events
32914         /**
32915          * @event click
32916          * The raw click event for the entire grid.
32917          * @param {Roo.bootstrap.NavProgressItem} this
32918          * @param {Roo.EventObject} e
32919          */
32920         "click" : true
32921     });
32922    
32923 };
32924
32925 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32926     
32927     rid : '',
32928     active : false,
32929     disabled : false,
32930     html : '',
32931     position : 'bottom',
32932     icon : false,
32933     
32934     getAutoCreate : function()
32935     {
32936         var iconCls = 'roo-navigation-bar-item-icon';
32937         
32938         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32939         
32940         var cfg = {
32941             tag: 'li',
32942             cls: 'roo-navigation-bar-item',
32943             cn : [
32944                 {
32945                     tag : 'i',
32946                     cls : iconCls
32947                 }
32948             ]
32949         };
32950         
32951         if(this.active){
32952             cfg.cls += ' active';
32953         }
32954         if(this.disabled){
32955             cfg.cls += ' disabled';
32956         }
32957         
32958         return cfg;
32959     },
32960     
32961     disable : function()
32962     {
32963         this.setDisabled(true);
32964     },
32965     
32966     enable : function()
32967     {
32968         this.setDisabled(false);
32969     },
32970     
32971     initEvents: function() 
32972     {
32973         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32974         
32975         this.iconEl.on('click', this.onClick, this);
32976     },
32977     
32978     onClick : function(e)
32979     {
32980         e.preventDefault();
32981         
32982         if(this.disabled){
32983             return;
32984         }
32985         
32986         if(this.fireEvent('click', this, e) === false){
32987             return;
32988         };
32989         
32990         this.parent().setActiveItem(this);
32991     },
32992     
32993     isActive: function () 
32994     {
32995         return this.active;
32996     },
32997     
32998     setActive : function(state)
32999     {
33000         if(this.active == state){
33001             return;
33002         }
33003         
33004         this.active = state;
33005         
33006         if (state) {
33007             this.el.addClass('active');
33008             return;
33009         }
33010         
33011         this.el.removeClass('active');
33012         
33013         return;
33014     },
33015     
33016     setDisabled : function(state)
33017     {
33018         if(this.disabled == state){
33019             return;
33020         }
33021         
33022         this.disabled = state;
33023         
33024         if (state) {
33025             this.el.addClass('disabled');
33026             return;
33027         }
33028         
33029         this.el.removeClass('disabled');
33030     },
33031     
33032     tooltipEl : function()
33033     {
33034         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33035     }
33036 });
33037  
33038
33039  /*
33040  * - LGPL
33041  *
33042  * FieldLabel
33043  * 
33044  */
33045
33046 /**
33047  * @class Roo.bootstrap.FieldLabel
33048  * @extends Roo.bootstrap.Component
33049  * Bootstrap FieldLabel class
33050  * @cfg {String} html contents of the element
33051  * @cfg {String} tag tag of the element default label
33052  * @cfg {String} cls class of the element
33053  * @cfg {String} target label target 
33054  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33055  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33056  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33057  * @cfg {String} iconTooltip default "This field is required"
33058  * @cfg {String} indicatorpos (left|right) default left
33059  * 
33060  * @constructor
33061  * Create a new FieldLabel
33062  * @param {Object} config The config object
33063  */
33064
33065 Roo.bootstrap.FieldLabel = function(config){
33066     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33067     
33068     this.addEvents({
33069             /**
33070              * @event invalid
33071              * Fires after the field has been marked as invalid.
33072              * @param {Roo.form.FieldLabel} this
33073              * @param {String} msg The validation message
33074              */
33075             invalid : true,
33076             /**
33077              * @event valid
33078              * Fires after the field has been validated with no errors.
33079              * @param {Roo.form.FieldLabel} this
33080              */
33081             valid : true
33082         });
33083 };
33084
33085 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33086     
33087     tag: 'label',
33088     cls: '',
33089     html: '',
33090     target: '',
33091     allowBlank : true,
33092     invalidClass : 'has-warning',
33093     validClass : 'has-success',
33094     iconTooltip : 'This field is required',
33095     indicatorpos : 'left',
33096     
33097     getAutoCreate : function(){
33098         
33099         var cls = "";
33100         if (!this.allowBlank) {
33101             cls  = "visible";
33102         }
33103         
33104         var cfg = {
33105             tag : this.tag,
33106             cls : 'roo-bootstrap-field-label ' + this.cls,
33107             for : this.target,
33108             cn : [
33109                 {
33110                     tag : 'i',
33111                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33112                     tooltip : this.iconTooltip
33113                 },
33114                 {
33115                     tag : 'span',
33116                     html : this.html
33117                 }
33118             ] 
33119         };
33120         
33121         if(this.indicatorpos == 'right'){
33122             var cfg = {
33123                 tag : this.tag,
33124                 cls : 'roo-bootstrap-field-label ' + this.cls,
33125                 for : this.target,
33126                 cn : [
33127                     {
33128                         tag : 'span',
33129                         html : this.html
33130                     },
33131                     {
33132                         tag : 'i',
33133                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33134                         tooltip : this.iconTooltip
33135                     }
33136                 ] 
33137             };
33138         }
33139         
33140         return cfg;
33141     },
33142     
33143     initEvents: function() 
33144     {
33145         Roo.bootstrap.Element.superclass.initEvents.call(this);
33146         
33147         this.indicator = this.indicatorEl();
33148         
33149         if(this.indicator){
33150             this.indicator.removeClass('visible');
33151             this.indicator.addClass('invisible');
33152         }
33153         
33154         Roo.bootstrap.FieldLabel.register(this);
33155     },
33156     
33157     indicatorEl : function()
33158     {
33159         var indicator = this.el.select('i.roo-required-indicator',true).first();
33160         
33161         if(!indicator){
33162             return false;
33163         }
33164         
33165         return indicator;
33166         
33167     },
33168     
33169     /**
33170      * Mark this field as valid
33171      */
33172     markValid : function()
33173     {
33174         if(this.indicator){
33175             this.indicator.removeClass('visible');
33176             this.indicator.addClass('invisible');
33177         }
33178         if (Roo.bootstrap.version == 3) {
33179             this.el.removeClass(this.invalidClass);
33180             this.el.addClass(this.validClass);
33181         } else {
33182             this.el.removeClass('is-invalid');
33183             this.el.addClass('is-valid');
33184         }
33185         
33186         
33187         this.fireEvent('valid', this);
33188     },
33189     
33190     /**
33191      * Mark this field as invalid
33192      * @param {String} msg The validation message
33193      */
33194     markInvalid : function(msg)
33195     {
33196         if(this.indicator){
33197             this.indicator.removeClass('invisible');
33198             this.indicator.addClass('visible');
33199         }
33200           if (Roo.bootstrap.version == 3) {
33201             this.el.removeClass(this.validClass);
33202             this.el.addClass(this.invalidClass);
33203         } else {
33204             this.el.removeClass('is-valid');
33205             this.el.addClass('is-invalid');
33206         }
33207         
33208         
33209         this.fireEvent('invalid', this, msg);
33210     }
33211     
33212    
33213 });
33214
33215 Roo.apply(Roo.bootstrap.FieldLabel, {
33216     
33217     groups: {},
33218     
33219      /**
33220     * register a FieldLabel Group
33221     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33222     */
33223     register : function(label)
33224     {
33225         if(this.groups.hasOwnProperty(label.target)){
33226             return;
33227         }
33228      
33229         this.groups[label.target] = label;
33230         
33231     },
33232     /**
33233     * fetch a FieldLabel Group based on the target
33234     * @param {string} target
33235     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33236     */
33237     get: function(target) {
33238         if (typeof(this.groups[target]) == 'undefined') {
33239             return false;
33240         }
33241         
33242         return this.groups[target] ;
33243     }
33244 });
33245
33246  
33247
33248  /*
33249  * - LGPL
33250  *
33251  * page DateSplitField.
33252  * 
33253  */
33254
33255
33256 /**
33257  * @class Roo.bootstrap.DateSplitField
33258  * @extends Roo.bootstrap.Component
33259  * Bootstrap DateSplitField class
33260  * @cfg {string} fieldLabel - the label associated
33261  * @cfg {Number} labelWidth set the width of label (0-12)
33262  * @cfg {String} labelAlign (top|left)
33263  * @cfg {Boolean} dayAllowBlank (true|false) default false
33264  * @cfg {Boolean} monthAllowBlank (true|false) default false
33265  * @cfg {Boolean} yearAllowBlank (true|false) default false
33266  * @cfg {string} dayPlaceholder 
33267  * @cfg {string} monthPlaceholder
33268  * @cfg {string} yearPlaceholder
33269  * @cfg {string} dayFormat default 'd'
33270  * @cfg {string} monthFormat default 'm'
33271  * @cfg {string} yearFormat default 'Y'
33272  * @cfg {Number} labellg set the width of label (1-12)
33273  * @cfg {Number} labelmd set the width of label (1-12)
33274  * @cfg {Number} labelsm set the width of label (1-12)
33275  * @cfg {Number} labelxs set the width of label (1-12)
33276
33277  *     
33278  * @constructor
33279  * Create a new DateSplitField
33280  * @param {Object} config The config object
33281  */
33282
33283 Roo.bootstrap.DateSplitField = function(config){
33284     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33285     
33286     this.addEvents({
33287         // raw events
33288          /**
33289          * @event years
33290          * getting the data of years
33291          * @param {Roo.bootstrap.DateSplitField} this
33292          * @param {Object} years
33293          */
33294         "years" : true,
33295         /**
33296          * @event days
33297          * getting the data of days
33298          * @param {Roo.bootstrap.DateSplitField} this
33299          * @param {Object} days
33300          */
33301         "days" : true,
33302         /**
33303          * @event invalid
33304          * Fires after the field has been marked as invalid.
33305          * @param {Roo.form.Field} this
33306          * @param {String} msg The validation message
33307          */
33308         invalid : true,
33309        /**
33310          * @event valid
33311          * Fires after the field has been validated with no errors.
33312          * @param {Roo.form.Field} this
33313          */
33314         valid : true
33315     });
33316 };
33317
33318 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33319     
33320     fieldLabel : '',
33321     labelAlign : 'top',
33322     labelWidth : 3,
33323     dayAllowBlank : false,
33324     monthAllowBlank : false,
33325     yearAllowBlank : false,
33326     dayPlaceholder : '',
33327     monthPlaceholder : '',
33328     yearPlaceholder : '',
33329     dayFormat : 'd',
33330     monthFormat : 'm',
33331     yearFormat : 'Y',
33332     isFormField : true,
33333     labellg : 0,
33334     labelmd : 0,
33335     labelsm : 0,
33336     labelxs : 0,
33337     
33338     getAutoCreate : function()
33339     {
33340         var cfg = {
33341             tag : 'div',
33342             cls : 'row roo-date-split-field-group',
33343             cn : [
33344                 {
33345                     tag : 'input',
33346                     type : 'hidden',
33347                     cls : 'form-hidden-field roo-date-split-field-group-value',
33348                     name : this.name
33349                 }
33350             ]
33351         };
33352         
33353         var labelCls = 'col-md-12';
33354         var contentCls = 'col-md-4';
33355         
33356         if(this.fieldLabel){
33357             
33358             var label = {
33359                 tag : 'div',
33360                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33361                 cn : [
33362                     {
33363                         tag : 'label',
33364                         html : this.fieldLabel
33365                     }
33366                 ]
33367             };
33368             
33369             if(this.labelAlign == 'left'){
33370             
33371                 if(this.labelWidth > 12){
33372                     label.style = "width: " + this.labelWidth + 'px';
33373                 }
33374
33375                 if(this.labelWidth < 13 && this.labelmd == 0){
33376                     this.labelmd = this.labelWidth;
33377                 }
33378
33379                 if(this.labellg > 0){
33380                     labelCls = ' col-lg-' + this.labellg;
33381                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33382                 }
33383
33384                 if(this.labelmd > 0){
33385                     labelCls = ' col-md-' + this.labelmd;
33386                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33387                 }
33388
33389                 if(this.labelsm > 0){
33390                     labelCls = ' col-sm-' + this.labelsm;
33391                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33392                 }
33393
33394                 if(this.labelxs > 0){
33395                     labelCls = ' col-xs-' + this.labelxs;
33396                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33397                 }
33398             }
33399             
33400             label.cls += ' ' + labelCls;
33401             
33402             cfg.cn.push(label);
33403         }
33404         
33405         Roo.each(['day', 'month', 'year'], function(t){
33406             cfg.cn.push({
33407                 tag : 'div',
33408                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33409             });
33410         }, this);
33411         
33412         return cfg;
33413     },
33414     
33415     inputEl: function ()
33416     {
33417         return this.el.select('.roo-date-split-field-group-value', true).first();
33418     },
33419     
33420     onRender : function(ct, position) 
33421     {
33422         var _this = this;
33423         
33424         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33425         
33426         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33427         
33428         this.dayField = new Roo.bootstrap.ComboBox({
33429             allowBlank : this.dayAllowBlank,
33430             alwaysQuery : true,
33431             displayField : 'value',
33432             editable : false,
33433             fieldLabel : '',
33434             forceSelection : true,
33435             mode : 'local',
33436             placeholder : this.dayPlaceholder,
33437             selectOnFocus : true,
33438             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33439             triggerAction : 'all',
33440             typeAhead : true,
33441             valueField : 'value',
33442             store : new Roo.data.SimpleStore({
33443                 data : (function() {    
33444                     var days = [];
33445                     _this.fireEvent('days', _this, days);
33446                     return days;
33447                 })(),
33448                 fields : [ 'value' ]
33449             }),
33450             listeners : {
33451                 select : function (_self, record, index)
33452                 {
33453                     _this.setValue(_this.getValue());
33454                 }
33455             }
33456         });
33457
33458         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33459         
33460         this.monthField = new Roo.bootstrap.MonthField({
33461             after : '<i class=\"fa fa-calendar\"></i>',
33462             allowBlank : this.monthAllowBlank,
33463             placeholder : this.monthPlaceholder,
33464             readOnly : true,
33465             listeners : {
33466                 render : function (_self)
33467                 {
33468                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33469                         e.preventDefault();
33470                         _self.focus();
33471                     });
33472                 },
33473                 select : function (_self, oldvalue, newvalue)
33474                 {
33475                     _this.setValue(_this.getValue());
33476                 }
33477             }
33478         });
33479         
33480         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33481         
33482         this.yearField = new Roo.bootstrap.ComboBox({
33483             allowBlank : this.yearAllowBlank,
33484             alwaysQuery : true,
33485             displayField : 'value',
33486             editable : false,
33487             fieldLabel : '',
33488             forceSelection : true,
33489             mode : 'local',
33490             placeholder : this.yearPlaceholder,
33491             selectOnFocus : true,
33492             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33493             triggerAction : 'all',
33494             typeAhead : true,
33495             valueField : 'value',
33496             store : new Roo.data.SimpleStore({
33497                 data : (function() {
33498                     var years = [];
33499                     _this.fireEvent('years', _this, years);
33500                     return years;
33501                 })(),
33502                 fields : [ 'value' ]
33503             }),
33504             listeners : {
33505                 select : function (_self, record, index)
33506                 {
33507                     _this.setValue(_this.getValue());
33508                 }
33509             }
33510         });
33511
33512         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33513     },
33514     
33515     setValue : function(v, format)
33516     {
33517         this.inputEl.dom.value = v;
33518         
33519         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33520         
33521         var d = Date.parseDate(v, f);
33522         
33523         if(!d){
33524             this.validate();
33525             return;
33526         }
33527         
33528         this.setDay(d.format(this.dayFormat));
33529         this.setMonth(d.format(this.monthFormat));
33530         this.setYear(d.format(this.yearFormat));
33531         
33532         this.validate();
33533         
33534         return;
33535     },
33536     
33537     setDay : function(v)
33538     {
33539         this.dayField.setValue(v);
33540         this.inputEl.dom.value = this.getValue();
33541         this.validate();
33542         return;
33543     },
33544     
33545     setMonth : function(v)
33546     {
33547         this.monthField.setValue(v, true);
33548         this.inputEl.dom.value = this.getValue();
33549         this.validate();
33550         return;
33551     },
33552     
33553     setYear : function(v)
33554     {
33555         this.yearField.setValue(v);
33556         this.inputEl.dom.value = this.getValue();
33557         this.validate();
33558         return;
33559     },
33560     
33561     getDay : function()
33562     {
33563         return this.dayField.getValue();
33564     },
33565     
33566     getMonth : function()
33567     {
33568         return this.monthField.getValue();
33569     },
33570     
33571     getYear : function()
33572     {
33573         return this.yearField.getValue();
33574     },
33575     
33576     getValue : function()
33577     {
33578         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33579         
33580         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33581         
33582         return date;
33583     },
33584     
33585     reset : function()
33586     {
33587         this.setDay('');
33588         this.setMonth('');
33589         this.setYear('');
33590         this.inputEl.dom.value = '';
33591         this.validate();
33592         return;
33593     },
33594     
33595     validate : function()
33596     {
33597         var d = this.dayField.validate();
33598         var m = this.monthField.validate();
33599         var y = this.yearField.validate();
33600         
33601         var valid = true;
33602         
33603         if(
33604                 (!this.dayAllowBlank && !d) ||
33605                 (!this.monthAllowBlank && !m) ||
33606                 (!this.yearAllowBlank && !y)
33607         ){
33608             valid = false;
33609         }
33610         
33611         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33612             return valid;
33613         }
33614         
33615         if(valid){
33616             this.markValid();
33617             return valid;
33618         }
33619         
33620         this.markInvalid();
33621         
33622         return valid;
33623     },
33624     
33625     markValid : function()
33626     {
33627         
33628         var label = this.el.select('label', true).first();
33629         var icon = this.el.select('i.fa-star', true).first();
33630
33631         if(label && icon){
33632             icon.remove();
33633         }
33634         
33635         this.fireEvent('valid', this);
33636     },
33637     
33638      /**
33639      * Mark this field as invalid
33640      * @param {String} msg The validation message
33641      */
33642     markInvalid : function(msg)
33643     {
33644         
33645         var label = this.el.select('label', true).first();
33646         var icon = this.el.select('i.fa-star', true).first();
33647
33648         if(label && !icon){
33649             this.el.select('.roo-date-split-field-label', true).createChild({
33650                 tag : 'i',
33651                 cls : 'text-danger fa fa-lg fa-star',
33652                 tooltip : 'This field is required',
33653                 style : 'margin-right:5px;'
33654             }, label, true);
33655         }
33656         
33657         this.fireEvent('invalid', this, msg);
33658     },
33659     
33660     clearInvalid : function()
33661     {
33662         var label = this.el.select('label', true).first();
33663         var icon = this.el.select('i.fa-star', true).first();
33664
33665         if(label && icon){
33666             icon.remove();
33667         }
33668         
33669         this.fireEvent('valid', this);
33670     },
33671     
33672     getName: function()
33673     {
33674         return this.name;
33675     }
33676     
33677 });
33678
33679  /**
33680  *
33681  * This is based on 
33682  * http://masonry.desandro.com
33683  *
33684  * The idea is to render all the bricks based on vertical width...
33685  *
33686  * The original code extends 'outlayer' - we might need to use that....
33687  * 
33688  */
33689
33690
33691 /**
33692  * @class Roo.bootstrap.LayoutMasonry
33693  * @extends Roo.bootstrap.Component
33694  * Bootstrap Layout Masonry class
33695  * 
33696  * @constructor
33697  * Create a new Element
33698  * @param {Object} config The config object
33699  */
33700
33701 Roo.bootstrap.LayoutMasonry = function(config){
33702     
33703     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33704     
33705     this.bricks = [];
33706     
33707     Roo.bootstrap.LayoutMasonry.register(this);
33708     
33709     this.addEvents({
33710         // raw events
33711         /**
33712          * @event layout
33713          * Fire after layout the items
33714          * @param {Roo.bootstrap.LayoutMasonry} this
33715          * @param {Roo.EventObject} e
33716          */
33717         "layout" : true
33718     });
33719     
33720 };
33721
33722 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33723     
33724     /**
33725      * @cfg {Boolean} isLayoutInstant = no animation?
33726      */   
33727     isLayoutInstant : false, // needed?
33728    
33729     /**
33730      * @cfg {Number} boxWidth  width of the columns
33731      */   
33732     boxWidth : 450,
33733     
33734       /**
33735      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33736      */   
33737     boxHeight : 0,
33738     
33739     /**
33740      * @cfg {Number} padWidth padding below box..
33741      */   
33742     padWidth : 10, 
33743     
33744     /**
33745      * @cfg {Number} gutter gutter width..
33746      */   
33747     gutter : 10,
33748     
33749      /**
33750      * @cfg {Number} maxCols maximum number of columns
33751      */   
33752     
33753     maxCols: 0,
33754     
33755     /**
33756      * @cfg {Boolean} isAutoInitial defalut true
33757      */   
33758     isAutoInitial : true, 
33759     
33760     containerWidth: 0,
33761     
33762     /**
33763      * @cfg {Boolean} isHorizontal defalut false
33764      */   
33765     isHorizontal : false, 
33766
33767     currentSize : null,
33768     
33769     tag: 'div',
33770     
33771     cls: '',
33772     
33773     bricks: null, //CompositeElement
33774     
33775     cols : 1,
33776     
33777     _isLayoutInited : false,
33778     
33779 //    isAlternative : false, // only use for vertical layout...
33780     
33781     /**
33782      * @cfg {Number} alternativePadWidth padding below box..
33783      */   
33784     alternativePadWidth : 50,
33785     
33786     selectedBrick : [],
33787     
33788     getAutoCreate : function(){
33789         
33790         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33791         
33792         var cfg = {
33793             tag: this.tag,
33794             cls: 'blog-masonary-wrapper ' + this.cls,
33795             cn : {
33796                 cls : 'mas-boxes masonary'
33797             }
33798         };
33799         
33800         return cfg;
33801     },
33802     
33803     getChildContainer: function( )
33804     {
33805         if (this.boxesEl) {
33806             return this.boxesEl;
33807         }
33808         
33809         this.boxesEl = this.el.select('.mas-boxes').first();
33810         
33811         return this.boxesEl;
33812     },
33813     
33814     
33815     initEvents : function()
33816     {
33817         var _this = this;
33818         
33819         if(this.isAutoInitial){
33820             Roo.log('hook children rendered');
33821             this.on('childrenrendered', function() {
33822                 Roo.log('children rendered');
33823                 _this.initial();
33824             } ,this);
33825         }
33826     },
33827     
33828     initial : function()
33829     {
33830         this.selectedBrick = [];
33831         
33832         this.currentSize = this.el.getBox(true);
33833         
33834         Roo.EventManager.onWindowResize(this.resize, this); 
33835
33836         if(!this.isAutoInitial){
33837             this.layout();
33838             return;
33839         }
33840         
33841         this.layout();
33842         
33843         return;
33844         //this.layout.defer(500,this);
33845         
33846     },
33847     
33848     resize : function()
33849     {
33850         var cs = this.el.getBox(true);
33851         
33852         if (
33853                 this.currentSize.width == cs.width && 
33854                 this.currentSize.x == cs.x && 
33855                 this.currentSize.height == cs.height && 
33856                 this.currentSize.y == cs.y 
33857         ) {
33858             Roo.log("no change in with or X or Y");
33859             return;
33860         }
33861         
33862         this.currentSize = cs;
33863         
33864         this.layout();
33865         
33866     },
33867     
33868     layout : function()
33869     {   
33870         this._resetLayout();
33871         
33872         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33873         
33874         this.layoutItems( isInstant );
33875       
33876         this._isLayoutInited = true;
33877         
33878         this.fireEvent('layout', this);
33879         
33880     },
33881     
33882     _resetLayout : function()
33883     {
33884         if(this.isHorizontal){
33885             this.horizontalMeasureColumns();
33886             return;
33887         }
33888         
33889         this.verticalMeasureColumns();
33890         
33891     },
33892     
33893     verticalMeasureColumns : function()
33894     {
33895         this.getContainerWidth();
33896         
33897 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33898 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33899 //            return;
33900 //        }
33901         
33902         var boxWidth = this.boxWidth + this.padWidth;
33903         
33904         if(this.containerWidth < this.boxWidth){
33905             boxWidth = this.containerWidth
33906         }
33907         
33908         var containerWidth = this.containerWidth;
33909         
33910         var cols = Math.floor(containerWidth / boxWidth);
33911         
33912         this.cols = Math.max( cols, 1 );
33913         
33914         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33915         
33916         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33917         
33918         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33919         
33920         this.colWidth = boxWidth + avail - this.padWidth;
33921         
33922         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33923         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33924     },
33925     
33926     horizontalMeasureColumns : function()
33927     {
33928         this.getContainerWidth();
33929         
33930         var boxWidth = this.boxWidth;
33931         
33932         if(this.containerWidth < boxWidth){
33933             boxWidth = this.containerWidth;
33934         }
33935         
33936         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33937         
33938         this.el.setHeight(boxWidth);
33939         
33940     },
33941     
33942     getContainerWidth : function()
33943     {
33944         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33945     },
33946     
33947     layoutItems : function( isInstant )
33948     {
33949         Roo.log(this.bricks);
33950         
33951         var items = Roo.apply([], this.bricks);
33952         
33953         if(this.isHorizontal){
33954             this._horizontalLayoutItems( items , isInstant );
33955             return;
33956         }
33957         
33958 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33959 //            this._verticalAlternativeLayoutItems( items , isInstant );
33960 //            return;
33961 //        }
33962         
33963         this._verticalLayoutItems( items , isInstant );
33964         
33965     },
33966     
33967     _verticalLayoutItems : function ( items , isInstant)
33968     {
33969         if ( !items || !items.length ) {
33970             return;
33971         }
33972         
33973         var standard = [
33974             ['xs', 'xs', 'xs', 'tall'],
33975             ['xs', 'xs', 'tall'],
33976             ['xs', 'xs', 'sm'],
33977             ['xs', 'xs', 'xs'],
33978             ['xs', 'tall'],
33979             ['xs', 'sm'],
33980             ['xs', 'xs'],
33981             ['xs'],
33982             
33983             ['sm', 'xs', 'xs'],
33984             ['sm', 'xs'],
33985             ['sm'],
33986             
33987             ['tall', 'xs', 'xs', 'xs'],
33988             ['tall', 'xs', 'xs'],
33989             ['tall', 'xs'],
33990             ['tall']
33991             
33992         ];
33993         
33994         var queue = [];
33995         
33996         var boxes = [];
33997         
33998         var box = [];
33999         
34000         Roo.each(items, function(item, k){
34001             
34002             switch (item.size) {
34003                 // these layouts take up a full box,
34004                 case 'md' :
34005                 case 'md-left' :
34006                 case 'md-right' :
34007                 case 'wide' :
34008                     
34009                     if(box.length){
34010                         boxes.push(box);
34011                         box = [];
34012                     }
34013                     
34014                     boxes.push([item]);
34015                     
34016                     break;
34017                     
34018                 case 'xs' :
34019                 case 'sm' :
34020                 case 'tall' :
34021                     
34022                     box.push(item);
34023                     
34024                     break;
34025                 default :
34026                     break;
34027                     
34028             }
34029             
34030         }, this);
34031         
34032         if(box.length){
34033             boxes.push(box);
34034             box = [];
34035         }
34036         
34037         var filterPattern = function(box, length)
34038         {
34039             if(!box.length){
34040                 return;
34041             }
34042             
34043             var match = false;
34044             
34045             var pattern = box.slice(0, length);
34046             
34047             var format = [];
34048             
34049             Roo.each(pattern, function(i){
34050                 format.push(i.size);
34051             }, this);
34052             
34053             Roo.each(standard, function(s){
34054                 
34055                 if(String(s) != String(format)){
34056                     return;
34057                 }
34058                 
34059                 match = true;
34060                 return false;
34061                 
34062             }, this);
34063             
34064             if(!match && length == 1){
34065                 return;
34066             }
34067             
34068             if(!match){
34069                 filterPattern(box, length - 1);
34070                 return;
34071             }
34072                 
34073             queue.push(pattern);
34074
34075             box = box.slice(length, box.length);
34076
34077             filterPattern(box, 4);
34078
34079             return;
34080             
34081         }
34082         
34083         Roo.each(boxes, function(box, k){
34084             
34085             if(!box.length){
34086                 return;
34087             }
34088             
34089             if(box.length == 1){
34090                 queue.push(box);
34091                 return;
34092             }
34093             
34094             filterPattern(box, 4);
34095             
34096         }, this);
34097         
34098         this._processVerticalLayoutQueue( queue, isInstant );
34099         
34100     },
34101     
34102 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34103 //    {
34104 //        if ( !items || !items.length ) {
34105 //            return;
34106 //        }
34107 //
34108 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34109 //        
34110 //    },
34111     
34112     _horizontalLayoutItems : function ( items , isInstant)
34113     {
34114         if ( !items || !items.length || items.length < 3) {
34115             return;
34116         }
34117         
34118         items.reverse();
34119         
34120         var eItems = items.slice(0, 3);
34121         
34122         items = items.slice(3, items.length);
34123         
34124         var standard = [
34125             ['xs', 'xs', 'xs', 'wide'],
34126             ['xs', 'xs', 'wide'],
34127             ['xs', 'xs', 'sm'],
34128             ['xs', 'xs', 'xs'],
34129             ['xs', 'wide'],
34130             ['xs', 'sm'],
34131             ['xs', 'xs'],
34132             ['xs'],
34133             
34134             ['sm', 'xs', 'xs'],
34135             ['sm', 'xs'],
34136             ['sm'],
34137             
34138             ['wide', 'xs', 'xs', 'xs'],
34139             ['wide', 'xs', 'xs'],
34140             ['wide', 'xs'],
34141             ['wide'],
34142             
34143             ['wide-thin']
34144         ];
34145         
34146         var queue = [];
34147         
34148         var boxes = [];
34149         
34150         var box = [];
34151         
34152         Roo.each(items, function(item, k){
34153             
34154             switch (item.size) {
34155                 case 'md' :
34156                 case 'md-left' :
34157                 case 'md-right' :
34158                 case 'tall' :
34159                     
34160                     if(box.length){
34161                         boxes.push(box);
34162                         box = [];
34163                     }
34164                     
34165                     boxes.push([item]);
34166                     
34167                     break;
34168                     
34169                 case 'xs' :
34170                 case 'sm' :
34171                 case 'wide' :
34172                 case 'wide-thin' :
34173                     
34174                     box.push(item);
34175                     
34176                     break;
34177                 default :
34178                     break;
34179                     
34180             }
34181             
34182         }, this);
34183         
34184         if(box.length){
34185             boxes.push(box);
34186             box = [];
34187         }
34188         
34189         var filterPattern = function(box, length)
34190         {
34191             if(!box.length){
34192                 return;
34193             }
34194             
34195             var match = false;
34196             
34197             var pattern = box.slice(0, length);
34198             
34199             var format = [];
34200             
34201             Roo.each(pattern, function(i){
34202                 format.push(i.size);
34203             }, this);
34204             
34205             Roo.each(standard, function(s){
34206                 
34207                 if(String(s) != String(format)){
34208                     return;
34209                 }
34210                 
34211                 match = true;
34212                 return false;
34213                 
34214             }, this);
34215             
34216             if(!match && length == 1){
34217                 return;
34218             }
34219             
34220             if(!match){
34221                 filterPattern(box, length - 1);
34222                 return;
34223             }
34224                 
34225             queue.push(pattern);
34226
34227             box = box.slice(length, box.length);
34228
34229             filterPattern(box, 4);
34230
34231             return;
34232             
34233         }
34234         
34235         Roo.each(boxes, function(box, k){
34236             
34237             if(!box.length){
34238                 return;
34239             }
34240             
34241             if(box.length == 1){
34242                 queue.push(box);
34243                 return;
34244             }
34245             
34246             filterPattern(box, 4);
34247             
34248         }, this);
34249         
34250         
34251         var prune = [];
34252         
34253         var pos = this.el.getBox(true);
34254         
34255         var minX = pos.x;
34256         
34257         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34258         
34259         var hit_end = false;
34260         
34261         Roo.each(queue, function(box){
34262             
34263             if(hit_end){
34264                 
34265                 Roo.each(box, function(b){
34266                 
34267                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34268                     b.el.hide();
34269
34270                 }, this);
34271
34272                 return;
34273             }
34274             
34275             var mx = 0;
34276             
34277             Roo.each(box, function(b){
34278                 
34279                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34280                 b.el.show();
34281
34282                 mx = Math.max(mx, b.x);
34283                 
34284             }, this);
34285             
34286             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34287             
34288             if(maxX < minX){
34289                 
34290                 Roo.each(box, function(b){
34291                 
34292                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34293                     b.el.hide();
34294                     
34295                 }, this);
34296                 
34297                 hit_end = true;
34298                 
34299                 return;
34300             }
34301             
34302             prune.push(box);
34303             
34304         }, this);
34305         
34306         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34307     },
34308     
34309     /** Sets position of item in DOM
34310     * @param {Element} item
34311     * @param {Number} x - horizontal position
34312     * @param {Number} y - vertical position
34313     * @param {Boolean} isInstant - disables transitions
34314     */
34315     _processVerticalLayoutQueue : function( queue, isInstant )
34316     {
34317         var pos = this.el.getBox(true);
34318         var x = pos.x;
34319         var y = pos.y;
34320         var maxY = [];
34321         
34322         for (var i = 0; i < this.cols; i++){
34323             maxY[i] = pos.y;
34324         }
34325         
34326         Roo.each(queue, function(box, k){
34327             
34328             var col = k % this.cols;
34329             
34330             Roo.each(box, function(b,kk){
34331                 
34332                 b.el.position('absolute');
34333                 
34334                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34335                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34336                 
34337                 if(b.size == 'md-left' || b.size == 'md-right'){
34338                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34339                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34340                 }
34341                 
34342                 b.el.setWidth(width);
34343                 b.el.setHeight(height);
34344                 // iframe?
34345                 b.el.select('iframe',true).setSize(width,height);
34346                 
34347             }, this);
34348             
34349             for (var i = 0; i < this.cols; i++){
34350                 
34351                 if(maxY[i] < maxY[col]){
34352                     col = i;
34353                     continue;
34354                 }
34355                 
34356                 col = Math.min(col, i);
34357                 
34358             }
34359             
34360             x = pos.x + col * (this.colWidth + this.padWidth);
34361             
34362             y = maxY[col];
34363             
34364             var positions = [];
34365             
34366             switch (box.length){
34367                 case 1 :
34368                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34369                     break;
34370                 case 2 :
34371                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34372                     break;
34373                 case 3 :
34374                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34375                     break;
34376                 case 4 :
34377                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34378                     break;
34379                 default :
34380                     break;
34381             }
34382             
34383             Roo.each(box, function(b,kk){
34384                 
34385                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34386                 
34387                 var sz = b.el.getSize();
34388                 
34389                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34390                 
34391             }, this);
34392             
34393         }, this);
34394         
34395         var mY = 0;
34396         
34397         for (var i = 0; i < this.cols; i++){
34398             mY = Math.max(mY, maxY[i]);
34399         }
34400         
34401         this.el.setHeight(mY - pos.y);
34402         
34403     },
34404     
34405 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34406 //    {
34407 //        var pos = this.el.getBox(true);
34408 //        var x = pos.x;
34409 //        var y = pos.y;
34410 //        var maxX = pos.right;
34411 //        
34412 //        var maxHeight = 0;
34413 //        
34414 //        Roo.each(items, function(item, k){
34415 //            
34416 //            var c = k % 2;
34417 //            
34418 //            item.el.position('absolute');
34419 //                
34420 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34421 //
34422 //            item.el.setWidth(width);
34423 //
34424 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34425 //
34426 //            item.el.setHeight(height);
34427 //            
34428 //            if(c == 0){
34429 //                item.el.setXY([x, y], isInstant ? false : true);
34430 //            } else {
34431 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34432 //            }
34433 //            
34434 //            y = y + height + this.alternativePadWidth;
34435 //            
34436 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34437 //            
34438 //        }, this);
34439 //        
34440 //        this.el.setHeight(maxHeight);
34441 //        
34442 //    },
34443     
34444     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34445     {
34446         var pos = this.el.getBox(true);
34447         
34448         var minX = pos.x;
34449         var minY = pos.y;
34450         
34451         var maxX = pos.right;
34452         
34453         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34454         
34455         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34456         
34457         Roo.each(queue, function(box, k){
34458             
34459             Roo.each(box, function(b, kk){
34460                 
34461                 b.el.position('absolute');
34462                 
34463                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34464                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34465                 
34466                 if(b.size == 'md-left' || b.size == 'md-right'){
34467                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34468                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34469                 }
34470                 
34471                 b.el.setWidth(width);
34472                 b.el.setHeight(height);
34473                 
34474             }, this);
34475             
34476             if(!box.length){
34477                 return;
34478             }
34479             
34480             var positions = [];
34481             
34482             switch (box.length){
34483                 case 1 :
34484                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34485                     break;
34486                 case 2 :
34487                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34488                     break;
34489                 case 3 :
34490                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34491                     break;
34492                 case 4 :
34493                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34494                     break;
34495                 default :
34496                     break;
34497             }
34498             
34499             Roo.each(box, function(b,kk){
34500                 
34501                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34502                 
34503                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34504                 
34505             }, this);
34506             
34507         }, this);
34508         
34509     },
34510     
34511     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34512     {
34513         Roo.each(eItems, function(b,k){
34514             
34515             b.size = (k == 0) ? 'sm' : 'xs';
34516             b.x = (k == 0) ? 2 : 1;
34517             b.y = (k == 0) ? 2 : 1;
34518             
34519             b.el.position('absolute');
34520             
34521             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34522                 
34523             b.el.setWidth(width);
34524             
34525             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34526             
34527             b.el.setHeight(height);
34528             
34529         }, this);
34530
34531         var positions = [];
34532         
34533         positions.push({
34534             x : maxX - this.unitWidth * 2 - this.gutter,
34535             y : minY
34536         });
34537         
34538         positions.push({
34539             x : maxX - this.unitWidth,
34540             y : minY + (this.unitWidth + this.gutter) * 2
34541         });
34542         
34543         positions.push({
34544             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34545             y : minY
34546         });
34547         
34548         Roo.each(eItems, function(b,k){
34549             
34550             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34551
34552         }, this);
34553         
34554     },
34555     
34556     getVerticalOneBoxColPositions : function(x, y, box)
34557     {
34558         var pos = [];
34559         
34560         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34561         
34562         if(box[0].size == 'md-left'){
34563             rand = 0;
34564         }
34565         
34566         if(box[0].size == 'md-right'){
34567             rand = 1;
34568         }
34569         
34570         pos.push({
34571             x : x + (this.unitWidth + this.gutter) * rand,
34572             y : y
34573         });
34574         
34575         return pos;
34576     },
34577     
34578     getVerticalTwoBoxColPositions : function(x, y, box)
34579     {
34580         var pos = [];
34581         
34582         if(box[0].size == 'xs'){
34583             
34584             pos.push({
34585                 x : x,
34586                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34587             });
34588
34589             pos.push({
34590                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34591                 y : y
34592             });
34593             
34594             return pos;
34595             
34596         }
34597         
34598         pos.push({
34599             x : x,
34600             y : y
34601         });
34602
34603         pos.push({
34604             x : x + (this.unitWidth + this.gutter) * 2,
34605             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34606         });
34607         
34608         return pos;
34609         
34610     },
34611     
34612     getVerticalThreeBoxColPositions : function(x, y, box)
34613     {
34614         var pos = [];
34615         
34616         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34617             
34618             pos.push({
34619                 x : x,
34620                 y : y
34621             });
34622
34623             pos.push({
34624                 x : x + (this.unitWidth + this.gutter) * 1,
34625                 y : y
34626             });
34627             
34628             pos.push({
34629                 x : x + (this.unitWidth + this.gutter) * 2,
34630                 y : y
34631             });
34632             
34633             return pos;
34634             
34635         }
34636         
34637         if(box[0].size == 'xs' && box[1].size == 'xs'){
34638             
34639             pos.push({
34640                 x : x,
34641                 y : y
34642             });
34643
34644             pos.push({
34645                 x : x,
34646                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34647             });
34648             
34649             pos.push({
34650                 x : x + (this.unitWidth + this.gutter) * 1,
34651                 y : y
34652             });
34653             
34654             return pos;
34655             
34656         }
34657         
34658         pos.push({
34659             x : x,
34660             y : y
34661         });
34662
34663         pos.push({
34664             x : x + (this.unitWidth + this.gutter) * 2,
34665             y : y
34666         });
34667
34668         pos.push({
34669             x : x + (this.unitWidth + this.gutter) * 2,
34670             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34671         });
34672             
34673         return pos;
34674         
34675     },
34676     
34677     getVerticalFourBoxColPositions : function(x, y, box)
34678     {
34679         var pos = [];
34680         
34681         if(box[0].size == 'xs'){
34682             
34683             pos.push({
34684                 x : x,
34685                 y : y
34686             });
34687
34688             pos.push({
34689                 x : x,
34690                 y : y + (this.unitHeight + this.gutter) * 1
34691             });
34692             
34693             pos.push({
34694                 x : x,
34695                 y : y + (this.unitHeight + this.gutter) * 2
34696             });
34697             
34698             pos.push({
34699                 x : x + (this.unitWidth + this.gutter) * 1,
34700                 y : y
34701             });
34702             
34703             return pos;
34704             
34705         }
34706         
34707         pos.push({
34708             x : x,
34709             y : y
34710         });
34711
34712         pos.push({
34713             x : x + (this.unitWidth + this.gutter) * 2,
34714             y : y
34715         });
34716
34717         pos.push({
34718             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34719             y : y + (this.unitHeight + this.gutter) * 1
34720         });
34721
34722         pos.push({
34723             x : x + (this.unitWidth + this.gutter) * 2,
34724             y : y + (this.unitWidth + this.gutter) * 2
34725         });
34726
34727         return pos;
34728         
34729     },
34730     
34731     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34732     {
34733         var pos = [];
34734         
34735         if(box[0].size == 'md-left'){
34736             pos.push({
34737                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34738                 y : minY
34739             });
34740             
34741             return pos;
34742         }
34743         
34744         if(box[0].size == 'md-right'){
34745             pos.push({
34746                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34747                 y : minY + (this.unitWidth + this.gutter) * 1
34748             });
34749             
34750             return pos;
34751         }
34752         
34753         var rand = Math.floor(Math.random() * (4 - box[0].y));
34754         
34755         pos.push({
34756             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34757             y : minY + (this.unitWidth + this.gutter) * rand
34758         });
34759         
34760         return pos;
34761         
34762     },
34763     
34764     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34765     {
34766         var pos = [];
34767         
34768         if(box[0].size == 'xs'){
34769             
34770             pos.push({
34771                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34772                 y : minY
34773             });
34774
34775             pos.push({
34776                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34777                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34778             });
34779             
34780             return pos;
34781             
34782         }
34783         
34784         pos.push({
34785             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34786             y : minY
34787         });
34788
34789         pos.push({
34790             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34791             y : minY + (this.unitWidth + this.gutter) * 2
34792         });
34793         
34794         return pos;
34795         
34796     },
34797     
34798     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34799     {
34800         var pos = [];
34801         
34802         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34803             
34804             pos.push({
34805                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34806                 y : minY
34807             });
34808
34809             pos.push({
34810                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34811                 y : minY + (this.unitWidth + this.gutter) * 1
34812             });
34813             
34814             pos.push({
34815                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34816                 y : minY + (this.unitWidth + this.gutter) * 2
34817             });
34818             
34819             return pos;
34820             
34821         }
34822         
34823         if(box[0].size == 'xs' && box[1].size == 'xs'){
34824             
34825             pos.push({
34826                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34827                 y : minY
34828             });
34829
34830             pos.push({
34831                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34832                 y : minY
34833             });
34834             
34835             pos.push({
34836                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34837                 y : minY + (this.unitWidth + this.gutter) * 1
34838             });
34839             
34840             return pos;
34841             
34842         }
34843         
34844         pos.push({
34845             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34846             y : minY
34847         });
34848
34849         pos.push({
34850             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34851             y : minY + (this.unitWidth + this.gutter) * 2
34852         });
34853
34854         pos.push({
34855             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34856             y : minY + (this.unitWidth + this.gutter) * 2
34857         });
34858             
34859         return pos;
34860         
34861     },
34862     
34863     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34864     {
34865         var pos = [];
34866         
34867         if(box[0].size == 'xs'){
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[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34876                 y : minY
34877             });
34878             
34879             pos.push({
34880                 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),
34881                 y : minY
34882             });
34883             
34884             pos.push({
34885                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34886                 y : minY + (this.unitWidth + this.gutter) * 1
34887             });
34888             
34889             return pos;
34890             
34891         }
34892         
34893         pos.push({
34894             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34895             y : minY
34896         });
34897         
34898         pos.push({
34899             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34900             y : minY + (this.unitWidth + this.gutter) * 2
34901         });
34902         
34903         pos.push({
34904             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34905             y : minY + (this.unitWidth + this.gutter) * 2
34906         });
34907         
34908         pos.push({
34909             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),
34910             y : minY + (this.unitWidth + this.gutter) * 2
34911         });
34912
34913         return pos;
34914         
34915     },
34916     
34917     /**
34918     * remove a Masonry Brick
34919     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34920     */
34921     removeBrick : function(brick_id)
34922     {
34923         if (!brick_id) {
34924             return;
34925         }
34926         
34927         for (var i = 0; i<this.bricks.length; i++) {
34928             if (this.bricks[i].id == brick_id) {
34929                 this.bricks.splice(i,1);
34930                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34931                 this.initial();
34932             }
34933         }
34934     },
34935     
34936     /**
34937     * adds a Masonry Brick
34938     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34939     */
34940     addBrick : function(cfg)
34941     {
34942         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34943         //this.register(cn);
34944         cn.parentId = this.id;
34945         cn.render(this.el);
34946         return cn;
34947     },
34948     
34949     /**
34950     * register a Masonry Brick
34951     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34952     */
34953     
34954     register : function(brick)
34955     {
34956         this.bricks.push(brick);
34957         brick.masonryId = this.id;
34958     },
34959     
34960     /**
34961     * clear all the Masonry Brick
34962     */
34963     clearAll : function()
34964     {
34965         this.bricks = [];
34966         //this.getChildContainer().dom.innerHTML = "";
34967         this.el.dom.innerHTML = '';
34968     },
34969     
34970     getSelected : function()
34971     {
34972         if (!this.selectedBrick) {
34973             return false;
34974         }
34975         
34976         return this.selectedBrick;
34977     }
34978 });
34979
34980 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34981     
34982     groups: {},
34983      /**
34984     * register a Masonry Layout
34985     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34986     */
34987     
34988     register : function(layout)
34989     {
34990         this.groups[layout.id] = layout;
34991     },
34992     /**
34993     * fetch a  Masonry Layout based on the masonry layout ID
34994     * @param {string} the masonry layout to add
34995     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34996     */
34997     
34998     get: function(layout_id) {
34999         if (typeof(this.groups[layout_id]) == 'undefined') {
35000             return false;
35001         }
35002         return this.groups[layout_id] ;
35003     }
35004     
35005     
35006     
35007 });
35008
35009  
35010
35011  /**
35012  *
35013  * This is based on 
35014  * http://masonry.desandro.com
35015  *
35016  * The idea is to render all the bricks based on vertical width...
35017  *
35018  * The original code extends 'outlayer' - we might need to use that....
35019  * 
35020  */
35021
35022
35023 /**
35024  * @class Roo.bootstrap.LayoutMasonryAuto
35025  * @extends Roo.bootstrap.Component
35026  * Bootstrap Layout Masonry class
35027  * 
35028  * @constructor
35029  * Create a new Element
35030  * @param {Object} config The config object
35031  */
35032
35033 Roo.bootstrap.LayoutMasonryAuto = function(config){
35034     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35035 };
35036
35037 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35038     
35039       /**
35040      * @cfg {Boolean} isFitWidth  - resize the width..
35041      */   
35042     isFitWidth : false,  // options..
35043     /**
35044      * @cfg {Boolean} isOriginLeft = left align?
35045      */   
35046     isOriginLeft : true,
35047     /**
35048      * @cfg {Boolean} isOriginTop = top align?
35049      */   
35050     isOriginTop : false,
35051     /**
35052      * @cfg {Boolean} isLayoutInstant = no animation?
35053      */   
35054     isLayoutInstant : false, // needed?
35055     /**
35056      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35057      */   
35058     isResizingContainer : true,
35059     /**
35060      * @cfg {Number} columnWidth  width of the columns 
35061      */   
35062     
35063     columnWidth : 0,
35064     
35065     /**
35066      * @cfg {Number} maxCols maximum number of columns
35067      */   
35068     
35069     maxCols: 0,
35070     /**
35071      * @cfg {Number} padHeight padding below box..
35072      */   
35073     
35074     padHeight : 10, 
35075     
35076     /**
35077      * @cfg {Boolean} isAutoInitial defalut true
35078      */   
35079     
35080     isAutoInitial : true, 
35081     
35082     // private?
35083     gutter : 0,
35084     
35085     containerWidth: 0,
35086     initialColumnWidth : 0,
35087     currentSize : null,
35088     
35089     colYs : null, // array.
35090     maxY : 0,
35091     padWidth: 10,
35092     
35093     
35094     tag: 'div',
35095     cls: '',
35096     bricks: null, //CompositeElement
35097     cols : 0, // array?
35098     // element : null, // wrapped now this.el
35099     _isLayoutInited : null, 
35100     
35101     
35102     getAutoCreate : function(){
35103         
35104         var cfg = {
35105             tag: this.tag,
35106             cls: 'blog-masonary-wrapper ' + this.cls,
35107             cn : {
35108                 cls : 'mas-boxes masonary'
35109             }
35110         };
35111         
35112         return cfg;
35113     },
35114     
35115     getChildContainer: function( )
35116     {
35117         if (this.boxesEl) {
35118             return this.boxesEl;
35119         }
35120         
35121         this.boxesEl = this.el.select('.mas-boxes').first();
35122         
35123         return this.boxesEl;
35124     },
35125     
35126     
35127     initEvents : function()
35128     {
35129         var _this = this;
35130         
35131         if(this.isAutoInitial){
35132             Roo.log('hook children rendered');
35133             this.on('childrenrendered', function() {
35134                 Roo.log('children rendered');
35135                 _this.initial();
35136             } ,this);
35137         }
35138         
35139     },
35140     
35141     initial : function()
35142     {
35143         this.reloadItems();
35144
35145         this.currentSize = this.el.getBox(true);
35146
35147         /// was window resize... - let's see if this works..
35148         Roo.EventManager.onWindowResize(this.resize, this); 
35149
35150         if(!this.isAutoInitial){
35151             this.layout();
35152             return;
35153         }
35154         
35155         this.layout.defer(500,this);
35156     },
35157     
35158     reloadItems: function()
35159     {
35160         this.bricks = this.el.select('.masonry-brick', true);
35161         
35162         this.bricks.each(function(b) {
35163             //Roo.log(b.getSize());
35164             if (!b.attr('originalwidth')) {
35165                 b.attr('originalwidth',  b.getSize().width);
35166             }
35167             
35168         });
35169         
35170         Roo.log(this.bricks.elements.length);
35171     },
35172     
35173     resize : function()
35174     {
35175         Roo.log('resize');
35176         var cs = this.el.getBox(true);
35177         
35178         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35179             Roo.log("no change in with or X");
35180             return;
35181         }
35182         this.currentSize = cs;
35183         this.layout();
35184     },
35185     
35186     layout : function()
35187     {
35188          Roo.log('layout');
35189         this._resetLayout();
35190         //this._manageStamps();
35191       
35192         // don't animate first layout
35193         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35194         this.layoutItems( isInstant );
35195       
35196         // flag for initalized
35197         this._isLayoutInited = true;
35198     },
35199     
35200     layoutItems : function( isInstant )
35201     {
35202         //var items = this._getItemsForLayout( this.items );
35203         // original code supports filtering layout items.. we just ignore it..
35204         
35205         this._layoutItems( this.bricks , isInstant );
35206       
35207         this._postLayout();
35208     },
35209     _layoutItems : function ( items , isInstant)
35210     {
35211        //this.fireEvent( 'layout', this, items );
35212     
35213
35214         if ( !items || !items.elements.length ) {
35215           // no items, emit event with empty array
35216             return;
35217         }
35218
35219         var queue = [];
35220         items.each(function(item) {
35221             Roo.log("layout item");
35222             Roo.log(item);
35223             // get x/y object from method
35224             var position = this._getItemLayoutPosition( item );
35225             // enqueue
35226             position.item = item;
35227             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35228             queue.push( position );
35229         }, this);
35230       
35231         this._processLayoutQueue( queue );
35232     },
35233     /** Sets position of item in DOM
35234     * @param {Element} item
35235     * @param {Number} x - horizontal position
35236     * @param {Number} y - vertical position
35237     * @param {Boolean} isInstant - disables transitions
35238     */
35239     _processLayoutQueue : function( queue )
35240     {
35241         for ( var i=0, len = queue.length; i < len; i++ ) {
35242             var obj = queue[i];
35243             obj.item.position('absolute');
35244             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35245         }
35246     },
35247       
35248     
35249     /**
35250     * Any logic you want to do after each layout,
35251     * i.e. size the container
35252     */
35253     _postLayout : function()
35254     {
35255         this.resizeContainer();
35256     },
35257     
35258     resizeContainer : function()
35259     {
35260         if ( !this.isResizingContainer ) {
35261             return;
35262         }
35263         var size = this._getContainerSize();
35264         if ( size ) {
35265             this.el.setSize(size.width,size.height);
35266             this.boxesEl.setSize(size.width,size.height);
35267         }
35268     },
35269     
35270     
35271     
35272     _resetLayout : function()
35273     {
35274         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35275         this.colWidth = this.el.getWidth();
35276         //this.gutter = this.el.getWidth(); 
35277         
35278         this.measureColumns();
35279
35280         // reset column Y
35281         var i = this.cols;
35282         this.colYs = [];
35283         while (i--) {
35284             this.colYs.push( 0 );
35285         }
35286     
35287         this.maxY = 0;
35288     },
35289
35290     measureColumns : function()
35291     {
35292         this.getContainerWidth();
35293       // if columnWidth is 0, default to outerWidth of first item
35294         if ( !this.columnWidth ) {
35295             var firstItem = this.bricks.first();
35296             Roo.log(firstItem);
35297             this.columnWidth  = this.containerWidth;
35298             if (firstItem && firstItem.attr('originalwidth') ) {
35299                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35300             }
35301             // columnWidth fall back to item of first element
35302             Roo.log("set column width?");
35303                         this.initialColumnWidth = this.columnWidth  ;
35304
35305             // if first elem has no width, default to size of container
35306             
35307         }
35308         
35309         
35310         if (this.initialColumnWidth) {
35311             this.columnWidth = this.initialColumnWidth;
35312         }
35313         
35314         
35315             
35316         // column width is fixed at the top - however if container width get's smaller we should
35317         // reduce it...
35318         
35319         // this bit calcs how man columns..
35320             
35321         var columnWidth = this.columnWidth += this.gutter;
35322       
35323         // calculate columns
35324         var containerWidth = this.containerWidth + this.gutter;
35325         
35326         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35327         // fix rounding errors, typically with gutters
35328         var excess = columnWidth - containerWidth % columnWidth;
35329         
35330         
35331         // if overshoot is less than a pixel, round up, otherwise floor it
35332         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35333         cols = Math[ mathMethod ]( cols );
35334         this.cols = Math.max( cols, 1 );
35335         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35336         
35337          // padding positioning..
35338         var totalColWidth = this.cols * this.columnWidth;
35339         var padavail = this.containerWidth - totalColWidth;
35340         // so for 2 columns - we need 3 'pads'
35341         
35342         var padNeeded = (1+this.cols) * this.padWidth;
35343         
35344         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35345         
35346         this.columnWidth += padExtra
35347         //this.padWidth = Math.floor(padavail /  ( this.cols));
35348         
35349         // adjust colum width so that padding is fixed??
35350         
35351         // we have 3 columns ... total = width * 3
35352         // we have X left over... that should be used by 
35353         
35354         //if (this.expandC) {
35355             
35356         //}
35357         
35358         
35359         
35360     },
35361     
35362     getContainerWidth : function()
35363     {
35364        /* // container is parent if fit width
35365         var container = this.isFitWidth ? this.element.parentNode : this.element;
35366         // check that this.size and size are there
35367         // IE8 triggers resize on body size change, so they might not be
35368         
35369         var size = getSize( container );  //FIXME
35370         this.containerWidth = size && size.innerWidth; //FIXME
35371         */
35372          
35373         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35374         
35375     },
35376     
35377     _getItemLayoutPosition : function( item )  // what is item?
35378     {
35379         // we resize the item to our columnWidth..
35380       
35381         item.setWidth(this.columnWidth);
35382         item.autoBoxAdjust  = false;
35383         
35384         var sz = item.getSize();
35385  
35386         // how many columns does this brick span
35387         var remainder = this.containerWidth % this.columnWidth;
35388         
35389         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35390         // round if off by 1 pixel, otherwise use ceil
35391         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35392         colSpan = Math.min( colSpan, this.cols );
35393         
35394         // normally this should be '1' as we dont' currently allow multi width columns..
35395         
35396         var colGroup = this._getColGroup( colSpan );
35397         // get the minimum Y value from the columns
35398         var minimumY = Math.min.apply( Math, colGroup );
35399         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35400         
35401         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35402          
35403         // position the brick
35404         var position = {
35405             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35406             y: this.currentSize.y + minimumY + this.padHeight
35407         };
35408         
35409         Roo.log(position);
35410         // apply setHeight to necessary columns
35411         var setHeight = minimumY + sz.height + this.padHeight;
35412         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35413         
35414         var setSpan = this.cols + 1 - colGroup.length;
35415         for ( var i = 0; i < setSpan; i++ ) {
35416           this.colYs[ shortColIndex + i ] = setHeight ;
35417         }
35418       
35419         return position;
35420     },
35421     
35422     /**
35423      * @param {Number} colSpan - number of columns the element spans
35424      * @returns {Array} colGroup
35425      */
35426     _getColGroup : function( colSpan )
35427     {
35428         if ( colSpan < 2 ) {
35429           // if brick spans only one column, use all the column Ys
35430           return this.colYs;
35431         }
35432       
35433         var colGroup = [];
35434         // how many different places could this brick fit horizontally
35435         var groupCount = this.cols + 1 - colSpan;
35436         // for each group potential horizontal position
35437         for ( var i = 0; i < groupCount; i++ ) {
35438           // make an array of colY values for that one group
35439           var groupColYs = this.colYs.slice( i, i + colSpan );
35440           // and get the max value of the array
35441           colGroup[i] = Math.max.apply( Math, groupColYs );
35442         }
35443         return colGroup;
35444     },
35445     /*
35446     _manageStamp : function( stamp )
35447     {
35448         var stampSize =  stamp.getSize();
35449         var offset = stamp.getBox();
35450         // get the columns that this stamp affects
35451         var firstX = this.isOriginLeft ? offset.x : offset.right;
35452         var lastX = firstX + stampSize.width;
35453         var firstCol = Math.floor( firstX / this.columnWidth );
35454         firstCol = Math.max( 0, firstCol );
35455         
35456         var lastCol = Math.floor( lastX / this.columnWidth );
35457         // lastCol should not go over if multiple of columnWidth #425
35458         lastCol -= lastX % this.columnWidth ? 0 : 1;
35459         lastCol = Math.min( this.cols - 1, lastCol );
35460         
35461         // set colYs to bottom of the stamp
35462         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35463             stampSize.height;
35464             
35465         for ( var i = firstCol; i <= lastCol; i++ ) {
35466           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35467         }
35468     },
35469     */
35470     
35471     _getContainerSize : function()
35472     {
35473         this.maxY = Math.max.apply( Math, this.colYs );
35474         var size = {
35475             height: this.maxY
35476         };
35477       
35478         if ( this.isFitWidth ) {
35479             size.width = this._getContainerFitWidth();
35480         }
35481       
35482         return size;
35483     },
35484     
35485     _getContainerFitWidth : function()
35486     {
35487         var unusedCols = 0;
35488         // count unused columns
35489         var i = this.cols;
35490         while ( --i ) {
35491           if ( this.colYs[i] !== 0 ) {
35492             break;
35493           }
35494           unusedCols++;
35495         }
35496         // fit container to columns that have been used
35497         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35498     },
35499     
35500     needsResizeLayout : function()
35501     {
35502         var previousWidth = this.containerWidth;
35503         this.getContainerWidth();
35504         return previousWidth !== this.containerWidth;
35505     }
35506  
35507 });
35508
35509  
35510
35511  /*
35512  * - LGPL
35513  *
35514  * element
35515  * 
35516  */
35517
35518 /**
35519  * @class Roo.bootstrap.MasonryBrick
35520  * @extends Roo.bootstrap.Component
35521  * Bootstrap MasonryBrick class
35522  * 
35523  * @constructor
35524  * Create a new MasonryBrick
35525  * @param {Object} config The config object
35526  */
35527
35528 Roo.bootstrap.MasonryBrick = function(config){
35529     
35530     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35531     
35532     Roo.bootstrap.MasonryBrick.register(this);
35533     
35534     this.addEvents({
35535         // raw events
35536         /**
35537          * @event click
35538          * When a MasonryBrick is clcik
35539          * @param {Roo.bootstrap.MasonryBrick} this
35540          * @param {Roo.EventObject} e
35541          */
35542         "click" : true
35543     });
35544 };
35545
35546 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35547     
35548     /**
35549      * @cfg {String} title
35550      */   
35551     title : '',
35552     /**
35553      * @cfg {String} html
35554      */   
35555     html : '',
35556     /**
35557      * @cfg {String} bgimage
35558      */   
35559     bgimage : '',
35560     /**
35561      * @cfg {String} videourl
35562      */   
35563     videourl : '',
35564     /**
35565      * @cfg {String} cls
35566      */   
35567     cls : '',
35568     /**
35569      * @cfg {String} href
35570      */   
35571     href : '',
35572     /**
35573      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35574      */   
35575     size : 'xs',
35576     
35577     /**
35578      * @cfg {String} placetitle (center|bottom)
35579      */   
35580     placetitle : '',
35581     
35582     /**
35583      * @cfg {Boolean} isFitContainer defalut true
35584      */   
35585     isFitContainer : true, 
35586     
35587     /**
35588      * @cfg {Boolean} preventDefault defalut false
35589      */   
35590     preventDefault : false, 
35591     
35592     /**
35593      * @cfg {Boolean} inverse defalut false
35594      */   
35595     maskInverse : false, 
35596     
35597     getAutoCreate : function()
35598     {
35599         if(!this.isFitContainer){
35600             return this.getSplitAutoCreate();
35601         }
35602         
35603         var cls = 'masonry-brick masonry-brick-full';
35604         
35605         if(this.href.length){
35606             cls += ' masonry-brick-link';
35607         }
35608         
35609         if(this.bgimage.length){
35610             cls += ' masonry-brick-image';
35611         }
35612         
35613         if(this.maskInverse){
35614             cls += ' mask-inverse';
35615         }
35616         
35617         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35618             cls += ' enable-mask';
35619         }
35620         
35621         if(this.size){
35622             cls += ' masonry-' + this.size + '-brick';
35623         }
35624         
35625         if(this.placetitle.length){
35626             
35627             switch (this.placetitle) {
35628                 case 'center' :
35629                     cls += ' masonry-center-title';
35630                     break;
35631                 case 'bottom' :
35632                     cls += ' masonry-bottom-title';
35633                     break;
35634                 default:
35635                     break;
35636             }
35637             
35638         } else {
35639             if(!this.html.length && !this.bgimage.length){
35640                 cls += ' masonry-center-title';
35641             }
35642
35643             if(!this.html.length && this.bgimage.length){
35644                 cls += ' masonry-bottom-title';
35645             }
35646         }
35647         
35648         if(this.cls){
35649             cls += ' ' + this.cls;
35650         }
35651         
35652         var cfg = {
35653             tag: (this.href.length) ? 'a' : 'div',
35654             cls: cls,
35655             cn: [
35656                 {
35657                     tag: 'div',
35658                     cls: 'masonry-brick-mask'
35659                 },
35660                 {
35661                     tag: 'div',
35662                     cls: 'masonry-brick-paragraph',
35663                     cn: []
35664                 }
35665             ]
35666         };
35667         
35668         if(this.href.length){
35669             cfg.href = this.href;
35670         }
35671         
35672         var cn = cfg.cn[1].cn;
35673         
35674         if(this.title.length){
35675             cn.push({
35676                 tag: 'h4',
35677                 cls: 'masonry-brick-title',
35678                 html: this.title
35679             });
35680         }
35681         
35682         if(this.html.length){
35683             cn.push({
35684                 tag: 'p',
35685                 cls: 'masonry-brick-text',
35686                 html: this.html
35687             });
35688         }
35689         
35690         if (!this.title.length && !this.html.length) {
35691             cfg.cn[1].cls += ' hide';
35692         }
35693         
35694         if(this.bgimage.length){
35695             cfg.cn.push({
35696                 tag: 'img',
35697                 cls: 'masonry-brick-image-view',
35698                 src: this.bgimage
35699             });
35700         }
35701         
35702         if(this.videourl.length){
35703             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35704             // youtube support only?
35705             cfg.cn.push({
35706                 tag: 'iframe',
35707                 cls: 'masonry-brick-image-view',
35708                 src: vurl,
35709                 frameborder : 0,
35710                 allowfullscreen : true
35711             });
35712         }
35713         
35714         return cfg;
35715         
35716     },
35717     
35718     getSplitAutoCreate : function()
35719     {
35720         var cls = 'masonry-brick masonry-brick-split';
35721         
35722         if(this.href.length){
35723             cls += ' masonry-brick-link';
35724         }
35725         
35726         if(this.bgimage.length){
35727             cls += ' masonry-brick-image';
35728         }
35729         
35730         if(this.size){
35731             cls += ' masonry-' + this.size + '-brick';
35732         }
35733         
35734         switch (this.placetitle) {
35735             case 'center' :
35736                 cls += ' masonry-center-title';
35737                 break;
35738             case 'bottom' :
35739                 cls += ' masonry-bottom-title';
35740                 break;
35741             default:
35742                 if(!this.bgimage.length){
35743                     cls += ' masonry-center-title';
35744                 }
35745
35746                 if(this.bgimage.length){
35747                     cls += ' masonry-bottom-title';
35748                 }
35749                 break;
35750         }
35751         
35752         if(this.cls){
35753             cls += ' ' + this.cls;
35754         }
35755         
35756         var cfg = {
35757             tag: (this.href.length) ? 'a' : 'div',
35758             cls: cls,
35759             cn: [
35760                 {
35761                     tag: 'div',
35762                     cls: 'masonry-brick-split-head',
35763                     cn: [
35764                         {
35765                             tag: 'div',
35766                             cls: 'masonry-brick-paragraph',
35767                             cn: []
35768                         }
35769                     ]
35770                 },
35771                 {
35772                     tag: 'div',
35773                     cls: 'masonry-brick-split-body',
35774                     cn: []
35775                 }
35776             ]
35777         };
35778         
35779         if(this.href.length){
35780             cfg.href = this.href;
35781         }
35782         
35783         if(this.title.length){
35784             cfg.cn[0].cn[0].cn.push({
35785                 tag: 'h4',
35786                 cls: 'masonry-brick-title',
35787                 html: this.title
35788             });
35789         }
35790         
35791         if(this.html.length){
35792             cfg.cn[1].cn.push({
35793                 tag: 'p',
35794                 cls: 'masonry-brick-text',
35795                 html: this.html
35796             });
35797         }
35798
35799         if(this.bgimage.length){
35800             cfg.cn[0].cn.push({
35801                 tag: 'img',
35802                 cls: 'masonry-brick-image-view',
35803                 src: this.bgimage
35804             });
35805         }
35806         
35807         if(this.videourl.length){
35808             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35809             // youtube support only?
35810             cfg.cn[0].cn.cn.push({
35811                 tag: 'iframe',
35812                 cls: 'masonry-brick-image-view',
35813                 src: vurl,
35814                 frameborder : 0,
35815                 allowfullscreen : true
35816             });
35817         }
35818         
35819         return cfg;
35820     },
35821     
35822     initEvents: function() 
35823     {
35824         switch (this.size) {
35825             case 'xs' :
35826                 this.x = 1;
35827                 this.y = 1;
35828                 break;
35829             case 'sm' :
35830                 this.x = 2;
35831                 this.y = 2;
35832                 break;
35833             case 'md' :
35834             case 'md-left' :
35835             case 'md-right' :
35836                 this.x = 3;
35837                 this.y = 3;
35838                 break;
35839             case 'tall' :
35840                 this.x = 2;
35841                 this.y = 3;
35842                 break;
35843             case 'wide' :
35844                 this.x = 3;
35845                 this.y = 2;
35846                 break;
35847             case 'wide-thin' :
35848                 this.x = 3;
35849                 this.y = 1;
35850                 break;
35851                         
35852             default :
35853                 break;
35854         }
35855         
35856         if(Roo.isTouch){
35857             this.el.on('touchstart', this.onTouchStart, this);
35858             this.el.on('touchmove', this.onTouchMove, this);
35859             this.el.on('touchend', this.onTouchEnd, this);
35860             this.el.on('contextmenu', this.onContextMenu, this);
35861         } else {
35862             this.el.on('mouseenter'  ,this.enter, this);
35863             this.el.on('mouseleave', this.leave, this);
35864             this.el.on('click', this.onClick, this);
35865         }
35866         
35867         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35868             this.parent().bricks.push(this);   
35869         }
35870         
35871     },
35872     
35873     onClick: function(e, el)
35874     {
35875         var time = this.endTimer - this.startTimer;
35876         // Roo.log(e.preventDefault());
35877         if(Roo.isTouch){
35878             if(time > 1000){
35879                 e.preventDefault();
35880                 return;
35881             }
35882         }
35883         
35884         if(!this.preventDefault){
35885             return;
35886         }
35887         
35888         e.preventDefault();
35889         
35890         if (this.activeClass != '') {
35891             this.selectBrick();
35892         }
35893         
35894         this.fireEvent('click', this, e);
35895     },
35896     
35897     enter: function(e, el)
35898     {
35899         e.preventDefault();
35900         
35901         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35902             return;
35903         }
35904         
35905         if(this.bgimage.length && this.html.length){
35906             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35907         }
35908     },
35909     
35910     leave: function(e, el)
35911     {
35912         e.preventDefault();
35913         
35914         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35915             return;
35916         }
35917         
35918         if(this.bgimage.length && this.html.length){
35919             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35920         }
35921     },
35922     
35923     onTouchStart: function(e, el)
35924     {
35925 //        e.preventDefault();
35926         
35927         this.touchmoved = false;
35928         
35929         if(!this.isFitContainer){
35930             return;
35931         }
35932         
35933         if(!this.bgimage.length || !this.html.length){
35934             return;
35935         }
35936         
35937         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35938         
35939         this.timer = new Date().getTime();
35940         
35941     },
35942     
35943     onTouchMove: function(e, el)
35944     {
35945         this.touchmoved = true;
35946     },
35947     
35948     onContextMenu : function(e,el)
35949     {
35950         e.preventDefault();
35951         e.stopPropagation();
35952         return false;
35953     },
35954     
35955     onTouchEnd: function(e, el)
35956     {
35957 //        e.preventDefault();
35958         
35959         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35960         
35961             this.leave(e,el);
35962             
35963             return;
35964         }
35965         
35966         if(!this.bgimage.length || !this.html.length){
35967             
35968             if(this.href.length){
35969                 window.location.href = this.href;
35970             }
35971             
35972             return;
35973         }
35974         
35975         if(!this.isFitContainer){
35976             return;
35977         }
35978         
35979         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35980         
35981         window.location.href = this.href;
35982     },
35983     
35984     //selection on single brick only
35985     selectBrick : function() {
35986         
35987         if (!this.parentId) {
35988             return;
35989         }
35990         
35991         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35992         var index = m.selectedBrick.indexOf(this.id);
35993         
35994         if ( index > -1) {
35995             m.selectedBrick.splice(index,1);
35996             this.el.removeClass(this.activeClass);
35997             return;
35998         }
35999         
36000         for(var i = 0; i < m.selectedBrick.length; i++) {
36001             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36002             b.el.removeClass(b.activeClass);
36003         }
36004         
36005         m.selectedBrick = [];
36006         
36007         m.selectedBrick.push(this.id);
36008         this.el.addClass(this.activeClass);
36009         return;
36010     },
36011     
36012     isSelected : function(){
36013         return this.el.hasClass(this.activeClass);
36014         
36015     }
36016 });
36017
36018 Roo.apply(Roo.bootstrap.MasonryBrick, {
36019     
36020     //groups: {},
36021     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36022      /**
36023     * register a Masonry Brick
36024     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36025     */
36026     
36027     register : function(brick)
36028     {
36029         //this.groups[brick.id] = brick;
36030         this.groups.add(brick.id, brick);
36031     },
36032     /**
36033     * fetch a  masonry brick based on the masonry brick ID
36034     * @param {string} the masonry brick to add
36035     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36036     */
36037     
36038     get: function(brick_id) 
36039     {
36040         // if (typeof(this.groups[brick_id]) == 'undefined') {
36041         //     return false;
36042         // }
36043         // return this.groups[brick_id] ;
36044         
36045         if(this.groups.key(brick_id)) {
36046             return this.groups.key(brick_id);
36047         }
36048         
36049         return false;
36050     }
36051     
36052     
36053     
36054 });
36055
36056  /*
36057  * - LGPL
36058  *
36059  * element
36060  * 
36061  */
36062
36063 /**
36064  * @class Roo.bootstrap.Brick
36065  * @extends Roo.bootstrap.Component
36066  * Bootstrap Brick class
36067  * 
36068  * @constructor
36069  * Create a new Brick
36070  * @param {Object} config The config object
36071  */
36072
36073 Roo.bootstrap.Brick = function(config){
36074     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36075     
36076     this.addEvents({
36077         // raw events
36078         /**
36079          * @event click
36080          * When a Brick is click
36081          * @param {Roo.bootstrap.Brick} this
36082          * @param {Roo.EventObject} e
36083          */
36084         "click" : true
36085     });
36086 };
36087
36088 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36089     
36090     /**
36091      * @cfg {String} title
36092      */   
36093     title : '',
36094     /**
36095      * @cfg {String} html
36096      */   
36097     html : '',
36098     /**
36099      * @cfg {String} bgimage
36100      */   
36101     bgimage : '',
36102     /**
36103      * @cfg {String} cls
36104      */   
36105     cls : '',
36106     /**
36107      * @cfg {String} href
36108      */   
36109     href : '',
36110     /**
36111      * @cfg {String} video
36112      */   
36113     video : '',
36114     /**
36115      * @cfg {Boolean} square
36116      */   
36117     square : true,
36118     
36119     getAutoCreate : function()
36120     {
36121         var cls = 'roo-brick';
36122         
36123         if(this.href.length){
36124             cls += ' roo-brick-link';
36125         }
36126         
36127         if(this.bgimage.length){
36128             cls += ' roo-brick-image';
36129         }
36130         
36131         if(!this.html.length && !this.bgimage.length){
36132             cls += ' roo-brick-center-title';
36133         }
36134         
36135         if(!this.html.length && this.bgimage.length){
36136             cls += ' roo-brick-bottom-title';
36137         }
36138         
36139         if(this.cls){
36140             cls += ' ' + this.cls;
36141         }
36142         
36143         var cfg = {
36144             tag: (this.href.length) ? 'a' : 'div',
36145             cls: cls,
36146             cn: [
36147                 {
36148                     tag: 'div',
36149                     cls: 'roo-brick-paragraph',
36150                     cn: []
36151                 }
36152             ]
36153         };
36154         
36155         if(this.href.length){
36156             cfg.href = this.href;
36157         }
36158         
36159         var cn = cfg.cn[0].cn;
36160         
36161         if(this.title.length){
36162             cn.push({
36163                 tag: 'h4',
36164                 cls: 'roo-brick-title',
36165                 html: this.title
36166             });
36167         }
36168         
36169         if(this.html.length){
36170             cn.push({
36171                 tag: 'p',
36172                 cls: 'roo-brick-text',
36173                 html: this.html
36174             });
36175         } else {
36176             cn.cls += ' hide';
36177         }
36178         
36179         if(this.bgimage.length){
36180             cfg.cn.push({
36181                 tag: 'img',
36182                 cls: 'roo-brick-image-view',
36183                 src: this.bgimage
36184             });
36185         }
36186         
36187         return cfg;
36188     },
36189     
36190     initEvents: function() 
36191     {
36192         if(this.title.length || this.html.length){
36193             this.el.on('mouseenter'  ,this.enter, this);
36194             this.el.on('mouseleave', this.leave, this);
36195         }
36196         
36197         Roo.EventManager.onWindowResize(this.resize, this); 
36198         
36199         if(this.bgimage.length){
36200             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36201             this.imageEl.on('load', this.onImageLoad, this);
36202             return;
36203         }
36204         
36205         this.resize();
36206     },
36207     
36208     onImageLoad : function()
36209     {
36210         this.resize();
36211     },
36212     
36213     resize : function()
36214     {
36215         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36216         
36217         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36218         
36219         if(this.bgimage.length){
36220             var image = this.el.select('.roo-brick-image-view', true).first();
36221             
36222             image.setWidth(paragraph.getWidth());
36223             
36224             if(this.square){
36225                 image.setHeight(paragraph.getWidth());
36226             }
36227             
36228             this.el.setHeight(image.getHeight());
36229             paragraph.setHeight(image.getHeight());
36230             
36231         }
36232         
36233     },
36234     
36235     enter: function(e, el)
36236     {
36237         e.preventDefault();
36238         
36239         if(this.bgimage.length){
36240             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36241             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36242         }
36243     },
36244     
36245     leave: function(e, el)
36246     {
36247         e.preventDefault();
36248         
36249         if(this.bgimage.length){
36250             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36251             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36252         }
36253     }
36254     
36255 });
36256
36257  
36258
36259  /*
36260  * - LGPL
36261  *
36262  * Number field 
36263  */
36264
36265 /**
36266  * @class Roo.bootstrap.NumberField
36267  * @extends Roo.bootstrap.Input
36268  * Bootstrap NumberField class
36269  * 
36270  * 
36271  * 
36272  * 
36273  * @constructor
36274  * Create a new NumberField
36275  * @param {Object} config The config object
36276  */
36277
36278 Roo.bootstrap.NumberField = function(config){
36279     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36280 };
36281
36282 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36283     
36284     /**
36285      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36286      */
36287     allowDecimals : true,
36288     /**
36289      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36290      */
36291     decimalSeparator : ".",
36292     /**
36293      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36294      */
36295     decimalPrecision : 2,
36296     /**
36297      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36298      */
36299     allowNegative : true,
36300     
36301     /**
36302      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36303      */
36304     allowZero: true,
36305     /**
36306      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36307      */
36308     minValue : Number.NEGATIVE_INFINITY,
36309     /**
36310      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36311      */
36312     maxValue : Number.MAX_VALUE,
36313     /**
36314      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36315      */
36316     minText : "The minimum value for this field is {0}",
36317     /**
36318      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36319      */
36320     maxText : "The maximum value for this field is {0}",
36321     /**
36322      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36323      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36324      */
36325     nanText : "{0} is not a valid number",
36326     /**
36327      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36328      */
36329     thousandsDelimiter : false,
36330     /**
36331      * @cfg {String} valueAlign alignment of value
36332      */
36333     valueAlign : "left",
36334
36335     getAutoCreate : function()
36336     {
36337         var hiddenInput = {
36338             tag: 'input',
36339             type: 'hidden',
36340             id: Roo.id(),
36341             cls: 'hidden-number-input'
36342         };
36343         
36344         if (this.name) {
36345             hiddenInput.name = this.name;
36346         }
36347         
36348         this.name = '';
36349         
36350         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36351         
36352         this.name = hiddenInput.name;
36353         
36354         if(cfg.cn.length > 0) {
36355             cfg.cn.push(hiddenInput);
36356         }
36357         
36358         return cfg;
36359     },
36360
36361     // private
36362     initEvents : function()
36363     {   
36364         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36365         
36366         var allowed = "0123456789";
36367         
36368         if(this.allowDecimals){
36369             allowed += this.decimalSeparator;
36370         }
36371         
36372         if(this.allowNegative){
36373             allowed += "-";
36374         }
36375         
36376         if(this.thousandsDelimiter) {
36377             allowed += ",";
36378         }
36379         
36380         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36381         
36382         var keyPress = function(e){
36383             
36384             var k = e.getKey();
36385             
36386             var c = e.getCharCode();
36387             
36388             if(
36389                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36390                     allowed.indexOf(String.fromCharCode(c)) === -1
36391             ){
36392                 e.stopEvent();
36393                 return;
36394             }
36395             
36396             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36397                 return;
36398             }
36399             
36400             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36401                 e.stopEvent();
36402             }
36403         };
36404         
36405         this.el.on("keypress", keyPress, this);
36406     },
36407     
36408     validateValue : function(value)
36409     {
36410         
36411         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36412             return false;
36413         }
36414         
36415         var num = this.parseValue(value);
36416         
36417         if(isNaN(num)){
36418             this.markInvalid(String.format(this.nanText, value));
36419             return false;
36420         }
36421         
36422         if(num < this.minValue){
36423             this.markInvalid(String.format(this.minText, this.minValue));
36424             return false;
36425         }
36426         
36427         if(num > this.maxValue){
36428             this.markInvalid(String.format(this.maxText, this.maxValue));
36429             return false;
36430         }
36431         
36432         return true;
36433     },
36434
36435     getValue : function()
36436     {
36437         var v = this.hiddenEl().getValue();
36438         
36439         return this.fixPrecision(this.parseValue(v));
36440     },
36441
36442     parseValue : function(value)
36443     {
36444         if(this.thousandsDelimiter) {
36445             value += "";
36446             r = new RegExp(",", "g");
36447             value = value.replace(r, "");
36448         }
36449         
36450         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36451         return isNaN(value) ? '' : value;
36452     },
36453
36454     fixPrecision : function(value)
36455     {
36456         if(this.thousandsDelimiter) {
36457             value += "";
36458             r = new RegExp(",", "g");
36459             value = value.replace(r, "");
36460         }
36461         
36462         var nan = isNaN(value);
36463         
36464         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36465             return nan ? '' : value;
36466         }
36467         return parseFloat(value).toFixed(this.decimalPrecision);
36468     },
36469
36470     setValue : function(v)
36471     {
36472         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36473         
36474         this.value = v;
36475         
36476         if(this.rendered){
36477             
36478             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36479             
36480             this.inputEl().dom.value = (v == '') ? '' :
36481                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36482             
36483             if(!this.allowZero && v === '0') {
36484                 this.hiddenEl().dom.value = '';
36485                 this.inputEl().dom.value = '';
36486             }
36487             
36488             this.validate();
36489         }
36490     },
36491
36492     decimalPrecisionFcn : function(v)
36493     {
36494         return Math.floor(v);
36495     },
36496
36497     beforeBlur : function()
36498     {
36499         var v = this.parseValue(this.getRawValue());
36500         
36501         if(v || v === 0 || v === ''){
36502             this.setValue(v);
36503         }
36504     },
36505     
36506     hiddenEl : function()
36507     {
36508         return this.el.select('input.hidden-number-input',true).first();
36509     }
36510     
36511 });
36512
36513  
36514
36515 /*
36516 * Licence: LGPL
36517 */
36518
36519 /**
36520  * @class Roo.bootstrap.DocumentSlider
36521  * @extends Roo.bootstrap.Component
36522  * Bootstrap DocumentSlider class
36523  * 
36524  * @constructor
36525  * Create a new DocumentViewer
36526  * @param {Object} config The config object
36527  */
36528
36529 Roo.bootstrap.DocumentSlider = function(config){
36530     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36531     
36532     this.files = [];
36533     
36534     this.addEvents({
36535         /**
36536          * @event initial
36537          * Fire after initEvent
36538          * @param {Roo.bootstrap.DocumentSlider} this
36539          */
36540         "initial" : true,
36541         /**
36542          * @event update
36543          * Fire after update
36544          * @param {Roo.bootstrap.DocumentSlider} this
36545          */
36546         "update" : true,
36547         /**
36548          * @event click
36549          * Fire after click
36550          * @param {Roo.bootstrap.DocumentSlider} this
36551          */
36552         "click" : true
36553     });
36554 };
36555
36556 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36557     
36558     files : false,
36559     
36560     indicator : 0,
36561     
36562     getAutoCreate : function()
36563     {
36564         var cfg = {
36565             tag : 'div',
36566             cls : 'roo-document-slider',
36567             cn : [
36568                 {
36569                     tag : 'div',
36570                     cls : 'roo-document-slider-header',
36571                     cn : [
36572                         {
36573                             tag : 'div',
36574                             cls : 'roo-document-slider-header-title'
36575                         }
36576                     ]
36577                 },
36578                 {
36579                     tag : 'div',
36580                     cls : 'roo-document-slider-body',
36581                     cn : [
36582                         {
36583                             tag : 'div',
36584                             cls : 'roo-document-slider-prev',
36585                             cn : [
36586                                 {
36587                                     tag : 'i',
36588                                     cls : 'fa fa-chevron-left'
36589                                 }
36590                             ]
36591                         },
36592                         {
36593                             tag : 'div',
36594                             cls : 'roo-document-slider-thumb',
36595                             cn : [
36596                                 {
36597                                     tag : 'img',
36598                                     cls : 'roo-document-slider-image'
36599                                 }
36600                             ]
36601                         },
36602                         {
36603                             tag : 'div',
36604                             cls : 'roo-document-slider-next',
36605                             cn : [
36606                                 {
36607                                     tag : 'i',
36608                                     cls : 'fa fa-chevron-right'
36609                                 }
36610                             ]
36611                         }
36612                     ]
36613                 }
36614             ]
36615         };
36616         
36617         return cfg;
36618     },
36619     
36620     initEvents : function()
36621     {
36622         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36623         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36624         
36625         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36626         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36627         
36628         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36629         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36630         
36631         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36632         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36633         
36634         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36635         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36636         
36637         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36638         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36639         
36640         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36641         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36642         
36643         this.thumbEl.on('click', this.onClick, this);
36644         
36645         this.prevIndicator.on('click', this.prev, this);
36646         
36647         this.nextIndicator.on('click', this.next, this);
36648         
36649     },
36650     
36651     initial : function()
36652     {
36653         if(this.files.length){
36654             this.indicator = 1;
36655             this.update()
36656         }
36657         
36658         this.fireEvent('initial', this);
36659     },
36660     
36661     update : function()
36662     {
36663         this.imageEl.attr('src', this.files[this.indicator - 1]);
36664         
36665         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36666         
36667         this.prevIndicator.show();
36668         
36669         if(this.indicator == 1){
36670             this.prevIndicator.hide();
36671         }
36672         
36673         this.nextIndicator.show();
36674         
36675         if(this.indicator == this.files.length){
36676             this.nextIndicator.hide();
36677         }
36678         
36679         this.thumbEl.scrollTo('top');
36680         
36681         this.fireEvent('update', this);
36682     },
36683     
36684     onClick : function(e)
36685     {
36686         e.preventDefault();
36687         
36688         this.fireEvent('click', this);
36689     },
36690     
36691     prev : function(e)
36692     {
36693         e.preventDefault();
36694         
36695         this.indicator = Math.max(1, this.indicator - 1);
36696         
36697         this.update();
36698     },
36699     
36700     next : function(e)
36701     {
36702         e.preventDefault();
36703         
36704         this.indicator = Math.min(this.files.length, this.indicator + 1);
36705         
36706         this.update();
36707     }
36708 });
36709 /*
36710  * - LGPL
36711  *
36712  * RadioSet
36713  *
36714  *
36715  */
36716
36717 /**
36718  * @class Roo.bootstrap.RadioSet
36719  * @extends Roo.bootstrap.Input
36720  * Bootstrap RadioSet class
36721  * @cfg {String} indicatorpos (left|right) default left
36722  * @cfg {Boolean} inline (true|false) inline the element (default true)
36723  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36724  * @constructor
36725  * Create a new RadioSet
36726  * @param {Object} config The config object
36727  */
36728
36729 Roo.bootstrap.RadioSet = function(config){
36730     
36731     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36732     
36733     this.radioes = [];
36734     
36735     Roo.bootstrap.RadioSet.register(this);
36736     
36737     this.addEvents({
36738         /**
36739         * @event check
36740         * Fires when the element is checked or unchecked.
36741         * @param {Roo.bootstrap.RadioSet} this This radio
36742         * @param {Roo.bootstrap.Radio} item The checked item
36743         */
36744        check : true,
36745        /**
36746         * @event click
36747         * Fires when the element is click.
36748         * @param {Roo.bootstrap.RadioSet} this This radio set
36749         * @param {Roo.bootstrap.Radio} item The checked item
36750         * @param {Roo.EventObject} e The event object
36751         */
36752        click : true
36753     });
36754     
36755 };
36756
36757 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36758
36759     radioes : false,
36760     
36761     inline : true,
36762     
36763     weight : '',
36764     
36765     indicatorpos : 'left',
36766     
36767     getAutoCreate : function()
36768     {
36769         var label = {
36770             tag : 'label',
36771             cls : 'roo-radio-set-label',
36772             cn : [
36773                 {
36774                     tag : 'span',
36775                     html : this.fieldLabel
36776                 }
36777             ]
36778         };
36779         if (Roo.bootstrap.version == 3) {
36780             
36781             
36782             if(this.indicatorpos == 'left'){
36783                 label.cn.unshift({
36784                     tag : 'i',
36785                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36786                     tooltip : 'This field is required'
36787                 });
36788             } else {
36789                 label.cn.push({
36790                     tag : 'i',
36791                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36792                     tooltip : 'This field is required'
36793                 });
36794             }
36795         }
36796         var items = {
36797             tag : 'div',
36798             cls : 'roo-radio-set-items'
36799         };
36800         
36801         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36802         
36803         if (align === 'left' && this.fieldLabel.length) {
36804             
36805             items = {
36806                 cls : "roo-radio-set-right", 
36807                 cn: [
36808                     items
36809                 ]
36810             };
36811             
36812             if(this.labelWidth > 12){
36813                 label.style = "width: " + this.labelWidth + 'px';
36814             }
36815             
36816             if(this.labelWidth < 13 && this.labelmd == 0){
36817                 this.labelmd = this.labelWidth;
36818             }
36819             
36820             if(this.labellg > 0){
36821                 label.cls += ' col-lg-' + this.labellg;
36822                 items.cls += ' col-lg-' + (12 - this.labellg);
36823             }
36824             
36825             if(this.labelmd > 0){
36826                 label.cls += ' col-md-' + this.labelmd;
36827                 items.cls += ' col-md-' + (12 - this.labelmd);
36828             }
36829             
36830             if(this.labelsm > 0){
36831                 label.cls += ' col-sm-' + this.labelsm;
36832                 items.cls += ' col-sm-' + (12 - this.labelsm);
36833             }
36834             
36835             if(this.labelxs > 0){
36836                 label.cls += ' col-xs-' + this.labelxs;
36837                 items.cls += ' col-xs-' + (12 - this.labelxs);
36838             }
36839         }
36840         
36841         var cfg = {
36842             tag : 'div',
36843             cls : 'roo-radio-set',
36844             cn : [
36845                 {
36846                     tag : 'input',
36847                     cls : 'roo-radio-set-input',
36848                     type : 'hidden',
36849                     name : this.name,
36850                     value : this.value ? this.value :  ''
36851                 },
36852                 label,
36853                 items
36854             ]
36855         };
36856         
36857         if(this.weight.length){
36858             cfg.cls += ' roo-radio-' + this.weight;
36859         }
36860         
36861         if(this.inline) {
36862             cfg.cls += ' roo-radio-set-inline';
36863         }
36864         
36865         var settings=this;
36866         ['xs','sm','md','lg'].map(function(size){
36867             if (settings[size]) {
36868                 cfg.cls += ' col-' + size + '-' + settings[size];
36869             }
36870         });
36871         
36872         return cfg;
36873         
36874     },
36875
36876     initEvents : function()
36877     {
36878         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36879         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36880         
36881         if(!this.fieldLabel.length){
36882             this.labelEl.hide();
36883         }
36884         
36885         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36886         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36887         
36888         this.indicator = this.indicatorEl();
36889         
36890         if(this.indicator){
36891             this.indicator.addClass('invisible');
36892         }
36893         
36894         this.originalValue = this.getValue();
36895         
36896     },
36897     
36898     inputEl: function ()
36899     {
36900         return this.el.select('.roo-radio-set-input', true).first();
36901     },
36902     
36903     getChildContainer : function()
36904     {
36905         return this.itemsEl;
36906     },
36907     
36908     register : function(item)
36909     {
36910         this.radioes.push(item);
36911         
36912     },
36913     
36914     validate : function()
36915     {   
36916         if(this.getVisibilityEl().hasClass('hidden')){
36917             return true;
36918         }
36919         
36920         var valid = false;
36921         
36922         Roo.each(this.radioes, function(i){
36923             if(!i.checked){
36924                 return;
36925             }
36926             
36927             valid = true;
36928             return false;
36929         });
36930         
36931         if(this.allowBlank) {
36932             return true;
36933         }
36934         
36935         if(this.disabled || valid){
36936             this.markValid();
36937             return true;
36938         }
36939         
36940         this.markInvalid();
36941         return false;
36942         
36943     },
36944     
36945     markValid : function()
36946     {
36947         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36948             this.indicatorEl().removeClass('visible');
36949             this.indicatorEl().addClass('invisible');
36950         }
36951         
36952         
36953         if (Roo.bootstrap.version == 3) {
36954             this.el.removeClass([this.invalidClass, this.validClass]);
36955             this.el.addClass(this.validClass);
36956         } else {
36957             this.el.removeClass(['is-invalid','is-valid']);
36958             this.el.addClass(['is-valid']);
36959         }
36960         this.fireEvent('valid', this);
36961     },
36962     
36963     markInvalid : function(msg)
36964     {
36965         if(this.allowBlank || this.disabled){
36966             return;
36967         }
36968         
36969         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36970             this.indicatorEl().removeClass('invisible');
36971             this.indicatorEl().addClass('visible');
36972         }
36973         if (Roo.bootstrap.version == 3) {
36974             this.el.removeClass([this.invalidClass, this.validClass]);
36975             this.el.addClass(this.invalidClass);
36976         } else {
36977             this.el.removeClass(['is-invalid','is-valid']);
36978             this.el.addClass(['is-invalid']);
36979         }
36980         
36981         this.fireEvent('invalid', this, msg);
36982         
36983     },
36984     
36985     setValue : function(v, suppressEvent)
36986     {   
36987         if(this.value === v){
36988             return;
36989         }
36990         
36991         this.value = v;
36992         
36993         if(this.rendered){
36994             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36995         }
36996         
36997         Roo.each(this.radioes, function(i){
36998             i.checked = false;
36999             i.el.removeClass('checked');
37000         });
37001         
37002         Roo.each(this.radioes, function(i){
37003             
37004             if(i.value === v || i.value.toString() === v.toString()){
37005                 i.checked = true;
37006                 i.el.addClass('checked');
37007                 
37008                 if(suppressEvent !== true){
37009                     this.fireEvent('check', this, i);
37010                 }
37011                 
37012                 return false;
37013             }
37014             
37015         }, this);
37016         
37017         this.validate();
37018     },
37019     
37020     clearInvalid : function(){
37021         
37022         if(!this.el || this.preventMark){
37023             return;
37024         }
37025         
37026         this.el.removeClass([this.invalidClass]);
37027         
37028         this.fireEvent('valid', this);
37029     }
37030     
37031 });
37032
37033 Roo.apply(Roo.bootstrap.RadioSet, {
37034     
37035     groups: {},
37036     
37037     register : function(set)
37038     {
37039         this.groups[set.name] = set;
37040     },
37041     
37042     get: function(name) 
37043     {
37044         if (typeof(this.groups[name]) == 'undefined') {
37045             return false;
37046         }
37047         
37048         return this.groups[name] ;
37049     }
37050     
37051 });
37052 /*
37053  * Based on:
37054  * Ext JS Library 1.1.1
37055  * Copyright(c) 2006-2007, Ext JS, LLC.
37056  *
37057  * Originally Released Under LGPL - original licence link has changed is not relivant.
37058  *
37059  * Fork - LGPL
37060  * <script type="text/javascript">
37061  */
37062
37063
37064 /**
37065  * @class Roo.bootstrap.SplitBar
37066  * @extends Roo.util.Observable
37067  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37068  * <br><br>
37069  * Usage:
37070  * <pre><code>
37071 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37072                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37073 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37074 split.minSize = 100;
37075 split.maxSize = 600;
37076 split.animate = true;
37077 split.on('moved', splitterMoved);
37078 </code></pre>
37079  * @constructor
37080  * Create a new SplitBar
37081  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37082  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37083  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37084  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37085                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37086                         position of the SplitBar).
37087  */
37088 Roo.bootstrap.SplitBar = function(cfg){
37089     
37090     /** @private */
37091     
37092     //{
37093     //  dragElement : elm
37094     //  resizingElement: el,
37095         // optional..
37096     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37097     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37098         // existingProxy ???
37099     //}
37100     
37101     this.el = Roo.get(cfg.dragElement, true);
37102     this.el.dom.unselectable = "on";
37103     /** @private */
37104     this.resizingEl = Roo.get(cfg.resizingElement, true);
37105
37106     /**
37107      * @private
37108      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37109      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37110      * @type Number
37111      */
37112     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37113     
37114     /**
37115      * The minimum size of the resizing element. (Defaults to 0)
37116      * @type Number
37117      */
37118     this.minSize = 0;
37119     
37120     /**
37121      * The maximum size of the resizing element. (Defaults to 2000)
37122      * @type Number
37123      */
37124     this.maxSize = 2000;
37125     
37126     /**
37127      * Whether to animate the transition to the new size
37128      * @type Boolean
37129      */
37130     this.animate = false;
37131     
37132     /**
37133      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37134      * @type Boolean
37135      */
37136     this.useShim = false;
37137     
37138     /** @private */
37139     this.shim = null;
37140     
37141     if(!cfg.existingProxy){
37142         /** @private */
37143         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37144     }else{
37145         this.proxy = Roo.get(cfg.existingProxy).dom;
37146     }
37147     /** @private */
37148     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37149     
37150     /** @private */
37151     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37152     
37153     /** @private */
37154     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37155     
37156     /** @private */
37157     this.dragSpecs = {};
37158     
37159     /**
37160      * @private The adapter to use to positon and resize elements
37161      */
37162     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37163     this.adapter.init(this);
37164     
37165     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37166         /** @private */
37167         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37168         this.el.addClass("roo-splitbar-h");
37169     }else{
37170         /** @private */
37171         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37172         this.el.addClass("roo-splitbar-v");
37173     }
37174     
37175     this.addEvents({
37176         /**
37177          * @event resize
37178          * Fires when the splitter is moved (alias for {@link #event-moved})
37179          * @param {Roo.bootstrap.SplitBar} this
37180          * @param {Number} newSize the new width or height
37181          */
37182         "resize" : true,
37183         /**
37184          * @event moved
37185          * Fires when the splitter is moved
37186          * @param {Roo.bootstrap.SplitBar} this
37187          * @param {Number} newSize the new width or height
37188          */
37189         "moved" : true,
37190         /**
37191          * @event beforeresize
37192          * Fires before the splitter is dragged
37193          * @param {Roo.bootstrap.SplitBar} this
37194          */
37195         "beforeresize" : true,
37196
37197         "beforeapply" : true
37198     });
37199
37200     Roo.util.Observable.call(this);
37201 };
37202
37203 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37204     onStartProxyDrag : function(x, y){
37205         this.fireEvent("beforeresize", this);
37206         if(!this.overlay){
37207             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37208             o.unselectable();
37209             o.enableDisplayMode("block");
37210             // all splitbars share the same overlay
37211             Roo.bootstrap.SplitBar.prototype.overlay = o;
37212         }
37213         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37214         this.overlay.show();
37215         Roo.get(this.proxy).setDisplayed("block");
37216         var size = this.adapter.getElementSize(this);
37217         this.activeMinSize = this.getMinimumSize();;
37218         this.activeMaxSize = this.getMaximumSize();;
37219         var c1 = size - this.activeMinSize;
37220         var c2 = Math.max(this.activeMaxSize - size, 0);
37221         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37222             this.dd.resetConstraints();
37223             this.dd.setXConstraint(
37224                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37225                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37226             );
37227             this.dd.setYConstraint(0, 0);
37228         }else{
37229             this.dd.resetConstraints();
37230             this.dd.setXConstraint(0, 0);
37231             this.dd.setYConstraint(
37232                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37233                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37234             );
37235          }
37236         this.dragSpecs.startSize = size;
37237         this.dragSpecs.startPoint = [x, y];
37238         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37239     },
37240     
37241     /** 
37242      * @private Called after the drag operation by the DDProxy
37243      */
37244     onEndProxyDrag : function(e){
37245         Roo.get(this.proxy).setDisplayed(false);
37246         var endPoint = Roo.lib.Event.getXY(e);
37247         if(this.overlay){
37248             this.overlay.hide();
37249         }
37250         var newSize;
37251         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37252             newSize = this.dragSpecs.startSize + 
37253                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37254                     endPoint[0] - this.dragSpecs.startPoint[0] :
37255                     this.dragSpecs.startPoint[0] - endPoint[0]
37256                 );
37257         }else{
37258             newSize = this.dragSpecs.startSize + 
37259                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37260                     endPoint[1] - this.dragSpecs.startPoint[1] :
37261                     this.dragSpecs.startPoint[1] - endPoint[1]
37262                 );
37263         }
37264         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37265         if(newSize != this.dragSpecs.startSize){
37266             if(this.fireEvent('beforeapply', this, newSize) !== false){
37267                 this.adapter.setElementSize(this, newSize);
37268                 this.fireEvent("moved", this, newSize);
37269                 this.fireEvent("resize", this, newSize);
37270             }
37271         }
37272     },
37273     
37274     /**
37275      * Get the adapter this SplitBar uses
37276      * @return The adapter object
37277      */
37278     getAdapter : function(){
37279         return this.adapter;
37280     },
37281     
37282     /**
37283      * Set the adapter this SplitBar uses
37284      * @param {Object} adapter A SplitBar adapter object
37285      */
37286     setAdapter : function(adapter){
37287         this.adapter = adapter;
37288         this.adapter.init(this);
37289     },
37290     
37291     /**
37292      * Gets the minimum size for the resizing element
37293      * @return {Number} The minimum size
37294      */
37295     getMinimumSize : function(){
37296         return this.minSize;
37297     },
37298     
37299     /**
37300      * Sets the minimum size for the resizing element
37301      * @param {Number} minSize The minimum size
37302      */
37303     setMinimumSize : function(minSize){
37304         this.minSize = minSize;
37305     },
37306     
37307     /**
37308      * Gets the maximum size for the resizing element
37309      * @return {Number} The maximum size
37310      */
37311     getMaximumSize : function(){
37312         return this.maxSize;
37313     },
37314     
37315     /**
37316      * Sets the maximum size for the resizing element
37317      * @param {Number} maxSize The maximum size
37318      */
37319     setMaximumSize : function(maxSize){
37320         this.maxSize = maxSize;
37321     },
37322     
37323     /**
37324      * Sets the initialize size for the resizing element
37325      * @param {Number} size The initial size
37326      */
37327     setCurrentSize : function(size){
37328         var oldAnimate = this.animate;
37329         this.animate = false;
37330         this.adapter.setElementSize(this, size);
37331         this.animate = oldAnimate;
37332     },
37333     
37334     /**
37335      * Destroy this splitbar. 
37336      * @param {Boolean} removeEl True to remove the element
37337      */
37338     destroy : function(removeEl){
37339         if(this.shim){
37340             this.shim.remove();
37341         }
37342         this.dd.unreg();
37343         this.proxy.parentNode.removeChild(this.proxy);
37344         if(removeEl){
37345             this.el.remove();
37346         }
37347     }
37348 });
37349
37350 /**
37351  * @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.
37352  */
37353 Roo.bootstrap.SplitBar.createProxy = function(dir){
37354     var proxy = new Roo.Element(document.createElement("div"));
37355     proxy.unselectable();
37356     var cls = 'roo-splitbar-proxy';
37357     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37358     document.body.appendChild(proxy.dom);
37359     return proxy.dom;
37360 };
37361
37362 /** 
37363  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37364  * Default Adapter. It assumes the splitter and resizing element are not positioned
37365  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37366  */
37367 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37368 };
37369
37370 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37371     // do nothing for now
37372     init : function(s){
37373     
37374     },
37375     /**
37376      * Called before drag operations to get the current size of the resizing element. 
37377      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37378      */
37379      getElementSize : function(s){
37380         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37381             return s.resizingEl.getWidth();
37382         }else{
37383             return s.resizingEl.getHeight();
37384         }
37385     },
37386     
37387     /**
37388      * Called after drag operations to set the size of the resizing element.
37389      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37390      * @param {Number} newSize The new size to set
37391      * @param {Function} onComplete A function to be invoked when resizing is complete
37392      */
37393     setElementSize : function(s, newSize, onComplete){
37394         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37395             if(!s.animate){
37396                 s.resizingEl.setWidth(newSize);
37397                 if(onComplete){
37398                     onComplete(s, newSize);
37399                 }
37400             }else{
37401                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37402             }
37403         }else{
37404             
37405             if(!s.animate){
37406                 s.resizingEl.setHeight(newSize);
37407                 if(onComplete){
37408                     onComplete(s, newSize);
37409                 }
37410             }else{
37411                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37412             }
37413         }
37414     }
37415 };
37416
37417 /** 
37418  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37419  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37420  * Adapter that  moves the splitter element to align with the resized sizing element. 
37421  * Used with an absolute positioned SplitBar.
37422  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37423  * document.body, make sure you assign an id to the body element.
37424  */
37425 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37426     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37427     this.container = Roo.get(container);
37428 };
37429
37430 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37431     init : function(s){
37432         this.basic.init(s);
37433     },
37434     
37435     getElementSize : function(s){
37436         return this.basic.getElementSize(s);
37437     },
37438     
37439     setElementSize : function(s, newSize, onComplete){
37440         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37441     },
37442     
37443     moveSplitter : function(s){
37444         var yes = Roo.bootstrap.SplitBar;
37445         switch(s.placement){
37446             case yes.LEFT:
37447                 s.el.setX(s.resizingEl.getRight());
37448                 break;
37449             case yes.RIGHT:
37450                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37451                 break;
37452             case yes.TOP:
37453                 s.el.setY(s.resizingEl.getBottom());
37454                 break;
37455             case yes.BOTTOM:
37456                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37457                 break;
37458         }
37459     }
37460 };
37461
37462 /**
37463  * Orientation constant - Create a vertical SplitBar
37464  * @static
37465  * @type Number
37466  */
37467 Roo.bootstrap.SplitBar.VERTICAL = 1;
37468
37469 /**
37470  * Orientation constant - Create a horizontal SplitBar
37471  * @static
37472  * @type Number
37473  */
37474 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37475
37476 /**
37477  * Placement constant - The resizing element is to the left of the splitter element
37478  * @static
37479  * @type Number
37480  */
37481 Roo.bootstrap.SplitBar.LEFT = 1;
37482
37483 /**
37484  * Placement constant - The resizing element is to the right of the splitter element
37485  * @static
37486  * @type Number
37487  */
37488 Roo.bootstrap.SplitBar.RIGHT = 2;
37489
37490 /**
37491  * Placement constant - The resizing element is positioned above the splitter element
37492  * @static
37493  * @type Number
37494  */
37495 Roo.bootstrap.SplitBar.TOP = 3;
37496
37497 /**
37498  * Placement constant - The resizing element is positioned under splitter element
37499  * @static
37500  * @type Number
37501  */
37502 Roo.bootstrap.SplitBar.BOTTOM = 4;
37503 Roo.namespace("Roo.bootstrap.layout");/*
37504  * Based on:
37505  * Ext JS Library 1.1.1
37506  * Copyright(c) 2006-2007, Ext JS, LLC.
37507  *
37508  * Originally Released Under LGPL - original licence link has changed is not relivant.
37509  *
37510  * Fork - LGPL
37511  * <script type="text/javascript">
37512  */
37513
37514 /**
37515  * @class Roo.bootstrap.layout.Manager
37516  * @extends Roo.bootstrap.Component
37517  * Base class for layout managers.
37518  */
37519 Roo.bootstrap.layout.Manager = function(config)
37520 {
37521     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37522
37523
37524
37525
37526
37527     /** false to disable window resize monitoring @type Boolean */
37528     this.monitorWindowResize = true;
37529     this.regions = {};
37530     this.addEvents({
37531         /**
37532          * @event layout
37533          * Fires when a layout is performed.
37534          * @param {Roo.LayoutManager} this
37535          */
37536         "layout" : true,
37537         /**
37538          * @event regionresized
37539          * Fires when the user resizes a region.
37540          * @param {Roo.LayoutRegion} region The resized region
37541          * @param {Number} newSize The new size (width for east/west, height for north/south)
37542          */
37543         "regionresized" : true,
37544         /**
37545          * @event regioncollapsed
37546          * Fires when a region is collapsed.
37547          * @param {Roo.LayoutRegion} region The collapsed region
37548          */
37549         "regioncollapsed" : true,
37550         /**
37551          * @event regionexpanded
37552          * Fires when a region is expanded.
37553          * @param {Roo.LayoutRegion} region The expanded region
37554          */
37555         "regionexpanded" : true
37556     });
37557     this.updating = false;
37558
37559     if (config.el) {
37560         this.el = Roo.get(config.el);
37561         this.initEvents();
37562     }
37563
37564 };
37565
37566 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37567
37568
37569     regions : null,
37570
37571     monitorWindowResize : true,
37572
37573
37574     updating : false,
37575
37576
37577     onRender : function(ct, position)
37578     {
37579         if(!this.el){
37580             this.el = Roo.get(ct);
37581             this.initEvents();
37582         }
37583         //this.fireEvent('render',this);
37584     },
37585
37586
37587     initEvents: function()
37588     {
37589
37590
37591         // ie scrollbar fix
37592         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37593             document.body.scroll = "no";
37594         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37595             this.el.position('relative');
37596         }
37597         this.id = this.el.id;
37598         this.el.addClass("roo-layout-container");
37599         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37600         if(this.el.dom != document.body ) {
37601             this.el.on('resize', this.layout,this);
37602             this.el.on('show', this.layout,this);
37603         }
37604
37605     },
37606
37607     /**
37608      * Returns true if this layout is currently being updated
37609      * @return {Boolean}
37610      */
37611     isUpdating : function(){
37612         return this.updating;
37613     },
37614
37615     /**
37616      * Suspend the LayoutManager from doing auto-layouts while
37617      * making multiple add or remove calls
37618      */
37619     beginUpdate : function(){
37620         this.updating = true;
37621     },
37622
37623     /**
37624      * Restore auto-layouts and optionally disable the manager from performing a layout
37625      * @param {Boolean} noLayout true to disable a layout update
37626      */
37627     endUpdate : function(noLayout){
37628         this.updating = false;
37629         if(!noLayout){
37630             this.layout();
37631         }
37632     },
37633
37634     layout: function(){
37635         // abstract...
37636     },
37637
37638     onRegionResized : function(region, newSize){
37639         this.fireEvent("regionresized", region, newSize);
37640         this.layout();
37641     },
37642
37643     onRegionCollapsed : function(region){
37644         this.fireEvent("regioncollapsed", region);
37645     },
37646
37647     onRegionExpanded : function(region){
37648         this.fireEvent("regionexpanded", region);
37649     },
37650
37651     /**
37652      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37653      * performs box-model adjustments.
37654      * @return {Object} The size as an object {width: (the width), height: (the height)}
37655      */
37656     getViewSize : function()
37657     {
37658         var size;
37659         if(this.el.dom != document.body){
37660             size = this.el.getSize();
37661         }else{
37662             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37663         }
37664         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37665         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37666         return size;
37667     },
37668
37669     /**
37670      * Returns the Element this layout is bound to.
37671      * @return {Roo.Element}
37672      */
37673     getEl : function(){
37674         return this.el;
37675     },
37676
37677     /**
37678      * Returns the specified region.
37679      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37680      * @return {Roo.LayoutRegion}
37681      */
37682     getRegion : function(target){
37683         return this.regions[target.toLowerCase()];
37684     },
37685
37686     onWindowResize : function(){
37687         if(this.monitorWindowResize){
37688             this.layout();
37689         }
37690     }
37691 });
37692 /*
37693  * Based on:
37694  * Ext JS Library 1.1.1
37695  * Copyright(c) 2006-2007, Ext JS, LLC.
37696  *
37697  * Originally Released Under LGPL - original licence link has changed is not relivant.
37698  *
37699  * Fork - LGPL
37700  * <script type="text/javascript">
37701  */
37702 /**
37703  * @class Roo.bootstrap.layout.Border
37704  * @extends Roo.bootstrap.layout.Manager
37705  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37706  * please see: examples/bootstrap/nested.html<br><br>
37707  
37708 <b>The container the layout is rendered into can be either the body element or any other element.
37709 If it is not the body element, the container needs to either be an absolute positioned element,
37710 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37711 the container size if it is not the body element.</b>
37712
37713 * @constructor
37714 * Create a new Border
37715 * @param {Object} config Configuration options
37716  */
37717 Roo.bootstrap.layout.Border = function(config){
37718     config = config || {};
37719     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37720     
37721     
37722     
37723     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37724         if(config[region]){
37725             config[region].region = region;
37726             this.addRegion(config[region]);
37727         }
37728     },this);
37729     
37730 };
37731
37732 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37733
37734 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37735     
37736     parent : false, // this might point to a 'nest' or a ???
37737     
37738     /**
37739      * Creates and adds a new region if it doesn't already exist.
37740      * @param {String} target The target region key (north, south, east, west or center).
37741      * @param {Object} config The regions config object
37742      * @return {BorderLayoutRegion} The new region
37743      */
37744     addRegion : function(config)
37745     {
37746         if(!this.regions[config.region]){
37747             var r = this.factory(config);
37748             this.bindRegion(r);
37749         }
37750         return this.regions[config.region];
37751     },
37752
37753     // private (kinda)
37754     bindRegion : function(r){
37755         this.regions[r.config.region] = r;
37756         
37757         r.on("visibilitychange",    this.layout, this);
37758         r.on("paneladded",          this.layout, this);
37759         r.on("panelremoved",        this.layout, this);
37760         r.on("invalidated",         this.layout, this);
37761         r.on("resized",             this.onRegionResized, this);
37762         r.on("collapsed",           this.onRegionCollapsed, this);
37763         r.on("expanded",            this.onRegionExpanded, this);
37764     },
37765
37766     /**
37767      * Performs a layout update.
37768      */
37769     layout : function()
37770     {
37771         if(this.updating) {
37772             return;
37773         }
37774         
37775         // render all the rebions if they have not been done alreayd?
37776         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37777             if(this.regions[region] && !this.regions[region].bodyEl){
37778                 this.regions[region].onRender(this.el)
37779             }
37780         },this);
37781         
37782         var size = this.getViewSize();
37783         var w = size.width;
37784         var h = size.height;
37785         var centerW = w;
37786         var centerH = h;
37787         var centerY = 0;
37788         var centerX = 0;
37789         //var x = 0, y = 0;
37790
37791         var rs = this.regions;
37792         var north = rs["north"];
37793         var south = rs["south"]; 
37794         var west = rs["west"];
37795         var east = rs["east"];
37796         var center = rs["center"];
37797         //if(this.hideOnLayout){ // not supported anymore
37798             //c.el.setStyle("display", "none");
37799         //}
37800         if(north && north.isVisible()){
37801             var b = north.getBox();
37802             var m = north.getMargins();
37803             b.width = w - (m.left+m.right);
37804             b.x = m.left;
37805             b.y = m.top;
37806             centerY = b.height + b.y + m.bottom;
37807             centerH -= centerY;
37808             north.updateBox(this.safeBox(b));
37809         }
37810         if(south && south.isVisible()){
37811             var b = south.getBox();
37812             var m = south.getMargins();
37813             b.width = w - (m.left+m.right);
37814             b.x = m.left;
37815             var totalHeight = (b.height + m.top + m.bottom);
37816             b.y = h - totalHeight + m.top;
37817             centerH -= totalHeight;
37818             south.updateBox(this.safeBox(b));
37819         }
37820         if(west && west.isVisible()){
37821             var b = west.getBox();
37822             var m = west.getMargins();
37823             b.height = centerH - (m.top+m.bottom);
37824             b.x = m.left;
37825             b.y = centerY + m.top;
37826             var totalWidth = (b.width + m.left + m.right);
37827             centerX += totalWidth;
37828             centerW -= totalWidth;
37829             west.updateBox(this.safeBox(b));
37830         }
37831         if(east && east.isVisible()){
37832             var b = east.getBox();
37833             var m = east.getMargins();
37834             b.height = centerH - (m.top+m.bottom);
37835             var totalWidth = (b.width + m.left + m.right);
37836             b.x = w - totalWidth + m.left;
37837             b.y = centerY + m.top;
37838             centerW -= totalWidth;
37839             east.updateBox(this.safeBox(b));
37840         }
37841         if(center){
37842             var m = center.getMargins();
37843             var centerBox = {
37844                 x: centerX + m.left,
37845                 y: centerY + m.top,
37846                 width: centerW - (m.left+m.right),
37847                 height: centerH - (m.top+m.bottom)
37848             };
37849             //if(this.hideOnLayout){
37850                 //center.el.setStyle("display", "block");
37851             //}
37852             center.updateBox(this.safeBox(centerBox));
37853         }
37854         this.el.repaint();
37855         this.fireEvent("layout", this);
37856     },
37857
37858     // private
37859     safeBox : function(box){
37860         box.width = Math.max(0, box.width);
37861         box.height = Math.max(0, box.height);
37862         return box;
37863     },
37864
37865     /**
37866      * Adds a ContentPanel (or subclass) to this layout.
37867      * @param {String} target The target region key (north, south, east, west or center).
37868      * @param {Roo.ContentPanel} panel The panel to add
37869      * @return {Roo.ContentPanel} The added panel
37870      */
37871     add : function(target, panel){
37872          
37873         target = target.toLowerCase();
37874         return this.regions[target].add(panel);
37875     },
37876
37877     /**
37878      * Remove a ContentPanel (or subclass) to this layout.
37879      * @param {String} target The target region key (north, south, east, west or center).
37880      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37881      * @return {Roo.ContentPanel} The removed panel
37882      */
37883     remove : function(target, panel){
37884         target = target.toLowerCase();
37885         return this.regions[target].remove(panel);
37886     },
37887
37888     /**
37889      * Searches all regions for a panel with the specified id
37890      * @param {String} panelId
37891      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37892      */
37893     findPanel : function(panelId){
37894         var rs = this.regions;
37895         for(var target in rs){
37896             if(typeof rs[target] != "function"){
37897                 var p = rs[target].getPanel(panelId);
37898                 if(p){
37899                     return p;
37900                 }
37901             }
37902         }
37903         return null;
37904     },
37905
37906     /**
37907      * Searches all regions for a panel with the specified id and activates (shows) it.
37908      * @param {String/ContentPanel} panelId The panels id or the panel itself
37909      * @return {Roo.ContentPanel} The shown panel or null
37910      */
37911     showPanel : function(panelId) {
37912       var rs = this.regions;
37913       for(var target in rs){
37914          var r = rs[target];
37915          if(typeof r != "function"){
37916             if(r.hasPanel(panelId)){
37917                return r.showPanel(panelId);
37918             }
37919          }
37920       }
37921       return null;
37922    },
37923
37924    /**
37925      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37926      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37927      */
37928    /*
37929     restoreState : function(provider){
37930         if(!provider){
37931             provider = Roo.state.Manager;
37932         }
37933         var sm = new Roo.LayoutStateManager();
37934         sm.init(this, provider);
37935     },
37936 */
37937  
37938  
37939     /**
37940      * Adds a xtype elements to the layout.
37941      * <pre><code>
37942
37943 layout.addxtype({
37944        xtype : 'ContentPanel',
37945        region: 'west',
37946        items: [ .... ]
37947    }
37948 );
37949
37950 layout.addxtype({
37951         xtype : 'NestedLayoutPanel',
37952         region: 'west',
37953         layout: {
37954            center: { },
37955            west: { }   
37956         },
37957         items : [ ... list of content panels or nested layout panels.. ]
37958    }
37959 );
37960 </code></pre>
37961      * @param {Object} cfg Xtype definition of item to add.
37962      */
37963     addxtype : function(cfg)
37964     {
37965         // basically accepts a pannel...
37966         // can accept a layout region..!?!?
37967         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37968         
37969         
37970         // theory?  children can only be panels??
37971         
37972         //if (!cfg.xtype.match(/Panel$/)) {
37973         //    return false;
37974         //}
37975         var ret = false;
37976         
37977         if (typeof(cfg.region) == 'undefined') {
37978             Roo.log("Failed to add Panel, region was not set");
37979             Roo.log(cfg);
37980             return false;
37981         }
37982         var region = cfg.region;
37983         delete cfg.region;
37984         
37985           
37986         var xitems = [];
37987         if (cfg.items) {
37988             xitems = cfg.items;
37989             delete cfg.items;
37990         }
37991         var nb = false;
37992         
37993         if ( region == 'center') {
37994             Roo.log("Center: " + cfg.title);
37995         }
37996         
37997         
37998         switch(cfg.xtype) 
37999         {
38000             case 'Content':  // ContentPanel (el, cfg)
38001             case 'Scroll':  // ContentPanel (el, cfg)
38002             case 'View': 
38003                 cfg.autoCreate = cfg.autoCreate || true;
38004                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38005                 //} else {
38006                 //    var el = this.el.createChild();
38007                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38008                 //}
38009                 
38010                 this.add(region, ret);
38011                 break;
38012             
38013             /*
38014             case 'TreePanel': // our new panel!
38015                 cfg.el = this.el.createChild();
38016                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38017                 this.add(region, ret);
38018                 break;
38019             */
38020             
38021             case 'Nest': 
38022                 // create a new Layout (which is  a Border Layout...
38023                 
38024                 var clayout = cfg.layout;
38025                 clayout.el  = this.el.createChild();
38026                 clayout.items   = clayout.items  || [];
38027                 
38028                 delete cfg.layout;
38029                 
38030                 // replace this exitems with the clayout ones..
38031                 xitems = clayout.items;
38032                  
38033                 // force background off if it's in center...
38034                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38035                     cfg.background = false;
38036                 }
38037                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38038                 
38039                 
38040                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38041                 //console.log('adding nested layout panel '  + cfg.toSource());
38042                 this.add(region, ret);
38043                 nb = {}; /// find first...
38044                 break;
38045             
38046             case 'Grid':
38047                 
38048                 // needs grid and region
38049                 
38050                 //var el = this.getRegion(region).el.createChild();
38051                 /*
38052                  *var el = this.el.createChild();
38053                 // create the grid first...
38054                 cfg.grid.container = el;
38055                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38056                 */
38057                 
38058                 if (region == 'center' && this.active ) {
38059                     cfg.background = false;
38060                 }
38061                 
38062                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38063                 
38064                 this.add(region, ret);
38065                 /*
38066                 if (cfg.background) {
38067                     // render grid on panel activation (if panel background)
38068                     ret.on('activate', function(gp) {
38069                         if (!gp.grid.rendered) {
38070                     //        gp.grid.render(el);
38071                         }
38072                     });
38073                 } else {
38074                   //  cfg.grid.render(el);
38075                 }
38076                 */
38077                 break;
38078            
38079            
38080             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38081                 // it was the old xcomponent building that caused this before.
38082                 // espeically if border is the top element in the tree.
38083                 ret = this;
38084                 break; 
38085                 
38086                     
38087                 
38088                 
38089                 
38090             default:
38091                 /*
38092                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38093                     
38094                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38095                     this.add(region, ret);
38096                 } else {
38097                 */
38098                     Roo.log(cfg);
38099                     throw "Can not add '" + cfg.xtype + "' to Border";
38100                     return null;
38101              
38102                                 
38103              
38104         }
38105         this.beginUpdate();
38106         // add children..
38107         var region = '';
38108         var abn = {};
38109         Roo.each(xitems, function(i)  {
38110             region = nb && i.region ? i.region : false;
38111             
38112             var add = ret.addxtype(i);
38113            
38114             if (region) {
38115                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38116                 if (!i.background) {
38117                     abn[region] = nb[region] ;
38118                 }
38119             }
38120             
38121         });
38122         this.endUpdate();
38123
38124         // make the last non-background panel active..
38125         //if (nb) { Roo.log(abn); }
38126         if (nb) {
38127             
38128             for(var r in abn) {
38129                 region = this.getRegion(r);
38130                 if (region) {
38131                     // tried using nb[r], but it does not work..
38132                      
38133                     region.showPanel(abn[r]);
38134                    
38135                 }
38136             }
38137         }
38138         return ret;
38139         
38140     },
38141     
38142     
38143 // private
38144     factory : function(cfg)
38145     {
38146         
38147         var validRegions = Roo.bootstrap.layout.Border.regions;
38148
38149         var target = cfg.region;
38150         cfg.mgr = this;
38151         
38152         var r = Roo.bootstrap.layout;
38153         Roo.log(target);
38154         switch(target){
38155             case "north":
38156                 return new r.North(cfg);
38157             case "south":
38158                 return new r.South(cfg);
38159             case "east":
38160                 return new r.East(cfg);
38161             case "west":
38162                 return new r.West(cfg);
38163             case "center":
38164                 return new r.Center(cfg);
38165         }
38166         throw 'Layout region "'+target+'" not supported.';
38167     }
38168     
38169     
38170 });
38171  /*
38172  * Based on:
38173  * Ext JS Library 1.1.1
38174  * Copyright(c) 2006-2007, Ext JS, LLC.
38175  *
38176  * Originally Released Under LGPL - original licence link has changed is not relivant.
38177  *
38178  * Fork - LGPL
38179  * <script type="text/javascript">
38180  */
38181  
38182 /**
38183  * @class Roo.bootstrap.layout.Basic
38184  * @extends Roo.util.Observable
38185  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38186  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38187  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38188  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38189  * @cfg {string}   region  the region that it inhabits..
38190  * @cfg {bool}   skipConfig skip config?
38191  * 
38192
38193  */
38194 Roo.bootstrap.layout.Basic = function(config){
38195     
38196     this.mgr = config.mgr;
38197     
38198     this.position = config.region;
38199     
38200     var skipConfig = config.skipConfig;
38201     
38202     this.events = {
38203         /**
38204          * @scope Roo.BasicLayoutRegion
38205          */
38206         
38207         /**
38208          * @event beforeremove
38209          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38210          * @param {Roo.LayoutRegion} this
38211          * @param {Roo.ContentPanel} panel The panel
38212          * @param {Object} e The cancel event object
38213          */
38214         "beforeremove" : true,
38215         /**
38216          * @event invalidated
38217          * Fires when the layout for this region is changed.
38218          * @param {Roo.LayoutRegion} this
38219          */
38220         "invalidated" : true,
38221         /**
38222          * @event visibilitychange
38223          * Fires when this region is shown or hidden 
38224          * @param {Roo.LayoutRegion} this
38225          * @param {Boolean} visibility true or false
38226          */
38227         "visibilitychange" : true,
38228         /**
38229          * @event paneladded
38230          * Fires when a panel is added. 
38231          * @param {Roo.LayoutRegion} this
38232          * @param {Roo.ContentPanel} panel The panel
38233          */
38234         "paneladded" : true,
38235         /**
38236          * @event panelremoved
38237          * Fires when a panel is removed. 
38238          * @param {Roo.LayoutRegion} this
38239          * @param {Roo.ContentPanel} panel The panel
38240          */
38241         "panelremoved" : true,
38242         /**
38243          * @event beforecollapse
38244          * Fires when this region before collapse.
38245          * @param {Roo.LayoutRegion} this
38246          */
38247         "beforecollapse" : true,
38248         /**
38249          * @event collapsed
38250          * Fires when this region is collapsed.
38251          * @param {Roo.LayoutRegion} this
38252          */
38253         "collapsed" : true,
38254         /**
38255          * @event expanded
38256          * Fires when this region is expanded.
38257          * @param {Roo.LayoutRegion} this
38258          */
38259         "expanded" : true,
38260         /**
38261          * @event slideshow
38262          * Fires when this region is slid into view.
38263          * @param {Roo.LayoutRegion} this
38264          */
38265         "slideshow" : true,
38266         /**
38267          * @event slidehide
38268          * Fires when this region slides out of view. 
38269          * @param {Roo.LayoutRegion} this
38270          */
38271         "slidehide" : true,
38272         /**
38273          * @event panelactivated
38274          * Fires when a panel is activated. 
38275          * @param {Roo.LayoutRegion} this
38276          * @param {Roo.ContentPanel} panel The activated panel
38277          */
38278         "panelactivated" : true,
38279         /**
38280          * @event resized
38281          * Fires when the user resizes this region. 
38282          * @param {Roo.LayoutRegion} this
38283          * @param {Number} newSize The new size (width for east/west, height for north/south)
38284          */
38285         "resized" : true
38286     };
38287     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38288     this.panels = new Roo.util.MixedCollection();
38289     this.panels.getKey = this.getPanelId.createDelegate(this);
38290     this.box = null;
38291     this.activePanel = null;
38292     // ensure listeners are added...
38293     
38294     if (config.listeners || config.events) {
38295         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38296             listeners : config.listeners || {},
38297             events : config.events || {}
38298         });
38299     }
38300     
38301     if(skipConfig !== true){
38302         this.applyConfig(config);
38303     }
38304 };
38305
38306 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38307 {
38308     getPanelId : function(p){
38309         return p.getId();
38310     },
38311     
38312     applyConfig : function(config){
38313         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38314         this.config = config;
38315         
38316     },
38317     
38318     /**
38319      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38320      * the width, for horizontal (north, south) the height.
38321      * @param {Number} newSize The new width or height
38322      */
38323     resizeTo : function(newSize){
38324         var el = this.el ? this.el :
38325                  (this.activePanel ? this.activePanel.getEl() : null);
38326         if(el){
38327             switch(this.position){
38328                 case "east":
38329                 case "west":
38330                     el.setWidth(newSize);
38331                     this.fireEvent("resized", this, newSize);
38332                 break;
38333                 case "north":
38334                 case "south":
38335                     el.setHeight(newSize);
38336                     this.fireEvent("resized", this, newSize);
38337                 break;                
38338             }
38339         }
38340     },
38341     
38342     getBox : function(){
38343         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38344     },
38345     
38346     getMargins : function(){
38347         return this.margins;
38348     },
38349     
38350     updateBox : function(box){
38351         this.box = box;
38352         var el = this.activePanel.getEl();
38353         el.dom.style.left = box.x + "px";
38354         el.dom.style.top = box.y + "px";
38355         this.activePanel.setSize(box.width, box.height);
38356     },
38357     
38358     /**
38359      * Returns the container element for this region.
38360      * @return {Roo.Element}
38361      */
38362     getEl : function(){
38363         return this.activePanel;
38364     },
38365     
38366     /**
38367      * Returns true if this region is currently visible.
38368      * @return {Boolean}
38369      */
38370     isVisible : function(){
38371         return this.activePanel ? true : false;
38372     },
38373     
38374     setActivePanel : function(panel){
38375         panel = this.getPanel(panel);
38376         if(this.activePanel && this.activePanel != panel){
38377             this.activePanel.setActiveState(false);
38378             this.activePanel.getEl().setLeftTop(-10000,-10000);
38379         }
38380         this.activePanel = panel;
38381         panel.setActiveState(true);
38382         if(this.box){
38383             panel.setSize(this.box.width, this.box.height);
38384         }
38385         this.fireEvent("panelactivated", this, panel);
38386         this.fireEvent("invalidated");
38387     },
38388     
38389     /**
38390      * Show the specified panel.
38391      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38392      * @return {Roo.ContentPanel} The shown panel or null
38393      */
38394     showPanel : function(panel){
38395         panel = this.getPanel(panel);
38396         if(panel){
38397             this.setActivePanel(panel);
38398         }
38399         return panel;
38400     },
38401     
38402     /**
38403      * Get the active panel for this region.
38404      * @return {Roo.ContentPanel} The active panel or null
38405      */
38406     getActivePanel : function(){
38407         return this.activePanel;
38408     },
38409     
38410     /**
38411      * Add the passed ContentPanel(s)
38412      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38413      * @return {Roo.ContentPanel} The panel added (if only one was added)
38414      */
38415     add : function(panel){
38416         if(arguments.length > 1){
38417             for(var i = 0, len = arguments.length; i < len; i++) {
38418                 this.add(arguments[i]);
38419             }
38420             return null;
38421         }
38422         if(this.hasPanel(panel)){
38423             this.showPanel(panel);
38424             return panel;
38425         }
38426         var el = panel.getEl();
38427         if(el.dom.parentNode != this.mgr.el.dom){
38428             this.mgr.el.dom.appendChild(el.dom);
38429         }
38430         if(panel.setRegion){
38431             panel.setRegion(this);
38432         }
38433         this.panels.add(panel);
38434         el.setStyle("position", "absolute");
38435         if(!panel.background){
38436             this.setActivePanel(panel);
38437             if(this.config.initialSize && this.panels.getCount()==1){
38438                 this.resizeTo(this.config.initialSize);
38439             }
38440         }
38441         this.fireEvent("paneladded", this, panel);
38442         return panel;
38443     },
38444     
38445     /**
38446      * Returns true if the panel is in this region.
38447      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38448      * @return {Boolean}
38449      */
38450     hasPanel : function(panel){
38451         if(typeof panel == "object"){ // must be panel obj
38452             panel = panel.getId();
38453         }
38454         return this.getPanel(panel) ? true : false;
38455     },
38456     
38457     /**
38458      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38459      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38460      * @param {Boolean} preservePanel Overrides the config preservePanel option
38461      * @return {Roo.ContentPanel} The panel that was removed
38462      */
38463     remove : function(panel, preservePanel){
38464         panel = this.getPanel(panel);
38465         if(!panel){
38466             return null;
38467         }
38468         var e = {};
38469         this.fireEvent("beforeremove", this, panel, e);
38470         if(e.cancel === true){
38471             return null;
38472         }
38473         var panelId = panel.getId();
38474         this.panels.removeKey(panelId);
38475         return panel;
38476     },
38477     
38478     /**
38479      * Returns the panel specified or null if it's not in this region.
38480      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38481      * @return {Roo.ContentPanel}
38482      */
38483     getPanel : function(id){
38484         if(typeof id == "object"){ // must be panel obj
38485             return id;
38486         }
38487         return this.panels.get(id);
38488     },
38489     
38490     /**
38491      * Returns this regions position (north/south/east/west/center).
38492      * @return {String} 
38493      */
38494     getPosition: function(){
38495         return this.position;    
38496     }
38497 });/*
38498  * Based on:
38499  * Ext JS Library 1.1.1
38500  * Copyright(c) 2006-2007, Ext JS, LLC.
38501  *
38502  * Originally Released Under LGPL - original licence link has changed is not relivant.
38503  *
38504  * Fork - LGPL
38505  * <script type="text/javascript">
38506  */
38507  
38508 /**
38509  * @class Roo.bootstrap.layout.Region
38510  * @extends Roo.bootstrap.layout.Basic
38511  * This class represents a region in a layout manager.
38512  
38513  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38514  * @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})
38515  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38516  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38517  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38518  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38519  * @cfg {String}    title           The title for the region (overrides panel titles)
38520  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38521  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38522  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38523  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38524  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38525  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38526  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38527  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38528  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38529  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38530
38531  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38532  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38533  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38534  * @cfg {Number}    width           For East/West panels
38535  * @cfg {Number}    height          For North/South panels
38536  * @cfg {Boolean}   split           To show the splitter
38537  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38538  * 
38539  * @cfg {string}   cls             Extra CSS classes to add to region
38540  * 
38541  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38542  * @cfg {string}   region  the region that it inhabits..
38543  *
38544
38545  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38546  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38547
38548  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38549  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38550  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38551  */
38552 Roo.bootstrap.layout.Region = function(config)
38553 {
38554     this.applyConfig(config);
38555
38556     var mgr = config.mgr;
38557     var pos = config.region;
38558     config.skipConfig = true;
38559     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38560     
38561     if (mgr.el) {
38562         this.onRender(mgr.el);   
38563     }
38564      
38565     this.visible = true;
38566     this.collapsed = false;
38567     this.unrendered_panels = [];
38568 };
38569
38570 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38571
38572     position: '', // set by wrapper (eg. north/south etc..)
38573     unrendered_panels : null,  // unrendered panels.
38574     
38575     tabPosition : false,
38576     
38577     mgr: false, // points to 'Border'
38578     
38579     
38580     createBody : function(){
38581         /** This region's body element 
38582         * @type Roo.Element */
38583         this.bodyEl = this.el.createChild({
38584                 tag: "div",
38585                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38586         });
38587     },
38588
38589     onRender: function(ctr, pos)
38590     {
38591         var dh = Roo.DomHelper;
38592         /** This region's container element 
38593         * @type Roo.Element */
38594         this.el = dh.append(ctr.dom, {
38595                 tag: "div",
38596                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38597             }, true);
38598         /** This region's title element 
38599         * @type Roo.Element */
38600     
38601         this.titleEl = dh.append(this.el.dom,  {
38602                 tag: "div",
38603                 unselectable: "on",
38604                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38605                 children:[
38606                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38607                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38608                 ]
38609             }, true);
38610         
38611         this.titleEl.enableDisplayMode();
38612         /** This region's title text element 
38613         * @type HTMLElement */
38614         this.titleTextEl = this.titleEl.dom.firstChild;
38615         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38616         /*
38617         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38618         this.closeBtn.enableDisplayMode();
38619         this.closeBtn.on("click", this.closeClicked, this);
38620         this.closeBtn.hide();
38621     */
38622         this.createBody(this.config);
38623         if(this.config.hideWhenEmpty){
38624             this.hide();
38625             this.on("paneladded", this.validateVisibility, this);
38626             this.on("panelremoved", this.validateVisibility, this);
38627         }
38628         if(this.autoScroll){
38629             this.bodyEl.setStyle("overflow", "auto");
38630         }else{
38631             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38632         }
38633         //if(c.titlebar !== false){
38634             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38635                 this.titleEl.hide();
38636             }else{
38637                 this.titleEl.show();
38638                 if(this.config.title){
38639                     this.titleTextEl.innerHTML = this.config.title;
38640                 }
38641             }
38642         //}
38643         if(this.config.collapsed){
38644             this.collapse(true);
38645         }
38646         if(this.config.hidden){
38647             this.hide();
38648         }
38649         
38650         if (this.unrendered_panels && this.unrendered_panels.length) {
38651             for (var i =0;i< this.unrendered_panels.length; i++) {
38652                 this.add(this.unrendered_panels[i]);
38653             }
38654             this.unrendered_panels = null;
38655             
38656         }
38657         
38658     },
38659     
38660     applyConfig : function(c)
38661     {
38662         /*
38663          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38664             var dh = Roo.DomHelper;
38665             if(c.titlebar !== false){
38666                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38667                 this.collapseBtn.on("click", this.collapse, this);
38668                 this.collapseBtn.enableDisplayMode();
38669                 /*
38670                 if(c.showPin === true || this.showPin){
38671                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38672                     this.stickBtn.enableDisplayMode();
38673                     this.stickBtn.on("click", this.expand, this);
38674                     this.stickBtn.hide();
38675                 }
38676                 
38677             }
38678             */
38679             /** This region's collapsed element
38680             * @type Roo.Element */
38681             /*
38682              *
38683             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38684                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38685             ]}, true);
38686             
38687             if(c.floatable !== false){
38688                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38689                this.collapsedEl.on("click", this.collapseClick, this);
38690             }
38691
38692             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38693                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38694                    id: "message", unselectable: "on", style:{"float":"left"}});
38695                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38696              }
38697             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38698             this.expandBtn.on("click", this.expand, this);
38699             
38700         }
38701         
38702         if(this.collapseBtn){
38703             this.collapseBtn.setVisible(c.collapsible == true);
38704         }
38705         
38706         this.cmargins = c.cmargins || this.cmargins ||
38707                          (this.position == "west" || this.position == "east" ?
38708                              {top: 0, left: 2, right:2, bottom: 0} :
38709                              {top: 2, left: 0, right:0, bottom: 2});
38710         */
38711         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38712         
38713         
38714         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38715         
38716         this.autoScroll = c.autoScroll || false;
38717         
38718         
38719        
38720         
38721         this.duration = c.duration || .30;
38722         this.slideDuration = c.slideDuration || .45;
38723         this.config = c;
38724        
38725     },
38726     /**
38727      * Returns true if this region is currently visible.
38728      * @return {Boolean}
38729      */
38730     isVisible : function(){
38731         return this.visible;
38732     },
38733
38734     /**
38735      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38736      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38737      */
38738     //setCollapsedTitle : function(title){
38739     //    title = title || "&#160;";
38740      //   if(this.collapsedTitleTextEl){
38741       //      this.collapsedTitleTextEl.innerHTML = title;
38742        // }
38743     //},
38744
38745     getBox : function(){
38746         var b;
38747       //  if(!this.collapsed){
38748             b = this.el.getBox(false, true);
38749        // }else{
38750           //  b = this.collapsedEl.getBox(false, true);
38751         //}
38752         return b;
38753     },
38754
38755     getMargins : function(){
38756         return this.margins;
38757         //return this.collapsed ? this.cmargins : this.margins;
38758     },
38759 /*
38760     highlight : function(){
38761         this.el.addClass("x-layout-panel-dragover");
38762     },
38763
38764     unhighlight : function(){
38765         this.el.removeClass("x-layout-panel-dragover");
38766     },
38767 */
38768     updateBox : function(box)
38769     {
38770         if (!this.bodyEl) {
38771             return; // not rendered yet..
38772         }
38773         
38774         this.box = box;
38775         if(!this.collapsed){
38776             this.el.dom.style.left = box.x + "px";
38777             this.el.dom.style.top = box.y + "px";
38778             this.updateBody(box.width, box.height);
38779         }else{
38780             this.collapsedEl.dom.style.left = box.x + "px";
38781             this.collapsedEl.dom.style.top = box.y + "px";
38782             this.collapsedEl.setSize(box.width, box.height);
38783         }
38784         if(this.tabs){
38785             this.tabs.autoSizeTabs();
38786         }
38787     },
38788
38789     updateBody : function(w, h)
38790     {
38791         if(w !== null){
38792             this.el.setWidth(w);
38793             w -= this.el.getBorderWidth("rl");
38794             if(this.config.adjustments){
38795                 w += this.config.adjustments[0];
38796             }
38797         }
38798         if(h !== null && h > 0){
38799             this.el.setHeight(h);
38800             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38801             h -= this.el.getBorderWidth("tb");
38802             if(this.config.adjustments){
38803                 h += this.config.adjustments[1];
38804             }
38805             this.bodyEl.setHeight(h);
38806             if(this.tabs){
38807                 h = this.tabs.syncHeight(h);
38808             }
38809         }
38810         if(this.panelSize){
38811             w = w !== null ? w : this.panelSize.width;
38812             h = h !== null ? h : this.panelSize.height;
38813         }
38814         if(this.activePanel){
38815             var el = this.activePanel.getEl();
38816             w = w !== null ? w : el.getWidth();
38817             h = h !== null ? h : el.getHeight();
38818             this.panelSize = {width: w, height: h};
38819             this.activePanel.setSize(w, h);
38820         }
38821         if(Roo.isIE && this.tabs){
38822             this.tabs.el.repaint();
38823         }
38824     },
38825
38826     /**
38827      * Returns the container element for this region.
38828      * @return {Roo.Element}
38829      */
38830     getEl : function(){
38831         return this.el;
38832     },
38833
38834     /**
38835      * Hides this region.
38836      */
38837     hide : function(){
38838         //if(!this.collapsed){
38839             this.el.dom.style.left = "-2000px";
38840             this.el.hide();
38841         //}else{
38842          //   this.collapsedEl.dom.style.left = "-2000px";
38843          //   this.collapsedEl.hide();
38844        // }
38845         this.visible = false;
38846         this.fireEvent("visibilitychange", this, false);
38847     },
38848
38849     /**
38850      * Shows this region if it was previously hidden.
38851      */
38852     show : function(){
38853         //if(!this.collapsed){
38854             this.el.show();
38855         //}else{
38856         //    this.collapsedEl.show();
38857        // }
38858         this.visible = true;
38859         this.fireEvent("visibilitychange", this, true);
38860     },
38861 /*
38862     closeClicked : function(){
38863         if(this.activePanel){
38864             this.remove(this.activePanel);
38865         }
38866     },
38867
38868     collapseClick : function(e){
38869         if(this.isSlid){
38870            e.stopPropagation();
38871            this.slideIn();
38872         }else{
38873            e.stopPropagation();
38874            this.slideOut();
38875         }
38876     },
38877 */
38878     /**
38879      * Collapses this region.
38880      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38881      */
38882     /*
38883     collapse : function(skipAnim, skipCheck = false){
38884         if(this.collapsed) {
38885             return;
38886         }
38887         
38888         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38889             
38890             this.collapsed = true;
38891             if(this.split){
38892                 this.split.el.hide();
38893             }
38894             if(this.config.animate && skipAnim !== true){
38895                 this.fireEvent("invalidated", this);
38896                 this.animateCollapse();
38897             }else{
38898                 this.el.setLocation(-20000,-20000);
38899                 this.el.hide();
38900                 this.collapsedEl.show();
38901                 this.fireEvent("collapsed", this);
38902                 this.fireEvent("invalidated", this);
38903             }
38904         }
38905         
38906     },
38907 */
38908     animateCollapse : function(){
38909         // overridden
38910     },
38911
38912     /**
38913      * Expands this region if it was previously collapsed.
38914      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38915      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38916      */
38917     /*
38918     expand : function(e, skipAnim){
38919         if(e) {
38920             e.stopPropagation();
38921         }
38922         if(!this.collapsed || this.el.hasActiveFx()) {
38923             return;
38924         }
38925         if(this.isSlid){
38926             this.afterSlideIn();
38927             skipAnim = true;
38928         }
38929         this.collapsed = false;
38930         if(this.config.animate && skipAnim !== true){
38931             this.animateExpand();
38932         }else{
38933             this.el.show();
38934             if(this.split){
38935                 this.split.el.show();
38936             }
38937             this.collapsedEl.setLocation(-2000,-2000);
38938             this.collapsedEl.hide();
38939             this.fireEvent("invalidated", this);
38940             this.fireEvent("expanded", this);
38941         }
38942     },
38943 */
38944     animateExpand : function(){
38945         // overridden
38946     },
38947
38948     initTabs : function()
38949     {
38950         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38951         
38952         var ts = new Roo.bootstrap.panel.Tabs({
38953             el: this.bodyEl.dom,
38954             region : this,
38955             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38956             disableTooltips: this.config.disableTabTips,
38957             toolbar : this.config.toolbar
38958         });
38959         
38960         if(this.config.hideTabs){
38961             ts.stripWrap.setDisplayed(false);
38962         }
38963         this.tabs = ts;
38964         ts.resizeTabs = this.config.resizeTabs === true;
38965         ts.minTabWidth = this.config.minTabWidth || 40;
38966         ts.maxTabWidth = this.config.maxTabWidth || 250;
38967         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38968         ts.monitorResize = false;
38969         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38970         ts.bodyEl.addClass('roo-layout-tabs-body');
38971         this.panels.each(this.initPanelAsTab, this);
38972     },
38973
38974     initPanelAsTab : function(panel){
38975         var ti = this.tabs.addTab(
38976             panel.getEl().id,
38977             panel.getTitle(),
38978             null,
38979             this.config.closeOnTab && panel.isClosable(),
38980             panel.tpl
38981         );
38982         if(panel.tabTip !== undefined){
38983             ti.setTooltip(panel.tabTip);
38984         }
38985         ti.on("activate", function(){
38986               this.setActivePanel(panel);
38987         }, this);
38988         
38989         if(this.config.closeOnTab){
38990             ti.on("beforeclose", function(t, e){
38991                 e.cancel = true;
38992                 this.remove(panel);
38993             }, this);
38994         }
38995         
38996         panel.tabItem = ti;
38997         
38998         return ti;
38999     },
39000
39001     updatePanelTitle : function(panel, title)
39002     {
39003         if(this.activePanel == panel){
39004             this.updateTitle(title);
39005         }
39006         if(this.tabs){
39007             var ti = this.tabs.getTab(panel.getEl().id);
39008             ti.setText(title);
39009             if(panel.tabTip !== undefined){
39010                 ti.setTooltip(panel.tabTip);
39011             }
39012         }
39013     },
39014
39015     updateTitle : function(title){
39016         if(this.titleTextEl && !this.config.title){
39017             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39018         }
39019     },
39020
39021     setActivePanel : function(panel)
39022     {
39023         panel = this.getPanel(panel);
39024         if(this.activePanel && this.activePanel != panel){
39025             if(this.activePanel.setActiveState(false) === false){
39026                 return;
39027             }
39028         }
39029         this.activePanel = panel;
39030         panel.setActiveState(true);
39031         if(this.panelSize){
39032             panel.setSize(this.panelSize.width, this.panelSize.height);
39033         }
39034         if(this.closeBtn){
39035             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39036         }
39037         this.updateTitle(panel.getTitle());
39038         if(this.tabs){
39039             this.fireEvent("invalidated", this);
39040         }
39041         this.fireEvent("panelactivated", this, panel);
39042     },
39043
39044     /**
39045      * Shows the specified panel.
39046      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39047      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39048      */
39049     showPanel : function(panel)
39050     {
39051         panel = this.getPanel(panel);
39052         if(panel){
39053             if(this.tabs){
39054                 var tab = this.tabs.getTab(panel.getEl().id);
39055                 if(tab.isHidden()){
39056                     this.tabs.unhideTab(tab.id);
39057                 }
39058                 tab.activate();
39059             }else{
39060                 this.setActivePanel(panel);
39061             }
39062         }
39063         return panel;
39064     },
39065
39066     /**
39067      * Get the active panel for this region.
39068      * @return {Roo.ContentPanel} The active panel or null
39069      */
39070     getActivePanel : function(){
39071         return this.activePanel;
39072     },
39073
39074     validateVisibility : function(){
39075         if(this.panels.getCount() < 1){
39076             this.updateTitle("&#160;");
39077             this.closeBtn.hide();
39078             this.hide();
39079         }else{
39080             if(!this.isVisible()){
39081                 this.show();
39082             }
39083         }
39084     },
39085
39086     /**
39087      * Adds the passed ContentPanel(s) to this region.
39088      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39089      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39090      */
39091     add : function(panel)
39092     {
39093         if(arguments.length > 1){
39094             for(var i = 0, len = arguments.length; i < len; i++) {
39095                 this.add(arguments[i]);
39096             }
39097             return null;
39098         }
39099         
39100         // if we have not been rendered yet, then we can not really do much of this..
39101         if (!this.bodyEl) {
39102             this.unrendered_panels.push(panel);
39103             return panel;
39104         }
39105         
39106         
39107         
39108         
39109         if(this.hasPanel(panel)){
39110             this.showPanel(panel);
39111             return panel;
39112         }
39113         panel.setRegion(this);
39114         this.panels.add(panel);
39115        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39116             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39117             // and hide them... ???
39118             this.bodyEl.dom.appendChild(panel.getEl().dom);
39119             if(panel.background !== true){
39120                 this.setActivePanel(panel);
39121             }
39122             this.fireEvent("paneladded", this, panel);
39123             return panel;
39124         }
39125         */
39126         if(!this.tabs){
39127             this.initTabs();
39128         }else{
39129             this.initPanelAsTab(panel);
39130         }
39131         
39132         
39133         if(panel.background !== true){
39134             this.tabs.activate(panel.getEl().id);
39135         }
39136         this.fireEvent("paneladded", this, panel);
39137         return panel;
39138     },
39139
39140     /**
39141      * Hides the tab for the specified panel.
39142      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39143      */
39144     hidePanel : function(panel){
39145         if(this.tabs && (panel = this.getPanel(panel))){
39146             this.tabs.hideTab(panel.getEl().id);
39147         }
39148     },
39149
39150     /**
39151      * Unhides the tab for a previously hidden panel.
39152      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39153      */
39154     unhidePanel : function(panel){
39155         if(this.tabs && (panel = this.getPanel(panel))){
39156             this.tabs.unhideTab(panel.getEl().id);
39157         }
39158     },
39159
39160     clearPanels : function(){
39161         while(this.panels.getCount() > 0){
39162              this.remove(this.panels.first());
39163         }
39164     },
39165
39166     /**
39167      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39168      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39169      * @param {Boolean} preservePanel Overrides the config preservePanel option
39170      * @return {Roo.ContentPanel} The panel that was removed
39171      */
39172     remove : function(panel, preservePanel)
39173     {
39174         panel = this.getPanel(panel);
39175         if(!panel){
39176             return null;
39177         }
39178         var e = {};
39179         this.fireEvent("beforeremove", this, panel, e);
39180         if(e.cancel === true){
39181             return null;
39182         }
39183         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39184         var panelId = panel.getId();
39185         this.panels.removeKey(panelId);
39186         if(preservePanel){
39187             document.body.appendChild(panel.getEl().dom);
39188         }
39189         if(this.tabs){
39190             this.tabs.removeTab(panel.getEl().id);
39191         }else if (!preservePanel){
39192             this.bodyEl.dom.removeChild(panel.getEl().dom);
39193         }
39194         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39195             var p = this.panels.first();
39196             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39197             tempEl.appendChild(p.getEl().dom);
39198             this.bodyEl.update("");
39199             this.bodyEl.dom.appendChild(p.getEl().dom);
39200             tempEl = null;
39201             this.updateTitle(p.getTitle());
39202             this.tabs = null;
39203             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39204             this.setActivePanel(p);
39205         }
39206         panel.setRegion(null);
39207         if(this.activePanel == panel){
39208             this.activePanel = null;
39209         }
39210         if(this.config.autoDestroy !== false && preservePanel !== true){
39211             try{panel.destroy();}catch(e){}
39212         }
39213         this.fireEvent("panelremoved", this, panel);
39214         return panel;
39215     },
39216
39217     /**
39218      * Returns the TabPanel component used by this region
39219      * @return {Roo.TabPanel}
39220      */
39221     getTabs : function(){
39222         return this.tabs;
39223     },
39224
39225     createTool : function(parentEl, className){
39226         var btn = Roo.DomHelper.append(parentEl, {
39227             tag: "div",
39228             cls: "x-layout-tools-button",
39229             children: [ {
39230                 tag: "div",
39231                 cls: "roo-layout-tools-button-inner " + className,
39232                 html: "&#160;"
39233             }]
39234         }, true);
39235         btn.addClassOnOver("roo-layout-tools-button-over");
39236         return btn;
39237     }
39238 });/*
39239  * Based on:
39240  * Ext JS Library 1.1.1
39241  * Copyright(c) 2006-2007, Ext JS, LLC.
39242  *
39243  * Originally Released Under LGPL - original licence link has changed is not relivant.
39244  *
39245  * Fork - LGPL
39246  * <script type="text/javascript">
39247  */
39248  
39249
39250
39251 /**
39252  * @class Roo.SplitLayoutRegion
39253  * @extends Roo.LayoutRegion
39254  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39255  */
39256 Roo.bootstrap.layout.Split = function(config){
39257     this.cursor = config.cursor;
39258     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39259 };
39260
39261 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39262 {
39263     splitTip : "Drag to resize.",
39264     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39265     useSplitTips : false,
39266
39267     applyConfig : function(config){
39268         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39269     },
39270     
39271     onRender : function(ctr,pos) {
39272         
39273         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39274         if(!this.config.split){
39275             return;
39276         }
39277         if(!this.split){
39278             
39279             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39280                             tag: "div",
39281                             id: this.el.id + "-split",
39282                             cls: "roo-layout-split roo-layout-split-"+this.position,
39283                             html: "&#160;"
39284             });
39285             /** The SplitBar for this region 
39286             * @type Roo.SplitBar */
39287             // does not exist yet...
39288             Roo.log([this.position, this.orientation]);
39289             
39290             this.split = new Roo.bootstrap.SplitBar({
39291                 dragElement : splitEl,
39292                 resizingElement: this.el,
39293                 orientation : this.orientation
39294             });
39295             
39296             this.split.on("moved", this.onSplitMove, this);
39297             this.split.useShim = this.config.useShim === true;
39298             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39299             if(this.useSplitTips){
39300                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39301             }
39302             //if(config.collapsible){
39303             //    this.split.el.on("dblclick", this.collapse,  this);
39304             //}
39305         }
39306         if(typeof this.config.minSize != "undefined"){
39307             this.split.minSize = this.config.minSize;
39308         }
39309         if(typeof this.config.maxSize != "undefined"){
39310             this.split.maxSize = this.config.maxSize;
39311         }
39312         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39313             this.hideSplitter();
39314         }
39315         
39316     },
39317
39318     getHMaxSize : function(){
39319          var cmax = this.config.maxSize || 10000;
39320          var center = this.mgr.getRegion("center");
39321          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39322     },
39323
39324     getVMaxSize : function(){
39325          var cmax = this.config.maxSize || 10000;
39326          var center = this.mgr.getRegion("center");
39327          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39328     },
39329
39330     onSplitMove : function(split, newSize){
39331         this.fireEvent("resized", this, newSize);
39332     },
39333     
39334     /** 
39335      * Returns the {@link Roo.SplitBar} for this region.
39336      * @return {Roo.SplitBar}
39337      */
39338     getSplitBar : function(){
39339         return this.split;
39340     },
39341     
39342     hide : function(){
39343         this.hideSplitter();
39344         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39345     },
39346
39347     hideSplitter : function(){
39348         if(this.split){
39349             this.split.el.setLocation(-2000,-2000);
39350             this.split.el.hide();
39351         }
39352     },
39353
39354     show : function(){
39355         if(this.split){
39356             this.split.el.show();
39357         }
39358         Roo.bootstrap.layout.Split.superclass.show.call(this);
39359     },
39360     
39361     beforeSlide: function(){
39362         if(Roo.isGecko){// firefox overflow auto bug workaround
39363             this.bodyEl.clip();
39364             if(this.tabs) {
39365                 this.tabs.bodyEl.clip();
39366             }
39367             if(this.activePanel){
39368                 this.activePanel.getEl().clip();
39369                 
39370                 if(this.activePanel.beforeSlide){
39371                     this.activePanel.beforeSlide();
39372                 }
39373             }
39374         }
39375     },
39376     
39377     afterSlide : function(){
39378         if(Roo.isGecko){// firefox overflow auto bug workaround
39379             this.bodyEl.unclip();
39380             if(this.tabs) {
39381                 this.tabs.bodyEl.unclip();
39382             }
39383             if(this.activePanel){
39384                 this.activePanel.getEl().unclip();
39385                 if(this.activePanel.afterSlide){
39386                     this.activePanel.afterSlide();
39387                 }
39388             }
39389         }
39390     },
39391
39392     initAutoHide : function(){
39393         if(this.autoHide !== false){
39394             if(!this.autoHideHd){
39395                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39396                 this.autoHideHd = {
39397                     "mouseout": function(e){
39398                         if(!e.within(this.el, true)){
39399                             st.delay(500);
39400                         }
39401                     },
39402                     "mouseover" : function(e){
39403                         st.cancel();
39404                     },
39405                     scope : this
39406                 };
39407             }
39408             this.el.on(this.autoHideHd);
39409         }
39410     },
39411
39412     clearAutoHide : function(){
39413         if(this.autoHide !== false){
39414             this.el.un("mouseout", this.autoHideHd.mouseout);
39415             this.el.un("mouseover", this.autoHideHd.mouseover);
39416         }
39417     },
39418
39419     clearMonitor : function(){
39420         Roo.get(document).un("click", this.slideInIf, this);
39421     },
39422
39423     // these names are backwards but not changed for compat
39424     slideOut : function(){
39425         if(this.isSlid || this.el.hasActiveFx()){
39426             return;
39427         }
39428         this.isSlid = true;
39429         if(this.collapseBtn){
39430             this.collapseBtn.hide();
39431         }
39432         this.closeBtnState = this.closeBtn.getStyle('display');
39433         this.closeBtn.hide();
39434         if(this.stickBtn){
39435             this.stickBtn.show();
39436         }
39437         this.el.show();
39438         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39439         this.beforeSlide();
39440         this.el.setStyle("z-index", 10001);
39441         this.el.slideIn(this.getSlideAnchor(), {
39442             callback: function(){
39443                 this.afterSlide();
39444                 this.initAutoHide();
39445                 Roo.get(document).on("click", this.slideInIf, this);
39446                 this.fireEvent("slideshow", this);
39447             },
39448             scope: this,
39449             block: true
39450         });
39451     },
39452
39453     afterSlideIn : function(){
39454         this.clearAutoHide();
39455         this.isSlid = false;
39456         this.clearMonitor();
39457         this.el.setStyle("z-index", "");
39458         if(this.collapseBtn){
39459             this.collapseBtn.show();
39460         }
39461         this.closeBtn.setStyle('display', this.closeBtnState);
39462         if(this.stickBtn){
39463             this.stickBtn.hide();
39464         }
39465         this.fireEvent("slidehide", this);
39466     },
39467
39468     slideIn : function(cb){
39469         if(!this.isSlid || this.el.hasActiveFx()){
39470             Roo.callback(cb);
39471             return;
39472         }
39473         this.isSlid = false;
39474         this.beforeSlide();
39475         this.el.slideOut(this.getSlideAnchor(), {
39476             callback: function(){
39477                 this.el.setLeftTop(-10000, -10000);
39478                 this.afterSlide();
39479                 this.afterSlideIn();
39480                 Roo.callback(cb);
39481             },
39482             scope: this,
39483             block: true
39484         });
39485     },
39486     
39487     slideInIf : function(e){
39488         if(!e.within(this.el)){
39489             this.slideIn();
39490         }
39491     },
39492
39493     animateCollapse : function(){
39494         this.beforeSlide();
39495         this.el.setStyle("z-index", 20000);
39496         var anchor = this.getSlideAnchor();
39497         this.el.slideOut(anchor, {
39498             callback : function(){
39499                 this.el.setStyle("z-index", "");
39500                 this.collapsedEl.slideIn(anchor, {duration:.3});
39501                 this.afterSlide();
39502                 this.el.setLocation(-10000,-10000);
39503                 this.el.hide();
39504                 this.fireEvent("collapsed", this);
39505             },
39506             scope: this,
39507             block: true
39508         });
39509     },
39510
39511     animateExpand : function(){
39512         this.beforeSlide();
39513         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39514         this.el.setStyle("z-index", 20000);
39515         this.collapsedEl.hide({
39516             duration:.1
39517         });
39518         this.el.slideIn(this.getSlideAnchor(), {
39519             callback : function(){
39520                 this.el.setStyle("z-index", "");
39521                 this.afterSlide();
39522                 if(this.split){
39523                     this.split.el.show();
39524                 }
39525                 this.fireEvent("invalidated", this);
39526                 this.fireEvent("expanded", this);
39527             },
39528             scope: this,
39529             block: true
39530         });
39531     },
39532
39533     anchors : {
39534         "west" : "left",
39535         "east" : "right",
39536         "north" : "top",
39537         "south" : "bottom"
39538     },
39539
39540     sanchors : {
39541         "west" : "l",
39542         "east" : "r",
39543         "north" : "t",
39544         "south" : "b"
39545     },
39546
39547     canchors : {
39548         "west" : "tl-tr",
39549         "east" : "tr-tl",
39550         "north" : "tl-bl",
39551         "south" : "bl-tl"
39552     },
39553
39554     getAnchor : function(){
39555         return this.anchors[this.position];
39556     },
39557
39558     getCollapseAnchor : function(){
39559         return this.canchors[this.position];
39560     },
39561
39562     getSlideAnchor : function(){
39563         return this.sanchors[this.position];
39564     },
39565
39566     getAlignAdj : function(){
39567         var cm = this.cmargins;
39568         switch(this.position){
39569             case "west":
39570                 return [0, 0];
39571             break;
39572             case "east":
39573                 return [0, 0];
39574             break;
39575             case "north":
39576                 return [0, 0];
39577             break;
39578             case "south":
39579                 return [0, 0];
39580             break;
39581         }
39582     },
39583
39584     getExpandAdj : function(){
39585         var c = this.collapsedEl, cm = this.cmargins;
39586         switch(this.position){
39587             case "west":
39588                 return [-(cm.right+c.getWidth()+cm.left), 0];
39589             break;
39590             case "east":
39591                 return [cm.right+c.getWidth()+cm.left, 0];
39592             break;
39593             case "north":
39594                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39595             break;
39596             case "south":
39597                 return [0, cm.top+cm.bottom+c.getHeight()];
39598             break;
39599         }
39600     }
39601 });/*
39602  * Based on:
39603  * Ext JS Library 1.1.1
39604  * Copyright(c) 2006-2007, Ext JS, LLC.
39605  *
39606  * Originally Released Under LGPL - original licence link has changed is not relivant.
39607  *
39608  * Fork - LGPL
39609  * <script type="text/javascript">
39610  */
39611 /*
39612  * These classes are private internal classes
39613  */
39614 Roo.bootstrap.layout.Center = function(config){
39615     config.region = "center";
39616     Roo.bootstrap.layout.Region.call(this, config);
39617     this.visible = true;
39618     this.minWidth = config.minWidth || 20;
39619     this.minHeight = config.minHeight || 20;
39620 };
39621
39622 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39623     hide : function(){
39624         // center panel can't be hidden
39625     },
39626     
39627     show : function(){
39628         // center panel can't be hidden
39629     },
39630     
39631     getMinWidth: function(){
39632         return this.minWidth;
39633     },
39634     
39635     getMinHeight: function(){
39636         return this.minHeight;
39637     }
39638 });
39639
39640
39641
39642
39643  
39644
39645
39646
39647
39648
39649
39650 Roo.bootstrap.layout.North = function(config)
39651 {
39652     config.region = 'north';
39653     config.cursor = 'n-resize';
39654     
39655     Roo.bootstrap.layout.Split.call(this, config);
39656     
39657     
39658     if(this.split){
39659         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39660         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39661         this.split.el.addClass("roo-layout-split-v");
39662     }
39663     //var size = config.initialSize || config.height;
39664     //if(this.el && typeof size != "undefined"){
39665     //    this.el.setHeight(size);
39666     //}
39667 };
39668 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39669 {
39670     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39671      
39672      
39673     onRender : function(ctr, pos)
39674     {
39675         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39676         var size = this.config.initialSize || this.config.height;
39677         if(this.el && typeof size != "undefined"){
39678             this.el.setHeight(size);
39679         }
39680     
39681     },
39682     
39683     getBox : function(){
39684         if(this.collapsed){
39685             return this.collapsedEl.getBox();
39686         }
39687         var box = this.el.getBox();
39688         if(this.split){
39689             box.height += this.split.el.getHeight();
39690         }
39691         return box;
39692     },
39693     
39694     updateBox : function(box){
39695         if(this.split && !this.collapsed){
39696             box.height -= this.split.el.getHeight();
39697             this.split.el.setLeft(box.x);
39698             this.split.el.setTop(box.y+box.height);
39699             this.split.el.setWidth(box.width);
39700         }
39701         if(this.collapsed){
39702             this.updateBody(box.width, null);
39703         }
39704         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39705     }
39706 });
39707
39708
39709
39710
39711
39712 Roo.bootstrap.layout.South = function(config){
39713     config.region = 'south';
39714     config.cursor = 's-resize';
39715     Roo.bootstrap.layout.Split.call(this, config);
39716     if(this.split){
39717         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39718         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39719         this.split.el.addClass("roo-layout-split-v");
39720     }
39721     
39722 };
39723
39724 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39725     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39726     
39727     onRender : function(ctr, pos)
39728     {
39729         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39730         var size = this.config.initialSize || this.config.height;
39731         if(this.el && typeof size != "undefined"){
39732             this.el.setHeight(size);
39733         }
39734     
39735     },
39736     
39737     getBox : function(){
39738         if(this.collapsed){
39739             return this.collapsedEl.getBox();
39740         }
39741         var box = this.el.getBox();
39742         if(this.split){
39743             var sh = this.split.el.getHeight();
39744             box.height += sh;
39745             box.y -= sh;
39746         }
39747         return box;
39748     },
39749     
39750     updateBox : function(box){
39751         if(this.split && !this.collapsed){
39752             var sh = this.split.el.getHeight();
39753             box.height -= sh;
39754             box.y += sh;
39755             this.split.el.setLeft(box.x);
39756             this.split.el.setTop(box.y-sh);
39757             this.split.el.setWidth(box.width);
39758         }
39759         if(this.collapsed){
39760             this.updateBody(box.width, null);
39761         }
39762         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39763     }
39764 });
39765
39766 Roo.bootstrap.layout.East = function(config){
39767     config.region = "east";
39768     config.cursor = "e-resize";
39769     Roo.bootstrap.layout.Split.call(this, config);
39770     if(this.split){
39771         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39772         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39773         this.split.el.addClass("roo-layout-split-h");
39774     }
39775     
39776 };
39777 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39778     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39779     
39780     onRender : function(ctr, pos)
39781     {
39782         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39783         var size = this.config.initialSize || this.config.width;
39784         if(this.el && typeof size != "undefined"){
39785             this.el.setWidth(size);
39786         }
39787     
39788     },
39789     
39790     getBox : function(){
39791         if(this.collapsed){
39792             return this.collapsedEl.getBox();
39793         }
39794         var box = this.el.getBox();
39795         if(this.split){
39796             var sw = this.split.el.getWidth();
39797             box.width += sw;
39798             box.x -= sw;
39799         }
39800         return box;
39801     },
39802
39803     updateBox : function(box){
39804         if(this.split && !this.collapsed){
39805             var sw = this.split.el.getWidth();
39806             box.width -= sw;
39807             this.split.el.setLeft(box.x);
39808             this.split.el.setTop(box.y);
39809             this.split.el.setHeight(box.height);
39810             box.x += sw;
39811         }
39812         if(this.collapsed){
39813             this.updateBody(null, box.height);
39814         }
39815         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39816     }
39817 });
39818
39819 Roo.bootstrap.layout.West = function(config){
39820     config.region = "west";
39821     config.cursor = "w-resize";
39822     
39823     Roo.bootstrap.layout.Split.call(this, config);
39824     if(this.split){
39825         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39826         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39827         this.split.el.addClass("roo-layout-split-h");
39828     }
39829     
39830 };
39831 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39832     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39833     
39834     onRender: function(ctr, pos)
39835     {
39836         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39837         var size = this.config.initialSize || this.config.width;
39838         if(typeof size != "undefined"){
39839             this.el.setWidth(size);
39840         }
39841     },
39842     
39843     getBox : function(){
39844         if(this.collapsed){
39845             return this.collapsedEl.getBox();
39846         }
39847         var box = this.el.getBox();
39848         if (box.width == 0) {
39849             box.width = this.config.width; // kludge?
39850         }
39851         if(this.split){
39852             box.width += this.split.el.getWidth();
39853         }
39854         return box;
39855     },
39856     
39857     updateBox : function(box){
39858         if(this.split && !this.collapsed){
39859             var sw = this.split.el.getWidth();
39860             box.width -= sw;
39861             this.split.el.setLeft(box.x+box.width);
39862             this.split.el.setTop(box.y);
39863             this.split.el.setHeight(box.height);
39864         }
39865         if(this.collapsed){
39866             this.updateBody(null, box.height);
39867         }
39868         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39869     }
39870 });Roo.namespace("Roo.bootstrap.panel");/*
39871  * Based on:
39872  * Ext JS Library 1.1.1
39873  * Copyright(c) 2006-2007, Ext JS, LLC.
39874  *
39875  * Originally Released Under LGPL - original licence link has changed is not relivant.
39876  *
39877  * Fork - LGPL
39878  * <script type="text/javascript">
39879  */
39880 /**
39881  * @class Roo.ContentPanel
39882  * @extends Roo.util.Observable
39883  * A basic ContentPanel element.
39884  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39885  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39886  * @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
39887  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39888  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39889  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39890  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39891  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39892  * @cfg {String} title          The title for this panel
39893  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39894  * @cfg {String} url            Calls {@link #setUrl} with this value
39895  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39896  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39897  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39898  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39899  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39900  * @cfg {Boolean} badges render the badges
39901  * @cfg {String} cls  extra classes to use  
39902  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39903
39904  * @constructor
39905  * Create a new ContentPanel.
39906  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39907  * @param {String/Object} config A string to set only the title or a config object
39908  * @param {String} content (optional) Set the HTML content for this panel
39909  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39910  */
39911 Roo.bootstrap.panel.Content = function( config){
39912     
39913     this.tpl = config.tpl || false;
39914     
39915     var el = config.el;
39916     var content = config.content;
39917
39918     if(config.autoCreate){ // xtype is available if this is called from factory
39919         el = Roo.id();
39920     }
39921     this.el = Roo.get(el);
39922     if(!this.el && config && config.autoCreate){
39923         if(typeof config.autoCreate == "object"){
39924             if(!config.autoCreate.id){
39925                 config.autoCreate.id = config.id||el;
39926             }
39927             this.el = Roo.DomHelper.append(document.body,
39928                         config.autoCreate, true);
39929         }else{
39930             var elcfg =  {
39931                 tag: "div",
39932                 cls: (config.cls || '') +
39933                     (config.background ? ' bg-' + config.background : '') +
39934                     " roo-layout-inactive-content",
39935                 id: config.id||el
39936             };
39937             if (config.iframe) {
39938                 elcfg.cn = [
39939                     {
39940                         tag : 'iframe',
39941                         style : 'border: 0px',
39942                         src : 'about:blank'
39943                     }
39944                 ];
39945             }
39946               
39947             if (config.html) {
39948                 elcfg.html = config.html;
39949                 
39950             }
39951                         
39952             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39953             if (config.iframe) {
39954                 this.iframeEl = this.el.select('iframe',true).first();
39955             }
39956             
39957         }
39958     } 
39959     this.closable = false;
39960     this.loaded = false;
39961     this.active = false;
39962    
39963       
39964     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39965         
39966         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39967         
39968         this.wrapEl = this.el; //this.el.wrap();
39969         var ti = [];
39970         if (config.toolbar.items) {
39971             ti = config.toolbar.items ;
39972             delete config.toolbar.items ;
39973         }
39974         
39975         var nitems = [];
39976         this.toolbar.render(this.wrapEl, 'before');
39977         for(var i =0;i < ti.length;i++) {
39978           //  Roo.log(['add child', items[i]]);
39979             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39980         }
39981         this.toolbar.items = nitems;
39982         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39983         delete config.toolbar;
39984         
39985     }
39986     /*
39987     // xtype created footer. - not sure if will work as we normally have to render first..
39988     if (this.footer && !this.footer.el && this.footer.xtype) {
39989         if (!this.wrapEl) {
39990             this.wrapEl = this.el.wrap();
39991         }
39992     
39993         this.footer.container = this.wrapEl.createChild();
39994          
39995         this.footer = Roo.factory(this.footer, Roo);
39996         
39997     }
39998     */
39999     
40000      if(typeof config == "string"){
40001         this.title = config;
40002     }else{
40003         Roo.apply(this, config);
40004     }
40005     
40006     if(this.resizeEl){
40007         this.resizeEl = Roo.get(this.resizeEl, true);
40008     }else{
40009         this.resizeEl = this.el;
40010     }
40011     // handle view.xtype
40012     
40013  
40014     
40015     
40016     this.addEvents({
40017         /**
40018          * @event activate
40019          * Fires when this panel is activated. 
40020          * @param {Roo.ContentPanel} this
40021          */
40022         "activate" : true,
40023         /**
40024          * @event deactivate
40025          * Fires when this panel is activated. 
40026          * @param {Roo.ContentPanel} this
40027          */
40028         "deactivate" : true,
40029
40030         /**
40031          * @event resize
40032          * Fires when this panel is resized if fitToFrame is true.
40033          * @param {Roo.ContentPanel} this
40034          * @param {Number} width The width after any component adjustments
40035          * @param {Number} height The height after any component adjustments
40036          */
40037         "resize" : true,
40038         
40039          /**
40040          * @event render
40041          * Fires when this tab is created
40042          * @param {Roo.ContentPanel} this
40043          */
40044         "render" : true
40045         
40046         
40047         
40048     });
40049     
40050
40051     
40052     
40053     if(this.autoScroll && !this.iframe){
40054         this.resizeEl.setStyle("overflow", "auto");
40055     } else {
40056         // fix randome scrolling
40057         //this.el.on('scroll', function() {
40058         //    Roo.log('fix random scolling');
40059         //    this.scrollTo('top',0); 
40060         //});
40061     }
40062     content = content || this.content;
40063     if(content){
40064         this.setContent(content);
40065     }
40066     if(config && config.url){
40067         this.setUrl(this.url, this.params, this.loadOnce);
40068     }
40069     
40070     
40071     
40072     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40073     
40074     if (this.view && typeof(this.view.xtype) != 'undefined') {
40075         this.view.el = this.el.appendChild(document.createElement("div"));
40076         this.view = Roo.factory(this.view); 
40077         this.view.render  &&  this.view.render(false, '');  
40078     }
40079     
40080     
40081     this.fireEvent('render', this);
40082 };
40083
40084 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40085     
40086     cls : '',
40087     background : '',
40088     
40089     tabTip : '',
40090     
40091     iframe : false,
40092     iframeEl : false,
40093     
40094     setRegion : function(region){
40095         this.region = region;
40096         this.setActiveClass(region && !this.background);
40097     },
40098     
40099     
40100     setActiveClass: function(state)
40101     {
40102         if(state){
40103            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40104            this.el.setStyle('position','relative');
40105         }else{
40106            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40107            this.el.setStyle('position', 'absolute');
40108         } 
40109     },
40110     
40111     /**
40112      * Returns the toolbar for this Panel if one was configured. 
40113      * @return {Roo.Toolbar} 
40114      */
40115     getToolbar : function(){
40116         return this.toolbar;
40117     },
40118     
40119     setActiveState : function(active)
40120     {
40121         this.active = active;
40122         this.setActiveClass(active);
40123         if(!active){
40124             if(this.fireEvent("deactivate", this) === false){
40125                 return false;
40126             }
40127             return true;
40128         }
40129         this.fireEvent("activate", this);
40130         return true;
40131     },
40132     /**
40133      * Updates this panel's element (not for iframe)
40134      * @param {String} content The new content
40135      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40136     */
40137     setContent : function(content, loadScripts){
40138         if (this.iframe) {
40139             return;
40140         }
40141         
40142         this.el.update(content, loadScripts);
40143     },
40144
40145     ignoreResize : function(w, h){
40146         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40147             return true;
40148         }else{
40149             this.lastSize = {width: w, height: h};
40150             return false;
40151         }
40152     },
40153     /**
40154      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40155      * @return {Roo.UpdateManager} The UpdateManager
40156      */
40157     getUpdateManager : function(){
40158         if (this.iframe) {
40159             return false;
40160         }
40161         return this.el.getUpdateManager();
40162     },
40163      /**
40164      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40165      * Does not work with IFRAME contents
40166      * @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:
40167 <pre><code>
40168 panel.load({
40169     url: "your-url.php",
40170     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40171     callback: yourFunction,
40172     scope: yourObject, //(optional scope)
40173     discardUrl: false,
40174     nocache: false,
40175     text: "Loading...",
40176     timeout: 30,
40177     scripts: false
40178 });
40179 </code></pre>
40180      
40181      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40182      * 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.
40183      * @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}
40184      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40185      * @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.
40186      * @return {Roo.ContentPanel} this
40187      */
40188     load : function(){
40189         
40190         if (this.iframe) {
40191             return this;
40192         }
40193         
40194         var um = this.el.getUpdateManager();
40195         um.update.apply(um, arguments);
40196         return this;
40197     },
40198
40199
40200     /**
40201      * 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.
40202      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40203      * @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)
40204      * @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)
40205      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40206      */
40207     setUrl : function(url, params, loadOnce){
40208         if (this.iframe) {
40209             this.iframeEl.dom.src = url;
40210             return false;
40211         }
40212         
40213         if(this.refreshDelegate){
40214             this.removeListener("activate", this.refreshDelegate);
40215         }
40216         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40217         this.on("activate", this.refreshDelegate);
40218         return this.el.getUpdateManager();
40219     },
40220     
40221     _handleRefresh : function(url, params, loadOnce){
40222         if(!loadOnce || !this.loaded){
40223             var updater = this.el.getUpdateManager();
40224             updater.update(url, params, this._setLoaded.createDelegate(this));
40225         }
40226     },
40227     
40228     _setLoaded : function(){
40229         this.loaded = true;
40230     }, 
40231     
40232     /**
40233      * Returns this panel's id
40234      * @return {String} 
40235      */
40236     getId : function(){
40237         return this.el.id;
40238     },
40239     
40240     /** 
40241      * Returns this panel's element - used by regiosn to add.
40242      * @return {Roo.Element} 
40243      */
40244     getEl : function(){
40245         return this.wrapEl || this.el;
40246     },
40247     
40248    
40249     
40250     adjustForComponents : function(width, height)
40251     {
40252         //Roo.log('adjustForComponents ');
40253         if(this.resizeEl != this.el){
40254             width -= this.el.getFrameWidth('lr');
40255             height -= this.el.getFrameWidth('tb');
40256         }
40257         if(this.toolbar){
40258             var te = this.toolbar.getEl();
40259             te.setWidth(width);
40260             height -= te.getHeight();
40261         }
40262         if(this.footer){
40263             var te = this.footer.getEl();
40264             te.setWidth(width);
40265             height -= te.getHeight();
40266         }
40267         
40268         
40269         if(this.adjustments){
40270             width += this.adjustments[0];
40271             height += this.adjustments[1];
40272         }
40273         return {"width": width, "height": height};
40274     },
40275     
40276     setSize : function(width, height){
40277         if(this.fitToFrame && !this.ignoreResize(width, height)){
40278             if(this.fitContainer && this.resizeEl != this.el){
40279                 this.el.setSize(width, height);
40280             }
40281             var size = this.adjustForComponents(width, height);
40282             if (this.iframe) {
40283                 this.iframeEl.setSize(width,height);
40284             }
40285             
40286             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40287             this.fireEvent('resize', this, size.width, size.height);
40288             
40289             
40290         }
40291     },
40292     
40293     /**
40294      * Returns this panel's title
40295      * @return {String} 
40296      */
40297     getTitle : function(){
40298         
40299         if (typeof(this.title) != 'object') {
40300             return this.title;
40301         }
40302         
40303         var t = '';
40304         for (var k in this.title) {
40305             if (!this.title.hasOwnProperty(k)) {
40306                 continue;
40307             }
40308             
40309             if (k.indexOf('-') >= 0) {
40310                 var s = k.split('-');
40311                 for (var i = 0; i<s.length; i++) {
40312                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40313                 }
40314             } else {
40315                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40316             }
40317         }
40318         return t;
40319     },
40320     
40321     /**
40322      * Set this panel's title
40323      * @param {String} title
40324      */
40325     setTitle : function(title){
40326         this.title = title;
40327         if(this.region){
40328             this.region.updatePanelTitle(this, title);
40329         }
40330     },
40331     
40332     /**
40333      * Returns true is this panel was configured to be closable
40334      * @return {Boolean} 
40335      */
40336     isClosable : function(){
40337         return this.closable;
40338     },
40339     
40340     beforeSlide : function(){
40341         this.el.clip();
40342         this.resizeEl.clip();
40343     },
40344     
40345     afterSlide : function(){
40346         this.el.unclip();
40347         this.resizeEl.unclip();
40348     },
40349     
40350     /**
40351      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40352      *   Will fail silently if the {@link #setUrl} method has not been called.
40353      *   This does not activate the panel, just updates its content.
40354      */
40355     refresh : function(){
40356         if(this.refreshDelegate){
40357            this.loaded = false;
40358            this.refreshDelegate();
40359         }
40360     },
40361     
40362     /**
40363      * Destroys this panel
40364      */
40365     destroy : function(){
40366         this.el.removeAllListeners();
40367         var tempEl = document.createElement("span");
40368         tempEl.appendChild(this.el.dom);
40369         tempEl.innerHTML = "";
40370         this.el.remove();
40371         this.el = null;
40372     },
40373     
40374     /**
40375      * form - if the content panel contains a form - this is a reference to it.
40376      * @type {Roo.form.Form}
40377      */
40378     form : false,
40379     /**
40380      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40381      *    This contains a reference to it.
40382      * @type {Roo.View}
40383      */
40384     view : false,
40385     
40386       /**
40387      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40388      * <pre><code>
40389
40390 layout.addxtype({
40391        xtype : 'Form',
40392        items: [ .... ]
40393    }
40394 );
40395
40396 </code></pre>
40397      * @param {Object} cfg Xtype definition of item to add.
40398      */
40399     
40400     
40401     getChildContainer: function () {
40402         return this.getEl();
40403     }
40404     
40405     
40406     /*
40407         var  ret = new Roo.factory(cfg);
40408         return ret;
40409         
40410         
40411         // add form..
40412         if (cfg.xtype.match(/^Form$/)) {
40413             
40414             var el;
40415             //if (this.footer) {
40416             //    el = this.footer.container.insertSibling(false, 'before');
40417             //} else {
40418                 el = this.el.createChild();
40419             //}
40420
40421             this.form = new  Roo.form.Form(cfg);
40422             
40423             
40424             if ( this.form.allItems.length) {
40425                 this.form.render(el.dom);
40426             }
40427             return this.form;
40428         }
40429         // should only have one of theses..
40430         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40431             // views.. should not be just added - used named prop 'view''
40432             
40433             cfg.el = this.el.appendChild(document.createElement("div"));
40434             // factory?
40435             
40436             var ret = new Roo.factory(cfg);
40437              
40438              ret.render && ret.render(false, ''); // render blank..
40439             this.view = ret;
40440             return ret;
40441         }
40442         return false;
40443     }
40444     \*/
40445 });
40446  
40447 /**
40448  * @class Roo.bootstrap.panel.Grid
40449  * @extends Roo.bootstrap.panel.Content
40450  * @constructor
40451  * Create a new GridPanel.
40452  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40453  * @param {Object} config A the config object
40454   
40455  */
40456
40457
40458
40459 Roo.bootstrap.panel.Grid = function(config)
40460 {
40461     
40462       
40463     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40464         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40465
40466     config.el = this.wrapper;
40467     //this.el = this.wrapper;
40468     
40469       if (config.container) {
40470         // ctor'ed from a Border/panel.grid
40471         
40472         
40473         this.wrapper.setStyle("overflow", "hidden");
40474         this.wrapper.addClass('roo-grid-container');
40475
40476     }
40477     
40478     
40479     if(config.toolbar){
40480         var tool_el = this.wrapper.createChild();    
40481         this.toolbar = Roo.factory(config.toolbar);
40482         var ti = [];
40483         if (config.toolbar.items) {
40484             ti = config.toolbar.items ;
40485             delete config.toolbar.items ;
40486         }
40487         
40488         var nitems = [];
40489         this.toolbar.render(tool_el);
40490         for(var i =0;i < ti.length;i++) {
40491           //  Roo.log(['add child', items[i]]);
40492             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40493         }
40494         this.toolbar.items = nitems;
40495         
40496         delete config.toolbar;
40497     }
40498     
40499     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40500     config.grid.scrollBody = true;;
40501     config.grid.monitorWindowResize = false; // turn off autosizing
40502     config.grid.autoHeight = false;
40503     config.grid.autoWidth = false;
40504     
40505     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40506     
40507     if (config.background) {
40508         // render grid on panel activation (if panel background)
40509         this.on('activate', function(gp) {
40510             if (!gp.grid.rendered) {
40511                 gp.grid.render(this.wrapper);
40512                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40513             }
40514         });
40515             
40516     } else {
40517         this.grid.render(this.wrapper);
40518         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40519
40520     }
40521     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40522     // ??? needed ??? config.el = this.wrapper;
40523     
40524     
40525     
40526   
40527     // xtype created footer. - not sure if will work as we normally have to render first..
40528     if (this.footer && !this.footer.el && this.footer.xtype) {
40529         
40530         var ctr = this.grid.getView().getFooterPanel(true);
40531         this.footer.dataSource = this.grid.dataSource;
40532         this.footer = Roo.factory(this.footer, Roo);
40533         this.footer.render(ctr);
40534         
40535     }
40536     
40537     
40538     
40539     
40540      
40541 };
40542
40543 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40544     getId : function(){
40545         return this.grid.id;
40546     },
40547     
40548     /**
40549      * Returns the grid for this panel
40550      * @return {Roo.bootstrap.Table} 
40551      */
40552     getGrid : function(){
40553         return this.grid;    
40554     },
40555     
40556     setSize : function(width, height){
40557         if(!this.ignoreResize(width, height)){
40558             var grid = this.grid;
40559             var size = this.adjustForComponents(width, height);
40560             // tfoot is not a footer?
40561           
40562             
40563             var gridel = grid.getGridEl();
40564             gridel.setSize(size.width, size.height);
40565             
40566             var tbd = grid.getGridEl().select('tbody', true).first();
40567             var thd = grid.getGridEl().select('thead',true).first();
40568             var tbf= grid.getGridEl().select('tfoot', true).first();
40569
40570             if (tbf) {
40571                 size.height -= tbf.getHeight();
40572             }
40573             if (thd) {
40574                 size.height -= thd.getHeight();
40575             }
40576             
40577             tbd.setSize(size.width, size.height );
40578             // this is for the account management tab -seems to work there.
40579             var thd = grid.getGridEl().select('thead',true).first();
40580             //if (tbd) {
40581             //    tbd.setSize(size.width, size.height - thd.getHeight());
40582             //}
40583              
40584             grid.autoSize();
40585         }
40586     },
40587      
40588     
40589     
40590     beforeSlide : function(){
40591         this.grid.getView().scroller.clip();
40592     },
40593     
40594     afterSlide : function(){
40595         this.grid.getView().scroller.unclip();
40596     },
40597     
40598     destroy : function(){
40599         this.grid.destroy();
40600         delete this.grid;
40601         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40602     }
40603 });
40604
40605 /**
40606  * @class Roo.bootstrap.panel.Nest
40607  * @extends Roo.bootstrap.panel.Content
40608  * @constructor
40609  * Create a new Panel, that can contain a layout.Border.
40610  * 
40611  * 
40612  * @param {Roo.BorderLayout} layout The layout for this panel
40613  * @param {String/Object} config A string to set only the title or a config object
40614  */
40615 Roo.bootstrap.panel.Nest = function(config)
40616 {
40617     // construct with only one argument..
40618     /* FIXME - implement nicer consturctors
40619     if (layout.layout) {
40620         config = layout;
40621         layout = config.layout;
40622         delete config.layout;
40623     }
40624     if (layout.xtype && !layout.getEl) {
40625         // then layout needs constructing..
40626         layout = Roo.factory(layout, Roo);
40627     }
40628     */
40629     
40630     config.el =  config.layout.getEl();
40631     
40632     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40633     
40634     config.layout.monitorWindowResize = false; // turn off autosizing
40635     this.layout = config.layout;
40636     this.layout.getEl().addClass("roo-layout-nested-layout");
40637     this.layout.parent = this;
40638     
40639     
40640     
40641     
40642 };
40643
40644 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40645
40646     setSize : function(width, height){
40647         if(!this.ignoreResize(width, height)){
40648             var size = this.adjustForComponents(width, height);
40649             var el = this.layout.getEl();
40650             if (size.height < 1) {
40651                 el.setWidth(size.width);   
40652             } else {
40653                 el.setSize(size.width, size.height);
40654             }
40655             var touch = el.dom.offsetWidth;
40656             this.layout.layout();
40657             // ie requires a double layout on the first pass
40658             if(Roo.isIE && !this.initialized){
40659                 this.initialized = true;
40660                 this.layout.layout();
40661             }
40662         }
40663     },
40664     
40665     // activate all subpanels if not currently active..
40666     
40667     setActiveState : function(active){
40668         this.active = active;
40669         this.setActiveClass(active);
40670         
40671         if(!active){
40672             this.fireEvent("deactivate", this);
40673             return;
40674         }
40675         
40676         this.fireEvent("activate", this);
40677         // not sure if this should happen before or after..
40678         if (!this.layout) {
40679             return; // should not happen..
40680         }
40681         var reg = false;
40682         for (var r in this.layout.regions) {
40683             reg = this.layout.getRegion(r);
40684             if (reg.getActivePanel()) {
40685                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40686                 reg.setActivePanel(reg.getActivePanel());
40687                 continue;
40688             }
40689             if (!reg.panels.length) {
40690                 continue;
40691             }
40692             reg.showPanel(reg.getPanel(0));
40693         }
40694         
40695         
40696         
40697         
40698     },
40699     
40700     /**
40701      * Returns the nested BorderLayout for this panel
40702      * @return {Roo.BorderLayout} 
40703      */
40704     getLayout : function(){
40705         return this.layout;
40706     },
40707     
40708      /**
40709      * Adds a xtype elements to the layout of the nested panel
40710      * <pre><code>
40711
40712 panel.addxtype({
40713        xtype : 'ContentPanel',
40714        region: 'west',
40715        items: [ .... ]
40716    }
40717 );
40718
40719 panel.addxtype({
40720         xtype : 'NestedLayoutPanel',
40721         region: 'west',
40722         layout: {
40723            center: { },
40724            west: { }   
40725         },
40726         items : [ ... list of content panels or nested layout panels.. ]
40727    }
40728 );
40729 </code></pre>
40730      * @param {Object} cfg Xtype definition of item to add.
40731      */
40732     addxtype : function(cfg) {
40733         return this.layout.addxtype(cfg);
40734     
40735     }
40736 });/*
40737  * Based on:
40738  * Ext JS Library 1.1.1
40739  * Copyright(c) 2006-2007, Ext JS, LLC.
40740  *
40741  * Originally Released Under LGPL - original licence link has changed is not relivant.
40742  *
40743  * Fork - LGPL
40744  * <script type="text/javascript">
40745  */
40746 /**
40747  * @class Roo.TabPanel
40748  * @extends Roo.util.Observable
40749  * A lightweight tab container.
40750  * <br><br>
40751  * Usage:
40752  * <pre><code>
40753 // basic tabs 1, built from existing content
40754 var tabs = new Roo.TabPanel("tabs1");
40755 tabs.addTab("script", "View Script");
40756 tabs.addTab("markup", "View Markup");
40757 tabs.activate("script");
40758
40759 // more advanced tabs, built from javascript
40760 var jtabs = new Roo.TabPanel("jtabs");
40761 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40762
40763 // set up the UpdateManager
40764 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40765 var updater = tab2.getUpdateManager();
40766 updater.setDefaultUrl("ajax1.htm");
40767 tab2.on('activate', updater.refresh, updater, true);
40768
40769 // Use setUrl for Ajax loading
40770 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40771 tab3.setUrl("ajax2.htm", null, true);
40772
40773 // Disabled tab
40774 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40775 tab4.disable();
40776
40777 jtabs.activate("jtabs-1");
40778  * </code></pre>
40779  * @constructor
40780  * Create a new TabPanel.
40781  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40782  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40783  */
40784 Roo.bootstrap.panel.Tabs = function(config){
40785     /**
40786     * The container element for this TabPanel.
40787     * @type Roo.Element
40788     */
40789     this.el = Roo.get(config.el);
40790     delete config.el;
40791     if(config){
40792         if(typeof config == "boolean"){
40793             this.tabPosition = config ? "bottom" : "top";
40794         }else{
40795             Roo.apply(this, config);
40796         }
40797     }
40798     
40799     if(this.tabPosition == "bottom"){
40800         // if tabs are at the bottom = create the body first.
40801         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40802         this.el.addClass("roo-tabs-bottom");
40803     }
40804     // next create the tabs holders
40805     
40806     if (this.tabPosition == "west"){
40807         
40808         var reg = this.region; // fake it..
40809         while (reg) {
40810             if (!reg.mgr.parent) {
40811                 break;
40812             }
40813             reg = reg.mgr.parent.region;
40814         }
40815         Roo.log("got nest?");
40816         Roo.log(reg);
40817         if (reg.mgr.getRegion('west')) {
40818             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40819             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40820             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40821             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40822             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40823         
40824             
40825         }
40826         
40827         
40828     } else {
40829      
40830         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40831         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40832         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40833         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40834     }
40835     
40836     
40837     if(Roo.isIE){
40838         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40839     }
40840     
40841     // finally - if tabs are at the top, then create the body last..
40842     if(this.tabPosition != "bottom"){
40843         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40844          * @type Roo.Element
40845          */
40846         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40847         this.el.addClass("roo-tabs-top");
40848     }
40849     this.items = [];
40850
40851     this.bodyEl.setStyle("position", "relative");
40852
40853     this.active = null;
40854     this.activateDelegate = this.activate.createDelegate(this);
40855
40856     this.addEvents({
40857         /**
40858          * @event tabchange
40859          * Fires when the active tab changes
40860          * @param {Roo.TabPanel} this
40861          * @param {Roo.TabPanelItem} activePanel The new active tab
40862          */
40863         "tabchange": true,
40864         /**
40865          * @event beforetabchange
40866          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40867          * @param {Roo.TabPanel} this
40868          * @param {Object} e Set cancel to true on this object to cancel the tab change
40869          * @param {Roo.TabPanelItem} tab The tab being changed to
40870          */
40871         "beforetabchange" : true
40872     });
40873
40874     Roo.EventManager.onWindowResize(this.onResize, this);
40875     this.cpad = this.el.getPadding("lr");
40876     this.hiddenCount = 0;
40877
40878
40879     // toolbar on the tabbar support...
40880     if (this.toolbar) {
40881         alert("no toolbar support yet");
40882         this.toolbar  = false;
40883         /*
40884         var tcfg = this.toolbar;
40885         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40886         this.toolbar = new Roo.Toolbar(tcfg);
40887         if (Roo.isSafari) {
40888             var tbl = tcfg.container.child('table', true);
40889             tbl.setAttribute('width', '100%');
40890         }
40891         */
40892         
40893     }
40894    
40895
40896
40897     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40898 };
40899
40900 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40901     /*
40902      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40903      */
40904     tabPosition : "top",
40905     /*
40906      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40907      */
40908     currentTabWidth : 0,
40909     /*
40910      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40911      */
40912     minTabWidth : 40,
40913     /*
40914      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40915      */
40916     maxTabWidth : 250,
40917     /*
40918      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40919      */
40920     preferredTabWidth : 175,
40921     /*
40922      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40923      */
40924     resizeTabs : false,
40925     /*
40926      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40927      */
40928     monitorResize : true,
40929     /*
40930      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40931      */
40932     toolbar : false,  // set by caller..
40933     
40934     region : false, /// set by caller
40935     
40936     disableTooltips : true, // not used yet...
40937
40938     /**
40939      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40940      * @param {String} id The id of the div to use <b>or create</b>
40941      * @param {String} text The text for the tab
40942      * @param {String} content (optional) Content to put in the TabPanelItem body
40943      * @param {Boolean} closable (optional) True to create a close icon on the tab
40944      * @return {Roo.TabPanelItem} The created TabPanelItem
40945      */
40946     addTab : function(id, text, content, closable, tpl)
40947     {
40948         var item = new Roo.bootstrap.panel.TabItem({
40949             panel: this,
40950             id : id,
40951             text : text,
40952             closable : closable,
40953             tpl : tpl
40954         });
40955         this.addTabItem(item);
40956         if(content){
40957             item.setContent(content);
40958         }
40959         return item;
40960     },
40961
40962     /**
40963      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40964      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40965      * @return {Roo.TabPanelItem}
40966      */
40967     getTab : function(id){
40968         return this.items[id];
40969     },
40970
40971     /**
40972      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40973      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40974      */
40975     hideTab : function(id){
40976         var t = this.items[id];
40977         if(!t.isHidden()){
40978            t.setHidden(true);
40979            this.hiddenCount++;
40980            this.autoSizeTabs();
40981         }
40982     },
40983
40984     /**
40985      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40986      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40987      */
40988     unhideTab : function(id){
40989         var t = this.items[id];
40990         if(t.isHidden()){
40991            t.setHidden(false);
40992            this.hiddenCount--;
40993            this.autoSizeTabs();
40994         }
40995     },
40996
40997     /**
40998      * Adds an existing {@link Roo.TabPanelItem}.
40999      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41000      */
41001     addTabItem : function(item)
41002     {
41003         this.items[item.id] = item;
41004         this.items.push(item);
41005         this.autoSizeTabs();
41006       //  if(this.resizeTabs){
41007     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41008   //         this.autoSizeTabs();
41009 //        }else{
41010 //            item.autoSize();
41011        // }
41012     },
41013
41014     /**
41015      * Removes a {@link Roo.TabPanelItem}.
41016      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41017      */
41018     removeTab : function(id){
41019         var items = this.items;
41020         var tab = items[id];
41021         if(!tab) { return; }
41022         var index = items.indexOf(tab);
41023         if(this.active == tab && items.length > 1){
41024             var newTab = this.getNextAvailable(index);
41025             if(newTab) {
41026                 newTab.activate();
41027             }
41028         }
41029         this.stripEl.dom.removeChild(tab.pnode.dom);
41030         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41031             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41032         }
41033         items.splice(index, 1);
41034         delete this.items[tab.id];
41035         tab.fireEvent("close", tab);
41036         tab.purgeListeners();
41037         this.autoSizeTabs();
41038     },
41039
41040     getNextAvailable : function(start){
41041         var items = this.items;
41042         var index = start;
41043         // look for a next tab that will slide over to
41044         // replace the one being removed
41045         while(index < items.length){
41046             var item = items[++index];
41047             if(item && !item.isHidden()){
41048                 return item;
41049             }
41050         }
41051         // if one isn't found select the previous tab (on the left)
41052         index = start;
41053         while(index >= 0){
41054             var item = items[--index];
41055             if(item && !item.isHidden()){
41056                 return item;
41057             }
41058         }
41059         return null;
41060     },
41061
41062     /**
41063      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41064      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41065      */
41066     disableTab : function(id){
41067         var tab = this.items[id];
41068         if(tab && this.active != tab){
41069             tab.disable();
41070         }
41071     },
41072
41073     /**
41074      * Enables a {@link Roo.TabPanelItem} that is disabled.
41075      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41076      */
41077     enableTab : function(id){
41078         var tab = this.items[id];
41079         tab.enable();
41080     },
41081
41082     /**
41083      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41084      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41085      * @return {Roo.TabPanelItem} The TabPanelItem.
41086      */
41087     activate : function(id)
41088     {
41089         //Roo.log('activite:'  + id);
41090         
41091         var tab = this.items[id];
41092         if(!tab){
41093             return null;
41094         }
41095         if(tab == this.active || tab.disabled){
41096             return tab;
41097         }
41098         var e = {};
41099         this.fireEvent("beforetabchange", this, e, tab);
41100         if(e.cancel !== true && !tab.disabled){
41101             if(this.active){
41102                 this.active.hide();
41103             }
41104             this.active = this.items[id];
41105             this.active.show();
41106             this.fireEvent("tabchange", this, this.active);
41107         }
41108         return tab;
41109     },
41110
41111     /**
41112      * Gets the active {@link Roo.TabPanelItem}.
41113      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41114      */
41115     getActiveTab : function(){
41116         return this.active;
41117     },
41118
41119     /**
41120      * Updates the tab body element to fit the height of the container element
41121      * for overflow scrolling
41122      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41123      */
41124     syncHeight : function(targetHeight){
41125         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41126         var bm = this.bodyEl.getMargins();
41127         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41128         this.bodyEl.setHeight(newHeight);
41129         return newHeight;
41130     },
41131
41132     onResize : function(){
41133         if(this.monitorResize){
41134             this.autoSizeTabs();
41135         }
41136     },
41137
41138     /**
41139      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41140      */
41141     beginUpdate : function(){
41142         this.updating = true;
41143     },
41144
41145     /**
41146      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41147      */
41148     endUpdate : function(){
41149         this.updating = false;
41150         this.autoSizeTabs();
41151     },
41152
41153     /**
41154      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41155      */
41156     autoSizeTabs : function()
41157     {
41158         var count = this.items.length;
41159         var vcount = count - this.hiddenCount;
41160         
41161         if (vcount < 2) {
41162             this.stripEl.hide();
41163         } else {
41164             this.stripEl.show();
41165         }
41166         
41167         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41168             return;
41169         }
41170         
41171         
41172         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41173         var availWidth = Math.floor(w / vcount);
41174         var b = this.stripBody;
41175         if(b.getWidth() > w){
41176             var tabs = this.items;
41177             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41178             if(availWidth < this.minTabWidth){
41179                 /*if(!this.sleft){    // incomplete scrolling code
41180                     this.createScrollButtons();
41181                 }
41182                 this.showScroll();
41183                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41184             }
41185         }else{
41186             if(this.currentTabWidth < this.preferredTabWidth){
41187                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41188             }
41189         }
41190     },
41191
41192     /**
41193      * Returns the number of tabs in this TabPanel.
41194      * @return {Number}
41195      */
41196      getCount : function(){
41197          return this.items.length;
41198      },
41199
41200     /**
41201      * Resizes all the tabs to the passed width
41202      * @param {Number} The new width
41203      */
41204     setTabWidth : function(width){
41205         this.currentTabWidth = width;
41206         for(var i = 0, len = this.items.length; i < len; i++) {
41207                 if(!this.items[i].isHidden()) {
41208                 this.items[i].setWidth(width);
41209             }
41210         }
41211     },
41212
41213     /**
41214      * Destroys this TabPanel
41215      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41216      */
41217     destroy : function(removeEl){
41218         Roo.EventManager.removeResizeListener(this.onResize, this);
41219         for(var i = 0, len = this.items.length; i < len; i++){
41220             this.items[i].purgeListeners();
41221         }
41222         if(removeEl === true){
41223             this.el.update("");
41224             this.el.remove();
41225         }
41226     },
41227     
41228     createStrip : function(container)
41229     {
41230         var strip = document.createElement("nav");
41231         strip.className = Roo.bootstrap.version == 4 ?
41232             "navbar-light bg-light" : 
41233             "navbar navbar-default"; //"x-tabs-wrap";
41234         container.appendChild(strip);
41235         return strip;
41236     },
41237     
41238     createStripList : function(strip)
41239     {
41240         // div wrapper for retard IE
41241         // returns the "tr" element.
41242         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41243         //'<div class="x-tabs-strip-wrap">'+
41244           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41245           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41246         return strip.firstChild; //.firstChild.firstChild.firstChild;
41247     },
41248     createBody : function(container)
41249     {
41250         var body = document.createElement("div");
41251         Roo.id(body, "tab-body");
41252         //Roo.fly(body).addClass("x-tabs-body");
41253         Roo.fly(body).addClass("tab-content");
41254         container.appendChild(body);
41255         return body;
41256     },
41257     createItemBody :function(bodyEl, id){
41258         var body = Roo.getDom(id);
41259         if(!body){
41260             body = document.createElement("div");
41261             body.id = id;
41262         }
41263         //Roo.fly(body).addClass("x-tabs-item-body");
41264         Roo.fly(body).addClass("tab-pane");
41265          bodyEl.insertBefore(body, bodyEl.firstChild);
41266         return body;
41267     },
41268     /** @private */
41269     createStripElements :  function(stripEl, text, closable, tpl)
41270     {
41271         var td = document.createElement("li"); // was td..
41272         td.className = 'nav-item';
41273         
41274         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41275         
41276         
41277         stripEl.appendChild(td);
41278         /*if(closable){
41279             td.className = "x-tabs-closable";
41280             if(!this.closeTpl){
41281                 this.closeTpl = new Roo.Template(
41282                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41283                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41284                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41285                 );
41286             }
41287             var el = this.closeTpl.overwrite(td, {"text": text});
41288             var close = el.getElementsByTagName("div")[0];
41289             var inner = el.getElementsByTagName("em")[0];
41290             return {"el": el, "close": close, "inner": inner};
41291         } else {
41292         */
41293         // not sure what this is..
41294 //            if(!this.tabTpl){
41295                 //this.tabTpl = new Roo.Template(
41296                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41297                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41298                 //);
41299 //                this.tabTpl = new Roo.Template(
41300 //                   '<a href="#">' +
41301 //                   '<span unselectable="on"' +
41302 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41303 //                            ' >{text}</span></a>'
41304 //                );
41305 //                
41306 //            }
41307
41308
41309             var template = tpl || this.tabTpl || false;
41310             
41311             if(!template){
41312                 template =  new Roo.Template(
41313                         Roo.bootstrap.version == 4 ? 
41314                             (
41315                                 '<a class="nav-link" href="#" unselectable="on"' +
41316                                      (this.disableTooltips ? '' : ' title="{text}"') +
41317                                      ' >{text}</a>'
41318                             ) : (
41319                                 '<a class="nav-link" href="#">' +
41320                                 '<span unselectable="on"' +
41321                                          (this.disableTooltips ? '' : ' title="{text}"') +
41322                                     ' >{text}</span></a>'
41323                             )
41324                 );
41325             }
41326             
41327             switch (typeof(template)) {
41328                 case 'object' :
41329                     break;
41330                 case 'string' :
41331                     template = new Roo.Template(template);
41332                     break;
41333                 default :
41334                     break;
41335             }
41336             
41337             var el = template.overwrite(td, {"text": text});
41338             
41339             var inner = el.getElementsByTagName("span")[0];
41340             
41341             return {"el": el, "inner": inner};
41342             
41343     }
41344         
41345     
41346 });
41347
41348 /**
41349  * @class Roo.TabPanelItem
41350  * @extends Roo.util.Observable
41351  * Represents an individual item (tab plus body) in a TabPanel.
41352  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41353  * @param {String} id The id of this TabPanelItem
41354  * @param {String} text The text for the tab of this TabPanelItem
41355  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41356  */
41357 Roo.bootstrap.panel.TabItem = function(config){
41358     /**
41359      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41360      * @type Roo.TabPanel
41361      */
41362     this.tabPanel = config.panel;
41363     /**
41364      * The id for this TabPanelItem
41365      * @type String
41366      */
41367     this.id = config.id;
41368     /** @private */
41369     this.disabled = false;
41370     /** @private */
41371     this.text = config.text;
41372     /** @private */
41373     this.loaded = false;
41374     this.closable = config.closable;
41375
41376     /**
41377      * The body element for this TabPanelItem.
41378      * @type Roo.Element
41379      */
41380     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41381     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41382     this.bodyEl.setStyle("display", "block");
41383     this.bodyEl.setStyle("zoom", "1");
41384     //this.hideAction();
41385
41386     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41387     /** @private */
41388     this.el = Roo.get(els.el);
41389     this.inner = Roo.get(els.inner, true);
41390      this.textEl = Roo.bootstrap.version == 4 ?
41391         this.el : Roo.get(this.el.dom.firstChild, true);
41392
41393     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41394     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41395
41396     
41397 //    this.el.on("mousedown", this.onTabMouseDown, this);
41398     this.el.on("click", this.onTabClick, this);
41399     /** @private */
41400     if(config.closable){
41401         var c = Roo.get(els.close, true);
41402         c.dom.title = this.closeText;
41403         c.addClassOnOver("close-over");
41404         c.on("click", this.closeClick, this);
41405      }
41406
41407     this.addEvents({
41408          /**
41409          * @event activate
41410          * Fires when this tab becomes the active tab.
41411          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41412          * @param {Roo.TabPanelItem} this
41413          */
41414         "activate": true,
41415         /**
41416          * @event beforeclose
41417          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41418          * @param {Roo.TabPanelItem} this
41419          * @param {Object} e Set cancel to true on this object to cancel the close.
41420          */
41421         "beforeclose": true,
41422         /**
41423          * @event close
41424          * Fires when this tab is closed.
41425          * @param {Roo.TabPanelItem} this
41426          */
41427          "close": true,
41428         /**
41429          * @event deactivate
41430          * Fires when this tab is no longer the active tab.
41431          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41432          * @param {Roo.TabPanelItem} this
41433          */
41434          "deactivate" : true
41435     });
41436     this.hidden = false;
41437
41438     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41439 };
41440
41441 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41442            {
41443     purgeListeners : function(){
41444        Roo.util.Observable.prototype.purgeListeners.call(this);
41445        this.el.removeAllListeners();
41446     },
41447     /**
41448      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41449      */
41450     show : function(){
41451         this.status_node.addClass("active");
41452         this.showAction();
41453         if(Roo.isOpera){
41454             this.tabPanel.stripWrap.repaint();
41455         }
41456         this.fireEvent("activate", this.tabPanel, this);
41457     },
41458
41459     /**
41460      * Returns true if this tab is the active tab.
41461      * @return {Boolean}
41462      */
41463     isActive : function(){
41464         return this.tabPanel.getActiveTab() == this;
41465     },
41466
41467     /**
41468      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41469      */
41470     hide : function(){
41471         this.status_node.removeClass("active");
41472         this.hideAction();
41473         this.fireEvent("deactivate", this.tabPanel, this);
41474     },
41475
41476     hideAction : function(){
41477         this.bodyEl.hide();
41478         this.bodyEl.setStyle("position", "absolute");
41479         this.bodyEl.setLeft("-20000px");
41480         this.bodyEl.setTop("-20000px");
41481     },
41482
41483     showAction : function(){
41484         this.bodyEl.setStyle("position", "relative");
41485         this.bodyEl.setTop("");
41486         this.bodyEl.setLeft("");
41487         this.bodyEl.show();
41488     },
41489
41490     /**
41491      * Set the tooltip for the tab.
41492      * @param {String} tooltip The tab's tooltip
41493      */
41494     setTooltip : function(text){
41495         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41496             this.textEl.dom.qtip = text;
41497             this.textEl.dom.removeAttribute('title');
41498         }else{
41499             this.textEl.dom.title = text;
41500         }
41501     },
41502
41503     onTabClick : function(e){
41504         e.preventDefault();
41505         this.tabPanel.activate(this.id);
41506     },
41507
41508     onTabMouseDown : function(e){
41509         e.preventDefault();
41510         this.tabPanel.activate(this.id);
41511     },
41512 /*
41513     getWidth : function(){
41514         return this.inner.getWidth();
41515     },
41516
41517     setWidth : function(width){
41518         var iwidth = width - this.linode.getPadding("lr");
41519         this.inner.setWidth(iwidth);
41520         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41521         this.linode.setWidth(width);
41522     },
41523 */
41524     /**
41525      * Show or hide the tab
41526      * @param {Boolean} hidden True to hide or false to show.
41527      */
41528     setHidden : function(hidden){
41529         this.hidden = hidden;
41530         this.linode.setStyle("display", hidden ? "none" : "");
41531     },
41532
41533     /**
41534      * Returns true if this tab is "hidden"
41535      * @return {Boolean}
41536      */
41537     isHidden : function(){
41538         return this.hidden;
41539     },
41540
41541     /**
41542      * Returns the text for this tab
41543      * @return {String}
41544      */
41545     getText : function(){
41546         return this.text;
41547     },
41548     /*
41549     autoSize : function(){
41550         //this.el.beginMeasure();
41551         this.textEl.setWidth(1);
41552         /*
41553          *  #2804 [new] Tabs in Roojs
41554          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41555          */
41556         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41557         //this.el.endMeasure();
41558     //},
41559
41560     /**
41561      * Sets the text for the tab (Note: this also sets the tooltip text)
41562      * @param {String} text The tab's text and tooltip
41563      */
41564     setText : function(text){
41565         this.text = text;
41566         this.textEl.update(text);
41567         this.setTooltip(text);
41568         //if(!this.tabPanel.resizeTabs){
41569         //    this.autoSize();
41570         //}
41571     },
41572     /**
41573      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41574      */
41575     activate : function(){
41576         this.tabPanel.activate(this.id);
41577     },
41578
41579     /**
41580      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41581      */
41582     disable : function(){
41583         if(this.tabPanel.active != this){
41584             this.disabled = true;
41585             this.status_node.addClass("disabled");
41586         }
41587     },
41588
41589     /**
41590      * Enables this TabPanelItem if it was previously disabled.
41591      */
41592     enable : function(){
41593         this.disabled = false;
41594         this.status_node.removeClass("disabled");
41595     },
41596
41597     /**
41598      * Sets the content for this TabPanelItem.
41599      * @param {String} content The content
41600      * @param {Boolean} loadScripts true to look for and load scripts
41601      */
41602     setContent : function(content, loadScripts){
41603         this.bodyEl.update(content, loadScripts);
41604     },
41605
41606     /**
41607      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41608      * @return {Roo.UpdateManager} The UpdateManager
41609      */
41610     getUpdateManager : function(){
41611         return this.bodyEl.getUpdateManager();
41612     },
41613
41614     /**
41615      * Set a URL to be used to load the content for this TabPanelItem.
41616      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41617      * @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)
41618      * @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)
41619      * @return {Roo.UpdateManager} The UpdateManager
41620      */
41621     setUrl : function(url, params, loadOnce){
41622         if(this.refreshDelegate){
41623             this.un('activate', this.refreshDelegate);
41624         }
41625         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41626         this.on("activate", this.refreshDelegate);
41627         return this.bodyEl.getUpdateManager();
41628     },
41629
41630     /** @private */
41631     _handleRefresh : function(url, params, loadOnce){
41632         if(!loadOnce || !this.loaded){
41633             var updater = this.bodyEl.getUpdateManager();
41634             updater.update(url, params, this._setLoaded.createDelegate(this));
41635         }
41636     },
41637
41638     /**
41639      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41640      *   Will fail silently if the setUrl method has not been called.
41641      *   This does not activate the panel, just updates its content.
41642      */
41643     refresh : function(){
41644         if(this.refreshDelegate){
41645            this.loaded = false;
41646            this.refreshDelegate();
41647         }
41648     },
41649
41650     /** @private */
41651     _setLoaded : function(){
41652         this.loaded = true;
41653     },
41654
41655     /** @private */
41656     closeClick : function(e){
41657         var o = {};
41658         e.stopEvent();
41659         this.fireEvent("beforeclose", this, o);
41660         if(o.cancel !== true){
41661             this.tabPanel.removeTab(this.id);
41662         }
41663     },
41664     /**
41665      * The text displayed in the tooltip for the close icon.
41666      * @type String
41667      */
41668     closeText : "Close this tab"
41669 });
41670 /**
41671 *    This script refer to:
41672 *    Title: International Telephone Input
41673 *    Author: Jack O'Connor
41674 *    Code version:  v12.1.12
41675 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41676 **/
41677
41678 Roo.bootstrap.PhoneInputData = function() {
41679     var d = [
41680       [
41681         "Afghanistan (‫افغانستان‬‎)",
41682         "af",
41683         "93"
41684       ],
41685       [
41686         "Albania (Shqipëri)",
41687         "al",
41688         "355"
41689       ],
41690       [
41691         "Algeria (‫الجزائر‬‎)",
41692         "dz",
41693         "213"
41694       ],
41695       [
41696         "American Samoa",
41697         "as",
41698         "1684"
41699       ],
41700       [
41701         "Andorra",
41702         "ad",
41703         "376"
41704       ],
41705       [
41706         "Angola",
41707         "ao",
41708         "244"
41709       ],
41710       [
41711         "Anguilla",
41712         "ai",
41713         "1264"
41714       ],
41715       [
41716         "Antigua and Barbuda",
41717         "ag",
41718         "1268"
41719       ],
41720       [
41721         "Argentina",
41722         "ar",
41723         "54"
41724       ],
41725       [
41726         "Armenia (Հայաստան)",
41727         "am",
41728         "374"
41729       ],
41730       [
41731         "Aruba",
41732         "aw",
41733         "297"
41734       ],
41735       [
41736         "Australia",
41737         "au",
41738         "61",
41739         0
41740       ],
41741       [
41742         "Austria (Österreich)",
41743         "at",
41744         "43"
41745       ],
41746       [
41747         "Azerbaijan (Azərbaycan)",
41748         "az",
41749         "994"
41750       ],
41751       [
41752         "Bahamas",
41753         "bs",
41754         "1242"
41755       ],
41756       [
41757         "Bahrain (‫البحرين‬‎)",
41758         "bh",
41759         "973"
41760       ],
41761       [
41762         "Bangladesh (বাংলাদেশ)",
41763         "bd",
41764         "880"
41765       ],
41766       [
41767         "Barbados",
41768         "bb",
41769         "1246"
41770       ],
41771       [
41772         "Belarus (Беларусь)",
41773         "by",
41774         "375"
41775       ],
41776       [
41777         "Belgium (België)",
41778         "be",
41779         "32"
41780       ],
41781       [
41782         "Belize",
41783         "bz",
41784         "501"
41785       ],
41786       [
41787         "Benin (Bénin)",
41788         "bj",
41789         "229"
41790       ],
41791       [
41792         "Bermuda",
41793         "bm",
41794         "1441"
41795       ],
41796       [
41797         "Bhutan (འབྲུག)",
41798         "bt",
41799         "975"
41800       ],
41801       [
41802         "Bolivia",
41803         "bo",
41804         "591"
41805       ],
41806       [
41807         "Bosnia and Herzegovina (Босна и Херцеговина)",
41808         "ba",
41809         "387"
41810       ],
41811       [
41812         "Botswana",
41813         "bw",
41814         "267"
41815       ],
41816       [
41817         "Brazil (Brasil)",
41818         "br",
41819         "55"
41820       ],
41821       [
41822         "British Indian Ocean Territory",
41823         "io",
41824         "246"
41825       ],
41826       [
41827         "British Virgin Islands",
41828         "vg",
41829         "1284"
41830       ],
41831       [
41832         "Brunei",
41833         "bn",
41834         "673"
41835       ],
41836       [
41837         "Bulgaria (България)",
41838         "bg",
41839         "359"
41840       ],
41841       [
41842         "Burkina Faso",
41843         "bf",
41844         "226"
41845       ],
41846       [
41847         "Burundi (Uburundi)",
41848         "bi",
41849         "257"
41850       ],
41851       [
41852         "Cambodia (កម្ពុជា)",
41853         "kh",
41854         "855"
41855       ],
41856       [
41857         "Cameroon (Cameroun)",
41858         "cm",
41859         "237"
41860       ],
41861       [
41862         "Canada",
41863         "ca",
41864         "1",
41865         1,
41866         ["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"]
41867       ],
41868       [
41869         "Cape Verde (Kabu Verdi)",
41870         "cv",
41871         "238"
41872       ],
41873       [
41874         "Caribbean Netherlands",
41875         "bq",
41876         "599",
41877         1
41878       ],
41879       [
41880         "Cayman Islands",
41881         "ky",
41882         "1345"
41883       ],
41884       [
41885         "Central African Republic (République centrafricaine)",
41886         "cf",
41887         "236"
41888       ],
41889       [
41890         "Chad (Tchad)",
41891         "td",
41892         "235"
41893       ],
41894       [
41895         "Chile",
41896         "cl",
41897         "56"
41898       ],
41899       [
41900         "China (中国)",
41901         "cn",
41902         "86"
41903       ],
41904       [
41905         "Christmas Island",
41906         "cx",
41907         "61",
41908         2
41909       ],
41910       [
41911         "Cocos (Keeling) Islands",
41912         "cc",
41913         "61",
41914         1
41915       ],
41916       [
41917         "Colombia",
41918         "co",
41919         "57"
41920       ],
41921       [
41922         "Comoros (‫جزر القمر‬‎)",
41923         "km",
41924         "269"
41925       ],
41926       [
41927         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41928         "cd",
41929         "243"
41930       ],
41931       [
41932         "Congo (Republic) (Congo-Brazzaville)",
41933         "cg",
41934         "242"
41935       ],
41936       [
41937         "Cook Islands",
41938         "ck",
41939         "682"
41940       ],
41941       [
41942         "Costa Rica",
41943         "cr",
41944         "506"
41945       ],
41946       [
41947         "Côte d’Ivoire",
41948         "ci",
41949         "225"
41950       ],
41951       [
41952         "Croatia (Hrvatska)",
41953         "hr",
41954         "385"
41955       ],
41956       [
41957         "Cuba",
41958         "cu",
41959         "53"
41960       ],
41961       [
41962         "Curaçao",
41963         "cw",
41964         "599",
41965         0
41966       ],
41967       [
41968         "Cyprus (Κύπρος)",
41969         "cy",
41970         "357"
41971       ],
41972       [
41973         "Czech Republic (Česká republika)",
41974         "cz",
41975         "420"
41976       ],
41977       [
41978         "Denmark (Danmark)",
41979         "dk",
41980         "45"
41981       ],
41982       [
41983         "Djibouti",
41984         "dj",
41985         "253"
41986       ],
41987       [
41988         "Dominica",
41989         "dm",
41990         "1767"
41991       ],
41992       [
41993         "Dominican Republic (República Dominicana)",
41994         "do",
41995         "1",
41996         2,
41997         ["809", "829", "849"]
41998       ],
41999       [
42000         "Ecuador",
42001         "ec",
42002         "593"
42003       ],
42004       [
42005         "Egypt (‫مصر‬‎)",
42006         "eg",
42007         "20"
42008       ],
42009       [
42010         "El Salvador",
42011         "sv",
42012         "503"
42013       ],
42014       [
42015         "Equatorial Guinea (Guinea Ecuatorial)",
42016         "gq",
42017         "240"
42018       ],
42019       [
42020         "Eritrea",
42021         "er",
42022         "291"
42023       ],
42024       [
42025         "Estonia (Eesti)",
42026         "ee",
42027         "372"
42028       ],
42029       [
42030         "Ethiopia",
42031         "et",
42032         "251"
42033       ],
42034       [
42035         "Falkland Islands (Islas Malvinas)",
42036         "fk",
42037         "500"
42038       ],
42039       [
42040         "Faroe Islands (Føroyar)",
42041         "fo",
42042         "298"
42043       ],
42044       [
42045         "Fiji",
42046         "fj",
42047         "679"
42048       ],
42049       [
42050         "Finland (Suomi)",
42051         "fi",
42052         "358",
42053         0
42054       ],
42055       [
42056         "France",
42057         "fr",
42058         "33"
42059       ],
42060       [
42061         "French Guiana (Guyane française)",
42062         "gf",
42063         "594"
42064       ],
42065       [
42066         "French Polynesia (Polynésie française)",
42067         "pf",
42068         "689"
42069       ],
42070       [
42071         "Gabon",
42072         "ga",
42073         "241"
42074       ],
42075       [
42076         "Gambia",
42077         "gm",
42078         "220"
42079       ],
42080       [
42081         "Georgia (საქართველო)",
42082         "ge",
42083         "995"
42084       ],
42085       [
42086         "Germany (Deutschland)",
42087         "de",
42088         "49"
42089       ],
42090       [
42091         "Ghana (Gaana)",
42092         "gh",
42093         "233"
42094       ],
42095       [
42096         "Gibraltar",
42097         "gi",
42098         "350"
42099       ],
42100       [
42101         "Greece (Ελλάδα)",
42102         "gr",
42103         "30"
42104       ],
42105       [
42106         "Greenland (Kalaallit Nunaat)",
42107         "gl",
42108         "299"
42109       ],
42110       [
42111         "Grenada",
42112         "gd",
42113         "1473"
42114       ],
42115       [
42116         "Guadeloupe",
42117         "gp",
42118         "590",
42119         0
42120       ],
42121       [
42122         "Guam",
42123         "gu",
42124         "1671"
42125       ],
42126       [
42127         "Guatemala",
42128         "gt",
42129         "502"
42130       ],
42131       [
42132         "Guernsey",
42133         "gg",
42134         "44",
42135         1
42136       ],
42137       [
42138         "Guinea (Guinée)",
42139         "gn",
42140         "224"
42141       ],
42142       [
42143         "Guinea-Bissau (Guiné Bissau)",
42144         "gw",
42145         "245"
42146       ],
42147       [
42148         "Guyana",
42149         "gy",
42150         "592"
42151       ],
42152       [
42153         "Haiti",
42154         "ht",
42155         "509"
42156       ],
42157       [
42158         "Honduras",
42159         "hn",
42160         "504"
42161       ],
42162       [
42163         "Hong Kong (香港)",
42164         "hk",
42165         "852"
42166       ],
42167       [
42168         "Hungary (Magyarország)",
42169         "hu",
42170         "36"
42171       ],
42172       [
42173         "Iceland (Ísland)",
42174         "is",
42175         "354"
42176       ],
42177       [
42178         "India (भारत)",
42179         "in",
42180         "91"
42181       ],
42182       [
42183         "Indonesia",
42184         "id",
42185         "62"
42186       ],
42187       [
42188         "Iran (‫ایران‬‎)",
42189         "ir",
42190         "98"
42191       ],
42192       [
42193         "Iraq (‫العراق‬‎)",
42194         "iq",
42195         "964"
42196       ],
42197       [
42198         "Ireland",
42199         "ie",
42200         "353"
42201       ],
42202       [
42203         "Isle of Man",
42204         "im",
42205         "44",
42206         2
42207       ],
42208       [
42209         "Israel (‫ישראל‬‎)",
42210         "il",
42211         "972"
42212       ],
42213       [
42214         "Italy (Italia)",
42215         "it",
42216         "39",
42217         0
42218       ],
42219       [
42220         "Jamaica",
42221         "jm",
42222         "1876"
42223       ],
42224       [
42225         "Japan (日本)",
42226         "jp",
42227         "81"
42228       ],
42229       [
42230         "Jersey",
42231         "je",
42232         "44",
42233         3
42234       ],
42235       [
42236         "Jordan (‫الأردن‬‎)",
42237         "jo",
42238         "962"
42239       ],
42240       [
42241         "Kazakhstan (Казахстан)",
42242         "kz",
42243         "7",
42244         1
42245       ],
42246       [
42247         "Kenya",
42248         "ke",
42249         "254"
42250       ],
42251       [
42252         "Kiribati",
42253         "ki",
42254         "686"
42255       ],
42256       [
42257         "Kosovo",
42258         "xk",
42259         "383"
42260       ],
42261       [
42262         "Kuwait (‫الكويت‬‎)",
42263         "kw",
42264         "965"
42265       ],
42266       [
42267         "Kyrgyzstan (Кыргызстан)",
42268         "kg",
42269         "996"
42270       ],
42271       [
42272         "Laos (ລາວ)",
42273         "la",
42274         "856"
42275       ],
42276       [
42277         "Latvia (Latvija)",
42278         "lv",
42279         "371"
42280       ],
42281       [
42282         "Lebanon (‫لبنان‬‎)",
42283         "lb",
42284         "961"
42285       ],
42286       [
42287         "Lesotho",
42288         "ls",
42289         "266"
42290       ],
42291       [
42292         "Liberia",
42293         "lr",
42294         "231"
42295       ],
42296       [
42297         "Libya (‫ليبيا‬‎)",
42298         "ly",
42299         "218"
42300       ],
42301       [
42302         "Liechtenstein",
42303         "li",
42304         "423"
42305       ],
42306       [
42307         "Lithuania (Lietuva)",
42308         "lt",
42309         "370"
42310       ],
42311       [
42312         "Luxembourg",
42313         "lu",
42314         "352"
42315       ],
42316       [
42317         "Macau (澳門)",
42318         "mo",
42319         "853"
42320       ],
42321       [
42322         "Macedonia (FYROM) (Македонија)",
42323         "mk",
42324         "389"
42325       ],
42326       [
42327         "Madagascar (Madagasikara)",
42328         "mg",
42329         "261"
42330       ],
42331       [
42332         "Malawi",
42333         "mw",
42334         "265"
42335       ],
42336       [
42337         "Malaysia",
42338         "my",
42339         "60"
42340       ],
42341       [
42342         "Maldives",
42343         "mv",
42344         "960"
42345       ],
42346       [
42347         "Mali",
42348         "ml",
42349         "223"
42350       ],
42351       [
42352         "Malta",
42353         "mt",
42354         "356"
42355       ],
42356       [
42357         "Marshall Islands",
42358         "mh",
42359         "692"
42360       ],
42361       [
42362         "Martinique",
42363         "mq",
42364         "596"
42365       ],
42366       [
42367         "Mauritania (‫موريتانيا‬‎)",
42368         "mr",
42369         "222"
42370       ],
42371       [
42372         "Mauritius (Moris)",
42373         "mu",
42374         "230"
42375       ],
42376       [
42377         "Mayotte",
42378         "yt",
42379         "262",
42380         1
42381       ],
42382       [
42383         "Mexico (México)",
42384         "mx",
42385         "52"
42386       ],
42387       [
42388         "Micronesia",
42389         "fm",
42390         "691"
42391       ],
42392       [
42393         "Moldova (Republica Moldova)",
42394         "md",
42395         "373"
42396       ],
42397       [
42398         "Monaco",
42399         "mc",
42400         "377"
42401       ],
42402       [
42403         "Mongolia (Монгол)",
42404         "mn",
42405         "976"
42406       ],
42407       [
42408         "Montenegro (Crna Gora)",
42409         "me",
42410         "382"
42411       ],
42412       [
42413         "Montserrat",
42414         "ms",
42415         "1664"
42416       ],
42417       [
42418         "Morocco (‫المغرب‬‎)",
42419         "ma",
42420         "212",
42421         0
42422       ],
42423       [
42424         "Mozambique (Moçambique)",
42425         "mz",
42426         "258"
42427       ],
42428       [
42429         "Myanmar (Burma) (မြန်မာ)",
42430         "mm",
42431         "95"
42432       ],
42433       [
42434         "Namibia (Namibië)",
42435         "na",
42436         "264"
42437       ],
42438       [
42439         "Nauru",
42440         "nr",
42441         "674"
42442       ],
42443       [
42444         "Nepal (नेपाल)",
42445         "np",
42446         "977"
42447       ],
42448       [
42449         "Netherlands (Nederland)",
42450         "nl",
42451         "31"
42452       ],
42453       [
42454         "New Caledonia (Nouvelle-Calédonie)",
42455         "nc",
42456         "687"
42457       ],
42458       [
42459         "New Zealand",
42460         "nz",
42461         "64"
42462       ],
42463       [
42464         "Nicaragua",
42465         "ni",
42466         "505"
42467       ],
42468       [
42469         "Niger (Nijar)",
42470         "ne",
42471         "227"
42472       ],
42473       [
42474         "Nigeria",
42475         "ng",
42476         "234"
42477       ],
42478       [
42479         "Niue",
42480         "nu",
42481         "683"
42482       ],
42483       [
42484         "Norfolk Island",
42485         "nf",
42486         "672"
42487       ],
42488       [
42489         "North Korea (조선 민주주의 인민 공화국)",
42490         "kp",
42491         "850"
42492       ],
42493       [
42494         "Northern Mariana Islands",
42495         "mp",
42496         "1670"
42497       ],
42498       [
42499         "Norway (Norge)",
42500         "no",
42501         "47",
42502         0
42503       ],
42504       [
42505         "Oman (‫عُمان‬‎)",
42506         "om",
42507         "968"
42508       ],
42509       [
42510         "Pakistan (‫پاکستان‬‎)",
42511         "pk",
42512         "92"
42513       ],
42514       [
42515         "Palau",
42516         "pw",
42517         "680"
42518       ],
42519       [
42520         "Palestine (‫فلسطين‬‎)",
42521         "ps",
42522         "970"
42523       ],
42524       [
42525         "Panama (Panamá)",
42526         "pa",
42527         "507"
42528       ],
42529       [
42530         "Papua New Guinea",
42531         "pg",
42532         "675"
42533       ],
42534       [
42535         "Paraguay",
42536         "py",
42537         "595"
42538       ],
42539       [
42540         "Peru (Perú)",
42541         "pe",
42542         "51"
42543       ],
42544       [
42545         "Philippines",
42546         "ph",
42547         "63"
42548       ],
42549       [
42550         "Poland (Polska)",
42551         "pl",
42552         "48"
42553       ],
42554       [
42555         "Portugal",
42556         "pt",
42557         "351"
42558       ],
42559       [
42560         "Puerto Rico",
42561         "pr",
42562         "1",
42563         3,
42564         ["787", "939"]
42565       ],
42566       [
42567         "Qatar (‫قطر‬‎)",
42568         "qa",
42569         "974"
42570       ],
42571       [
42572         "Réunion (La Réunion)",
42573         "re",
42574         "262",
42575         0
42576       ],
42577       [
42578         "Romania (România)",
42579         "ro",
42580         "40"
42581       ],
42582       [
42583         "Russia (Россия)",
42584         "ru",
42585         "7",
42586         0
42587       ],
42588       [
42589         "Rwanda",
42590         "rw",
42591         "250"
42592       ],
42593       [
42594         "Saint Barthélemy",
42595         "bl",
42596         "590",
42597         1
42598       ],
42599       [
42600         "Saint Helena",
42601         "sh",
42602         "290"
42603       ],
42604       [
42605         "Saint Kitts and Nevis",
42606         "kn",
42607         "1869"
42608       ],
42609       [
42610         "Saint Lucia",
42611         "lc",
42612         "1758"
42613       ],
42614       [
42615         "Saint Martin (Saint-Martin (partie française))",
42616         "mf",
42617         "590",
42618         2
42619       ],
42620       [
42621         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42622         "pm",
42623         "508"
42624       ],
42625       [
42626         "Saint Vincent and the Grenadines",
42627         "vc",
42628         "1784"
42629       ],
42630       [
42631         "Samoa",
42632         "ws",
42633         "685"
42634       ],
42635       [
42636         "San Marino",
42637         "sm",
42638         "378"
42639       ],
42640       [
42641         "São Tomé and Príncipe (São Tomé e Príncipe)",
42642         "st",
42643         "239"
42644       ],
42645       [
42646         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42647         "sa",
42648         "966"
42649       ],
42650       [
42651         "Senegal (Sénégal)",
42652         "sn",
42653         "221"
42654       ],
42655       [
42656         "Serbia (Србија)",
42657         "rs",
42658         "381"
42659       ],
42660       [
42661         "Seychelles",
42662         "sc",
42663         "248"
42664       ],
42665       [
42666         "Sierra Leone",
42667         "sl",
42668         "232"
42669       ],
42670       [
42671         "Singapore",
42672         "sg",
42673         "65"
42674       ],
42675       [
42676         "Sint Maarten",
42677         "sx",
42678         "1721"
42679       ],
42680       [
42681         "Slovakia (Slovensko)",
42682         "sk",
42683         "421"
42684       ],
42685       [
42686         "Slovenia (Slovenija)",
42687         "si",
42688         "386"
42689       ],
42690       [
42691         "Solomon Islands",
42692         "sb",
42693         "677"
42694       ],
42695       [
42696         "Somalia (Soomaaliya)",
42697         "so",
42698         "252"
42699       ],
42700       [
42701         "South Africa",
42702         "za",
42703         "27"
42704       ],
42705       [
42706         "South Korea (대한민국)",
42707         "kr",
42708         "82"
42709       ],
42710       [
42711         "South Sudan (‫جنوب السودان‬‎)",
42712         "ss",
42713         "211"
42714       ],
42715       [
42716         "Spain (España)",
42717         "es",
42718         "34"
42719       ],
42720       [
42721         "Sri Lanka (ශ්‍රී ලංකාව)",
42722         "lk",
42723         "94"
42724       ],
42725       [
42726         "Sudan (‫السودان‬‎)",
42727         "sd",
42728         "249"
42729       ],
42730       [
42731         "Suriname",
42732         "sr",
42733         "597"
42734       ],
42735       [
42736         "Svalbard and Jan Mayen",
42737         "sj",
42738         "47",
42739         1
42740       ],
42741       [
42742         "Swaziland",
42743         "sz",
42744         "268"
42745       ],
42746       [
42747         "Sweden (Sverige)",
42748         "se",
42749         "46"
42750       ],
42751       [
42752         "Switzerland (Schweiz)",
42753         "ch",
42754         "41"
42755       ],
42756       [
42757         "Syria (‫سوريا‬‎)",
42758         "sy",
42759         "963"
42760       ],
42761       [
42762         "Taiwan (台灣)",
42763         "tw",
42764         "886"
42765       ],
42766       [
42767         "Tajikistan",
42768         "tj",
42769         "992"
42770       ],
42771       [
42772         "Tanzania",
42773         "tz",
42774         "255"
42775       ],
42776       [
42777         "Thailand (ไทย)",
42778         "th",
42779         "66"
42780       ],
42781       [
42782         "Timor-Leste",
42783         "tl",
42784         "670"
42785       ],
42786       [
42787         "Togo",
42788         "tg",
42789         "228"
42790       ],
42791       [
42792         "Tokelau",
42793         "tk",
42794         "690"
42795       ],
42796       [
42797         "Tonga",
42798         "to",
42799         "676"
42800       ],
42801       [
42802         "Trinidad and Tobago",
42803         "tt",
42804         "1868"
42805       ],
42806       [
42807         "Tunisia (‫تونس‬‎)",
42808         "tn",
42809         "216"
42810       ],
42811       [
42812         "Turkey (Türkiye)",
42813         "tr",
42814         "90"
42815       ],
42816       [
42817         "Turkmenistan",
42818         "tm",
42819         "993"
42820       ],
42821       [
42822         "Turks and Caicos Islands",
42823         "tc",
42824         "1649"
42825       ],
42826       [
42827         "Tuvalu",
42828         "tv",
42829         "688"
42830       ],
42831       [
42832         "U.S. Virgin Islands",
42833         "vi",
42834         "1340"
42835       ],
42836       [
42837         "Uganda",
42838         "ug",
42839         "256"
42840       ],
42841       [
42842         "Ukraine (Україна)",
42843         "ua",
42844         "380"
42845       ],
42846       [
42847         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42848         "ae",
42849         "971"
42850       ],
42851       [
42852         "United Kingdom",
42853         "gb",
42854         "44",
42855         0
42856       ],
42857       [
42858         "United States",
42859         "us",
42860         "1",
42861         0
42862       ],
42863       [
42864         "Uruguay",
42865         "uy",
42866         "598"
42867       ],
42868       [
42869         "Uzbekistan (Oʻzbekiston)",
42870         "uz",
42871         "998"
42872       ],
42873       [
42874         "Vanuatu",
42875         "vu",
42876         "678"
42877       ],
42878       [
42879         "Vatican City (Città del Vaticano)",
42880         "va",
42881         "39",
42882         1
42883       ],
42884       [
42885         "Venezuela",
42886         "ve",
42887         "58"
42888       ],
42889       [
42890         "Vietnam (Việt Nam)",
42891         "vn",
42892         "84"
42893       ],
42894       [
42895         "Wallis and Futuna (Wallis-et-Futuna)",
42896         "wf",
42897         "681"
42898       ],
42899       [
42900         "Western Sahara (‫الصحراء الغربية‬‎)",
42901         "eh",
42902         "212",
42903         1
42904       ],
42905       [
42906         "Yemen (‫اليمن‬‎)",
42907         "ye",
42908         "967"
42909       ],
42910       [
42911         "Zambia",
42912         "zm",
42913         "260"
42914       ],
42915       [
42916         "Zimbabwe",
42917         "zw",
42918         "263"
42919       ],
42920       [
42921         "Åland Islands",
42922         "ax",
42923         "358",
42924         1
42925       ]
42926   ];
42927   
42928   return d;
42929 }/**
42930 *    This script refer to:
42931 *    Title: International Telephone Input
42932 *    Author: Jack O'Connor
42933 *    Code version:  v12.1.12
42934 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42935 **/
42936
42937 /**
42938  * @class Roo.bootstrap.PhoneInput
42939  * @extends Roo.bootstrap.TriggerField
42940  * An input with International dial-code selection
42941  
42942  * @cfg {String} defaultDialCode default '+852'
42943  * @cfg {Array} preferedCountries default []
42944   
42945  * @constructor
42946  * Create a new PhoneInput.
42947  * @param {Object} config Configuration options
42948  */
42949
42950 Roo.bootstrap.PhoneInput = function(config) {
42951     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42952 };
42953
42954 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42955         
42956         listWidth: undefined,
42957         
42958         selectedClass: 'active',
42959         
42960         invalidClass : "has-warning",
42961         
42962         validClass: 'has-success',
42963         
42964         allowed: '0123456789',
42965         
42966         max_length: 15,
42967         
42968         /**
42969          * @cfg {String} defaultDialCode The default dial code when initializing the input
42970          */
42971         defaultDialCode: '+852',
42972         
42973         /**
42974          * @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
42975          */
42976         preferedCountries: false,
42977         
42978         getAutoCreate : function()
42979         {
42980             var data = Roo.bootstrap.PhoneInputData();
42981             var align = this.labelAlign || this.parentLabelAlign();
42982             var id = Roo.id();
42983             
42984             this.allCountries = [];
42985             this.dialCodeMapping = [];
42986             
42987             for (var i = 0; i < data.length; i++) {
42988               var c = data[i];
42989               this.allCountries[i] = {
42990                 name: c[0],
42991                 iso2: c[1],
42992                 dialCode: c[2],
42993                 priority: c[3] || 0,
42994                 areaCodes: c[4] || null
42995               };
42996               this.dialCodeMapping[c[2]] = {
42997                   name: c[0],
42998                   iso2: c[1],
42999                   priority: c[3] || 0,
43000                   areaCodes: c[4] || null
43001               };
43002             }
43003             
43004             var cfg = {
43005                 cls: 'form-group',
43006                 cn: []
43007             };
43008             
43009             var input =  {
43010                 tag: 'input',
43011                 id : id,
43012                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43013                 maxlength: this.max_length,
43014                 cls : 'form-control tel-input',
43015                 autocomplete: 'new-password'
43016             };
43017             
43018             var hiddenInput = {
43019                 tag: 'input',
43020                 type: 'hidden',
43021                 cls: 'hidden-tel-input'
43022             };
43023             
43024             if (this.name) {
43025                 hiddenInput.name = this.name;
43026             }
43027             
43028             if (this.disabled) {
43029                 input.disabled = true;
43030             }
43031             
43032             var flag_container = {
43033                 tag: 'div',
43034                 cls: 'flag-box',
43035                 cn: [
43036                     {
43037                         tag: 'div',
43038                         cls: 'flag'
43039                     },
43040                     {
43041                         tag: 'div',
43042                         cls: 'caret'
43043                     }
43044                 ]
43045             };
43046             
43047             var box = {
43048                 tag: 'div',
43049                 cls: this.hasFeedback ? 'has-feedback' : '',
43050                 cn: [
43051                     hiddenInput,
43052                     input,
43053                     {
43054                         tag: 'input',
43055                         cls: 'dial-code-holder',
43056                         disabled: true
43057                     }
43058                 ]
43059             };
43060             
43061             var container = {
43062                 cls: 'roo-select2-container input-group',
43063                 cn: [
43064                     flag_container,
43065                     box
43066                 ]
43067             };
43068             
43069             if (this.fieldLabel.length) {
43070                 var indicator = {
43071                     tag: 'i',
43072                     tooltip: 'This field is required'
43073                 };
43074                 
43075                 var label = {
43076                     tag: 'label',
43077                     'for':  id,
43078                     cls: 'control-label',
43079                     cn: []
43080                 };
43081                 
43082                 var label_text = {
43083                     tag: 'span',
43084                     html: this.fieldLabel
43085                 };
43086                 
43087                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43088                 label.cn = [
43089                     indicator,
43090                     label_text
43091                 ];
43092                 
43093                 if(this.indicatorpos == 'right') {
43094                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43095                     label.cn = [
43096                         label_text,
43097                         indicator
43098                     ];
43099                 }
43100                 
43101                 if(align == 'left') {
43102                     container = {
43103                         tag: 'div',
43104                         cn: [
43105                             container
43106                         ]
43107                     };
43108                     
43109                     if(this.labelWidth > 12){
43110                         label.style = "width: " + this.labelWidth + 'px';
43111                     }
43112                     if(this.labelWidth < 13 && this.labelmd == 0){
43113                         this.labelmd = this.labelWidth;
43114                     }
43115                     if(this.labellg > 0){
43116                         label.cls += ' col-lg-' + this.labellg;
43117                         input.cls += ' col-lg-' + (12 - this.labellg);
43118                     }
43119                     if(this.labelmd > 0){
43120                         label.cls += ' col-md-' + this.labelmd;
43121                         container.cls += ' col-md-' + (12 - this.labelmd);
43122                     }
43123                     if(this.labelsm > 0){
43124                         label.cls += ' col-sm-' + this.labelsm;
43125                         container.cls += ' col-sm-' + (12 - this.labelsm);
43126                     }
43127                     if(this.labelxs > 0){
43128                         label.cls += ' col-xs-' + this.labelxs;
43129                         container.cls += ' col-xs-' + (12 - this.labelxs);
43130                     }
43131                 }
43132             }
43133             
43134             cfg.cn = [
43135                 label,
43136                 container
43137             ];
43138             
43139             var settings = this;
43140             
43141             ['xs','sm','md','lg'].map(function(size){
43142                 if (settings[size]) {
43143                     cfg.cls += ' col-' + size + '-' + settings[size];
43144                 }
43145             });
43146             
43147             this.store = new Roo.data.Store({
43148                 proxy : new Roo.data.MemoryProxy({}),
43149                 reader : new Roo.data.JsonReader({
43150                     fields : [
43151                         {
43152                             'name' : 'name',
43153                             'type' : 'string'
43154                         },
43155                         {
43156                             'name' : 'iso2',
43157                             'type' : 'string'
43158                         },
43159                         {
43160                             'name' : 'dialCode',
43161                             'type' : 'string'
43162                         },
43163                         {
43164                             'name' : 'priority',
43165                             'type' : 'string'
43166                         },
43167                         {
43168                             'name' : 'areaCodes',
43169                             'type' : 'string'
43170                         }
43171                     ]
43172                 })
43173             });
43174             
43175             if(!this.preferedCountries) {
43176                 this.preferedCountries = [
43177                     'hk',
43178                     'gb',
43179                     'us'
43180                 ];
43181             }
43182             
43183             var p = this.preferedCountries.reverse();
43184             
43185             if(p) {
43186                 for (var i = 0; i < p.length; i++) {
43187                     for (var j = 0; j < this.allCountries.length; j++) {
43188                         if(this.allCountries[j].iso2 == p[i]) {
43189                             var t = this.allCountries[j];
43190                             this.allCountries.splice(j,1);
43191                             this.allCountries.unshift(t);
43192                         }
43193                     } 
43194                 }
43195             }
43196             
43197             this.store.proxy.data = {
43198                 success: true,
43199                 data: this.allCountries
43200             };
43201             
43202             return cfg;
43203         },
43204         
43205         initEvents : function()
43206         {
43207             this.createList();
43208             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43209             
43210             this.indicator = this.indicatorEl();
43211             this.flag = this.flagEl();
43212             this.dialCodeHolder = this.dialCodeHolderEl();
43213             
43214             this.trigger = this.el.select('div.flag-box',true).first();
43215             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43216             
43217             var _this = this;
43218             
43219             (function(){
43220                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43221                 _this.list.setWidth(lw);
43222             }).defer(100);
43223             
43224             this.list.on('mouseover', this.onViewOver, this);
43225             this.list.on('mousemove', this.onViewMove, this);
43226             this.inputEl().on("keyup", this.onKeyUp, this);
43227             this.inputEl().on("keypress", this.onKeyPress, this);
43228             
43229             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43230
43231             this.view = new Roo.View(this.list, this.tpl, {
43232                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43233             });
43234             
43235             this.view.on('click', this.onViewClick, this);
43236             this.setValue(this.defaultDialCode);
43237         },
43238         
43239         onTriggerClick : function(e)
43240         {
43241             Roo.log('trigger click');
43242             if(this.disabled){
43243                 return;
43244             }
43245             
43246             if(this.isExpanded()){
43247                 this.collapse();
43248                 this.hasFocus = false;
43249             }else {
43250                 this.store.load({});
43251                 this.hasFocus = true;
43252                 this.expand();
43253             }
43254         },
43255         
43256         isExpanded : function()
43257         {
43258             return this.list.isVisible();
43259         },
43260         
43261         collapse : function()
43262         {
43263             if(!this.isExpanded()){
43264                 return;
43265             }
43266             this.list.hide();
43267             Roo.get(document).un('mousedown', this.collapseIf, this);
43268             Roo.get(document).un('mousewheel', this.collapseIf, this);
43269             this.fireEvent('collapse', this);
43270             this.validate();
43271         },
43272         
43273         expand : function()
43274         {
43275             Roo.log('expand');
43276
43277             if(this.isExpanded() || !this.hasFocus){
43278                 return;
43279             }
43280             
43281             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43282             this.list.setWidth(lw);
43283             
43284             this.list.show();
43285             this.restrictHeight();
43286             
43287             Roo.get(document).on('mousedown', this.collapseIf, this);
43288             Roo.get(document).on('mousewheel', this.collapseIf, this);
43289             
43290             this.fireEvent('expand', this);
43291         },
43292         
43293         restrictHeight : function()
43294         {
43295             this.list.alignTo(this.inputEl(), this.listAlign);
43296             this.list.alignTo(this.inputEl(), this.listAlign);
43297         },
43298         
43299         onViewOver : function(e, t)
43300         {
43301             if(this.inKeyMode){
43302                 return;
43303             }
43304             var item = this.view.findItemFromChild(t);
43305             
43306             if(item){
43307                 var index = this.view.indexOf(item);
43308                 this.select(index, false);
43309             }
43310         },
43311
43312         // private
43313         onViewClick : function(view, doFocus, el, e)
43314         {
43315             var index = this.view.getSelectedIndexes()[0];
43316             
43317             var r = this.store.getAt(index);
43318             
43319             if(r){
43320                 this.onSelect(r, index);
43321             }
43322             if(doFocus !== false && !this.blockFocus){
43323                 this.inputEl().focus();
43324             }
43325         },
43326         
43327         onViewMove : function(e, t)
43328         {
43329             this.inKeyMode = false;
43330         },
43331         
43332         select : function(index, scrollIntoView)
43333         {
43334             this.selectedIndex = index;
43335             this.view.select(index);
43336             if(scrollIntoView !== false){
43337                 var el = this.view.getNode(index);
43338                 if(el){
43339                     this.list.scrollChildIntoView(el, false);
43340                 }
43341             }
43342         },
43343         
43344         createList : function()
43345         {
43346             this.list = Roo.get(document.body).createChild({
43347                 tag: 'ul',
43348                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43349                 style: 'display:none'
43350             });
43351             
43352             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43353         },
43354         
43355         collapseIf : function(e)
43356         {
43357             var in_combo  = e.within(this.el);
43358             var in_list =  e.within(this.list);
43359             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43360             
43361             if (in_combo || in_list || is_list) {
43362                 return;
43363             }
43364             this.collapse();
43365         },
43366         
43367         onSelect : function(record, index)
43368         {
43369             if(this.fireEvent('beforeselect', this, record, index) !== false){
43370                 
43371                 this.setFlagClass(record.data.iso2);
43372                 this.setDialCode(record.data.dialCode);
43373                 this.hasFocus = false;
43374                 this.collapse();
43375                 this.fireEvent('select', this, record, index);
43376             }
43377         },
43378         
43379         flagEl : function()
43380         {
43381             var flag = this.el.select('div.flag',true).first();
43382             if(!flag){
43383                 return false;
43384             }
43385             return flag;
43386         },
43387         
43388         dialCodeHolderEl : function()
43389         {
43390             var d = this.el.select('input.dial-code-holder',true).first();
43391             if(!d){
43392                 return false;
43393             }
43394             return d;
43395         },
43396         
43397         setDialCode : function(v)
43398         {
43399             this.dialCodeHolder.dom.value = '+'+v;
43400         },
43401         
43402         setFlagClass : function(n)
43403         {
43404             this.flag.dom.className = 'flag '+n;
43405         },
43406         
43407         getValue : function()
43408         {
43409             var v = this.inputEl().getValue();
43410             if(this.dialCodeHolder) {
43411                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43412             }
43413             return v;
43414         },
43415         
43416         setValue : function(v)
43417         {
43418             var d = this.getDialCode(v);
43419             
43420             //invalid dial code
43421             if(v.length == 0 || !d || d.length == 0) {
43422                 if(this.rendered){
43423                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43424                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43425                 }
43426                 return;
43427             }
43428             
43429             //valid dial code
43430             this.setFlagClass(this.dialCodeMapping[d].iso2);
43431             this.setDialCode(d);
43432             this.inputEl().dom.value = v.replace('+'+d,'');
43433             this.hiddenEl().dom.value = this.getValue();
43434             
43435             this.validate();
43436         },
43437         
43438         getDialCode : function(v)
43439         {
43440             v = v ||  '';
43441             
43442             if (v.length == 0) {
43443                 return this.dialCodeHolder.dom.value;
43444             }
43445             
43446             var dialCode = "";
43447             if (v.charAt(0) != "+") {
43448                 return false;
43449             }
43450             var numericChars = "";
43451             for (var i = 1; i < v.length; i++) {
43452               var c = v.charAt(i);
43453               if (!isNaN(c)) {
43454                 numericChars += c;
43455                 if (this.dialCodeMapping[numericChars]) {
43456                   dialCode = v.substr(1, i);
43457                 }
43458                 if (numericChars.length == 4) {
43459                   break;
43460                 }
43461               }
43462             }
43463             return dialCode;
43464         },
43465         
43466         reset : function()
43467         {
43468             this.setValue(this.defaultDialCode);
43469             this.validate();
43470         },
43471         
43472         hiddenEl : function()
43473         {
43474             return this.el.select('input.hidden-tel-input',true).first();
43475         },
43476         
43477         // after setting val
43478         onKeyUp : function(e){
43479             this.setValue(this.getValue());
43480         },
43481         
43482         onKeyPress : function(e){
43483             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43484                 e.stopEvent();
43485             }
43486         }
43487         
43488 });
43489 /**
43490  * @class Roo.bootstrap.MoneyField
43491  * @extends Roo.bootstrap.ComboBox
43492  * Bootstrap MoneyField class
43493  * 
43494  * @constructor
43495  * Create a new MoneyField.
43496  * @param {Object} config Configuration options
43497  */
43498
43499 Roo.bootstrap.MoneyField = function(config) {
43500     
43501     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43502     
43503 };
43504
43505 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43506     
43507     /**
43508      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43509      */
43510     allowDecimals : true,
43511     /**
43512      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43513      */
43514     decimalSeparator : ".",
43515     /**
43516      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43517      */
43518     decimalPrecision : 0,
43519     /**
43520      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43521      */
43522     allowNegative : true,
43523     /**
43524      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43525      */
43526     allowZero: true,
43527     /**
43528      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43529      */
43530     minValue : Number.NEGATIVE_INFINITY,
43531     /**
43532      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43533      */
43534     maxValue : Number.MAX_VALUE,
43535     /**
43536      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43537      */
43538     minText : "The minimum value for this field is {0}",
43539     /**
43540      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43541      */
43542     maxText : "The maximum value for this field is {0}",
43543     /**
43544      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43545      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43546      */
43547     nanText : "{0} is not a valid number",
43548     /**
43549      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43550      */
43551     castInt : true,
43552     /**
43553      * @cfg {String} defaults currency of the MoneyField
43554      * value should be in lkey
43555      */
43556     defaultCurrency : false,
43557     /**
43558      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43559      */
43560     thousandsDelimiter : false,
43561     /**
43562      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43563      */
43564     max_length: false,
43565     
43566     inputlg : 9,
43567     inputmd : 9,
43568     inputsm : 9,
43569     inputxs : 6,
43570     
43571     store : false,
43572     
43573     getAutoCreate : function()
43574     {
43575         var align = this.labelAlign || this.parentLabelAlign();
43576         
43577         var id = Roo.id();
43578
43579         var cfg = {
43580             cls: 'form-group',
43581             cn: []
43582         };
43583
43584         var input =  {
43585             tag: 'input',
43586             id : id,
43587             cls : 'form-control roo-money-amount-input',
43588             autocomplete: 'new-password'
43589         };
43590         
43591         var hiddenInput = {
43592             tag: 'input',
43593             type: 'hidden',
43594             id: Roo.id(),
43595             cls: 'hidden-number-input'
43596         };
43597         
43598         if(this.max_length) {
43599             input.maxlength = this.max_length; 
43600         }
43601         
43602         if (this.name) {
43603             hiddenInput.name = this.name;
43604         }
43605
43606         if (this.disabled) {
43607             input.disabled = true;
43608         }
43609
43610         var clg = 12 - this.inputlg;
43611         var cmd = 12 - this.inputmd;
43612         var csm = 12 - this.inputsm;
43613         var cxs = 12 - this.inputxs;
43614         
43615         var container = {
43616             tag : 'div',
43617             cls : 'row roo-money-field',
43618             cn : [
43619                 {
43620                     tag : 'div',
43621                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43622                     cn : [
43623                         {
43624                             tag : 'div',
43625                             cls: 'roo-select2-container input-group',
43626                             cn: [
43627                                 {
43628                                     tag : 'input',
43629                                     cls : 'form-control roo-money-currency-input',
43630                                     autocomplete: 'new-password',
43631                                     readOnly : 1,
43632                                     name : this.currencyName
43633                                 },
43634                                 {
43635                                     tag :'span',
43636                                     cls : 'input-group-addon',
43637                                     cn : [
43638                                         {
43639                                             tag: 'span',
43640                                             cls: 'caret'
43641                                         }
43642                                     ]
43643                                 }
43644                             ]
43645                         }
43646                     ]
43647                 },
43648                 {
43649                     tag : 'div',
43650                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43651                     cn : [
43652                         {
43653                             tag: 'div',
43654                             cls: this.hasFeedback ? 'has-feedback' : '',
43655                             cn: [
43656                                 input
43657                             ]
43658                         }
43659                     ]
43660                 }
43661             ]
43662             
43663         };
43664         
43665         if (this.fieldLabel.length) {
43666             var indicator = {
43667                 tag: 'i',
43668                 tooltip: 'This field is required'
43669             };
43670
43671             var label = {
43672                 tag: 'label',
43673                 'for':  id,
43674                 cls: 'control-label',
43675                 cn: []
43676             };
43677
43678             var label_text = {
43679                 tag: 'span',
43680                 html: this.fieldLabel
43681             };
43682
43683             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43684             label.cn = [
43685                 indicator,
43686                 label_text
43687             ];
43688
43689             if(this.indicatorpos == 'right') {
43690                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43691                 label.cn = [
43692                     label_text,
43693                     indicator
43694                 ];
43695             }
43696
43697             if(align == 'left') {
43698                 container = {
43699                     tag: 'div',
43700                     cn: [
43701                         container
43702                     ]
43703                 };
43704
43705                 if(this.labelWidth > 12){
43706                     label.style = "width: " + this.labelWidth + 'px';
43707                 }
43708                 if(this.labelWidth < 13 && this.labelmd == 0){
43709                     this.labelmd = this.labelWidth;
43710                 }
43711                 if(this.labellg > 0){
43712                     label.cls += ' col-lg-' + this.labellg;
43713                     input.cls += ' col-lg-' + (12 - this.labellg);
43714                 }
43715                 if(this.labelmd > 0){
43716                     label.cls += ' col-md-' + this.labelmd;
43717                     container.cls += ' col-md-' + (12 - this.labelmd);
43718                 }
43719                 if(this.labelsm > 0){
43720                     label.cls += ' col-sm-' + this.labelsm;
43721                     container.cls += ' col-sm-' + (12 - this.labelsm);
43722                 }
43723                 if(this.labelxs > 0){
43724                     label.cls += ' col-xs-' + this.labelxs;
43725                     container.cls += ' col-xs-' + (12 - this.labelxs);
43726                 }
43727             }
43728         }
43729
43730         cfg.cn = [
43731             label,
43732             container,
43733             hiddenInput
43734         ];
43735         
43736         var settings = this;
43737
43738         ['xs','sm','md','lg'].map(function(size){
43739             if (settings[size]) {
43740                 cfg.cls += ' col-' + size + '-' + settings[size];
43741             }
43742         });
43743         
43744         return cfg;
43745     },
43746     
43747     initEvents : function()
43748     {
43749         this.indicator = this.indicatorEl();
43750         
43751         this.initCurrencyEvent();
43752         
43753         this.initNumberEvent();
43754     },
43755     
43756     initCurrencyEvent : function()
43757     {
43758         if (!this.store) {
43759             throw "can not find store for combo";
43760         }
43761         
43762         this.store = Roo.factory(this.store, Roo.data);
43763         this.store.parent = this;
43764         
43765         this.createList();
43766         
43767         this.triggerEl = this.el.select('.input-group-addon', true).first();
43768         
43769         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43770         
43771         var _this = this;
43772         
43773         (function(){
43774             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43775             _this.list.setWidth(lw);
43776         }).defer(100);
43777         
43778         this.list.on('mouseover', this.onViewOver, this);
43779         this.list.on('mousemove', this.onViewMove, this);
43780         this.list.on('scroll', this.onViewScroll, this);
43781         
43782         if(!this.tpl){
43783             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43784         }
43785         
43786         this.view = new Roo.View(this.list, this.tpl, {
43787             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43788         });
43789         
43790         this.view.on('click', this.onViewClick, this);
43791         
43792         this.store.on('beforeload', this.onBeforeLoad, this);
43793         this.store.on('load', this.onLoad, this);
43794         this.store.on('loadexception', this.onLoadException, this);
43795         
43796         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43797             "up" : function(e){
43798                 this.inKeyMode = true;
43799                 this.selectPrev();
43800             },
43801
43802             "down" : function(e){
43803                 if(!this.isExpanded()){
43804                     this.onTriggerClick();
43805                 }else{
43806                     this.inKeyMode = true;
43807                     this.selectNext();
43808                 }
43809             },
43810
43811             "enter" : function(e){
43812                 this.collapse();
43813                 
43814                 if(this.fireEvent("specialkey", this, e)){
43815                     this.onViewClick(false);
43816                 }
43817                 
43818                 return true;
43819             },
43820
43821             "esc" : function(e){
43822                 this.collapse();
43823             },
43824
43825             "tab" : function(e){
43826                 this.collapse();
43827                 
43828                 if(this.fireEvent("specialkey", this, e)){
43829                     this.onViewClick(false);
43830                 }
43831                 
43832                 return true;
43833             },
43834
43835             scope : this,
43836
43837             doRelay : function(foo, bar, hname){
43838                 if(hname == 'down' || this.scope.isExpanded()){
43839                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43840                 }
43841                 return true;
43842             },
43843
43844             forceKeyDown: true
43845         });
43846         
43847         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43848         
43849     },
43850     
43851     initNumberEvent : function(e)
43852     {
43853         this.inputEl().on("keydown" , this.fireKey,  this);
43854         this.inputEl().on("focus", this.onFocus,  this);
43855         this.inputEl().on("blur", this.onBlur,  this);
43856         
43857         this.inputEl().relayEvent('keyup', this);
43858         
43859         if(this.indicator){
43860             this.indicator.addClass('invisible');
43861         }
43862  
43863         this.originalValue = this.getValue();
43864         
43865         if(this.validationEvent == 'keyup'){
43866             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43867             this.inputEl().on('keyup', this.filterValidation, this);
43868         }
43869         else if(this.validationEvent !== false){
43870             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43871         }
43872         
43873         if(this.selectOnFocus){
43874             this.on("focus", this.preFocus, this);
43875             
43876         }
43877         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43878             this.inputEl().on("keypress", this.filterKeys, this);
43879         } else {
43880             this.inputEl().relayEvent('keypress', this);
43881         }
43882         
43883         var allowed = "0123456789";
43884         
43885         if(this.allowDecimals){
43886             allowed += this.decimalSeparator;
43887         }
43888         
43889         if(this.allowNegative){
43890             allowed += "-";
43891         }
43892         
43893         if(this.thousandsDelimiter) {
43894             allowed += ",";
43895         }
43896         
43897         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43898         
43899         var keyPress = function(e){
43900             
43901             var k = e.getKey();
43902             
43903             var c = e.getCharCode();
43904             
43905             if(
43906                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43907                     allowed.indexOf(String.fromCharCode(c)) === -1
43908             ){
43909                 e.stopEvent();
43910                 return;
43911             }
43912             
43913             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43914                 return;
43915             }
43916             
43917             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43918                 e.stopEvent();
43919             }
43920         };
43921         
43922         this.inputEl().on("keypress", keyPress, this);
43923         
43924     },
43925     
43926     onTriggerClick : function(e)
43927     {   
43928         if(this.disabled){
43929             return;
43930         }
43931         
43932         this.page = 0;
43933         this.loadNext = false;
43934         
43935         if(this.isExpanded()){
43936             this.collapse();
43937             return;
43938         }
43939         
43940         this.hasFocus = true;
43941         
43942         if(this.triggerAction == 'all') {
43943             this.doQuery(this.allQuery, true);
43944             return;
43945         }
43946         
43947         this.doQuery(this.getRawValue());
43948     },
43949     
43950     getCurrency : function()
43951     {   
43952         var v = this.currencyEl().getValue();
43953         
43954         return v;
43955     },
43956     
43957     restrictHeight : function()
43958     {
43959         this.list.alignTo(this.currencyEl(), this.listAlign);
43960         this.list.alignTo(this.currencyEl(), this.listAlign);
43961     },
43962     
43963     onViewClick : function(view, doFocus, el, e)
43964     {
43965         var index = this.view.getSelectedIndexes()[0];
43966         
43967         var r = this.store.getAt(index);
43968         
43969         if(r){
43970             this.onSelect(r, index);
43971         }
43972     },
43973     
43974     onSelect : function(record, index){
43975         
43976         if(this.fireEvent('beforeselect', this, record, index) !== false){
43977         
43978             this.setFromCurrencyData(index > -1 ? record.data : false);
43979             
43980             this.collapse();
43981             
43982             this.fireEvent('select', this, record, index);
43983         }
43984     },
43985     
43986     setFromCurrencyData : function(o)
43987     {
43988         var currency = '';
43989         
43990         this.lastCurrency = o;
43991         
43992         if (this.currencyField) {
43993             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43994         } else {
43995             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43996         }
43997         
43998         this.lastSelectionText = currency;
43999         
44000         //setting default currency
44001         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44002             this.setCurrency(this.defaultCurrency);
44003             return;
44004         }
44005         
44006         this.setCurrency(currency);
44007     },
44008     
44009     setFromData : function(o)
44010     {
44011         var c = {};
44012         
44013         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44014         
44015         this.setFromCurrencyData(c);
44016         
44017         var value = '';
44018         
44019         if (this.name) {
44020             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44021         } else {
44022             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44023         }
44024         
44025         this.setValue(value);
44026         
44027     },
44028     
44029     setCurrency : function(v)
44030     {   
44031         this.currencyValue = v;
44032         
44033         if(this.rendered){
44034             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44035             this.validate();
44036         }
44037     },
44038     
44039     setValue : function(v)
44040     {
44041         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44042         
44043         this.value = v;
44044         
44045         if(this.rendered){
44046             
44047             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44048             
44049             this.inputEl().dom.value = (v == '') ? '' :
44050                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44051             
44052             if(!this.allowZero && v === '0') {
44053                 this.hiddenEl().dom.value = '';
44054                 this.inputEl().dom.value = '';
44055             }
44056             
44057             this.validate();
44058         }
44059     },
44060     
44061     getRawValue : function()
44062     {
44063         var v = this.inputEl().getValue();
44064         
44065         return v;
44066     },
44067     
44068     getValue : function()
44069     {
44070         return this.fixPrecision(this.parseValue(this.getRawValue()));
44071     },
44072     
44073     parseValue : function(value)
44074     {
44075         if(this.thousandsDelimiter) {
44076             value += "";
44077             r = new RegExp(",", "g");
44078             value = value.replace(r, "");
44079         }
44080         
44081         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44082         return isNaN(value) ? '' : value;
44083         
44084     },
44085     
44086     fixPrecision : function(value)
44087     {
44088         if(this.thousandsDelimiter) {
44089             value += "";
44090             r = new RegExp(",", "g");
44091             value = value.replace(r, "");
44092         }
44093         
44094         var nan = isNaN(value);
44095         
44096         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44097             return nan ? '' : value;
44098         }
44099         return parseFloat(value).toFixed(this.decimalPrecision);
44100     },
44101     
44102     decimalPrecisionFcn : function(v)
44103     {
44104         return Math.floor(v);
44105     },
44106     
44107     validateValue : function(value)
44108     {
44109         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44110             return false;
44111         }
44112         
44113         var num = this.parseValue(value);
44114         
44115         if(isNaN(num)){
44116             this.markInvalid(String.format(this.nanText, value));
44117             return false;
44118         }
44119         
44120         if(num < this.minValue){
44121             this.markInvalid(String.format(this.minText, this.minValue));
44122             return false;
44123         }
44124         
44125         if(num > this.maxValue){
44126             this.markInvalid(String.format(this.maxText, this.maxValue));
44127             return false;
44128         }
44129         
44130         return true;
44131     },
44132     
44133     validate : function()
44134     {
44135         if(this.disabled || this.allowBlank){
44136             this.markValid();
44137             return true;
44138         }
44139         
44140         var currency = this.getCurrency();
44141         
44142         if(this.validateValue(this.getRawValue()) && currency.length){
44143             this.markValid();
44144             return true;
44145         }
44146         
44147         this.markInvalid();
44148         return false;
44149     },
44150     
44151     getName: function()
44152     {
44153         return this.name;
44154     },
44155     
44156     beforeBlur : function()
44157     {
44158         if(!this.castInt){
44159             return;
44160         }
44161         
44162         var v = this.parseValue(this.getRawValue());
44163         
44164         if(v || v == 0){
44165             this.setValue(v);
44166         }
44167     },
44168     
44169     onBlur : function()
44170     {
44171         this.beforeBlur();
44172         
44173         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44174             //this.el.removeClass(this.focusClass);
44175         }
44176         
44177         this.hasFocus = false;
44178         
44179         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44180             this.validate();
44181         }
44182         
44183         var v = this.getValue();
44184         
44185         if(String(v) !== String(this.startValue)){
44186             this.fireEvent('change', this, v, this.startValue);
44187         }
44188         
44189         this.fireEvent("blur", this);
44190     },
44191     
44192     inputEl : function()
44193     {
44194         return this.el.select('.roo-money-amount-input', true).first();
44195     },
44196     
44197     currencyEl : function()
44198     {
44199         return this.el.select('.roo-money-currency-input', true).first();
44200     },
44201     
44202     hiddenEl : function()
44203     {
44204         return this.el.select('input.hidden-number-input',true).first();
44205     }
44206     
44207 });/**
44208  * @class Roo.bootstrap.BezierSignature
44209  * @extends Roo.bootstrap.Component
44210  * Bootstrap BezierSignature class
44211  * This script refer to:
44212  *    Title: Signature Pad
44213  *    Author: szimek
44214  *    Availability: https://github.com/szimek/signature_pad
44215  *
44216  * @constructor
44217  * Create a new BezierSignature
44218  * @param {Object} config The config object
44219  */
44220
44221 Roo.bootstrap.BezierSignature = function(config){
44222     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44223     this.addEvents({
44224         "resize" : true
44225     });
44226 };
44227
44228 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44229 {
44230      
44231     curve_data: [],
44232     
44233     is_empty: true,
44234     
44235     mouse_btn_down: true,
44236     
44237     /**
44238      * @cfg {int} canvas height
44239      */
44240     canvas_height: '200px',
44241     
44242     /**
44243      * @cfg {float|function} Radius of a single dot.
44244      */ 
44245     dot_size: false,
44246     
44247     /**
44248      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44249      */
44250     min_width: 0.5,
44251     
44252     /**
44253      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44254      */
44255     max_width: 2.5,
44256     
44257     /**
44258      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44259      */
44260     throttle: 16,
44261     
44262     /**
44263      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44264      */
44265     min_distance: 5,
44266     
44267     /**
44268      * @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.
44269      */
44270     bg_color: 'rgba(0, 0, 0, 0)',
44271     
44272     /**
44273      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44274      */
44275     dot_color: 'black',
44276     
44277     /**
44278      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44279      */ 
44280     velocity_filter_weight: 0.7,
44281     
44282     /**
44283      * @cfg {function} Callback when stroke begin. 
44284      */
44285     onBegin: false,
44286     
44287     /**
44288      * @cfg {function} Callback when stroke end.
44289      */
44290     onEnd: false,
44291     
44292     getAutoCreate : function()
44293     {
44294         var cls = 'roo-signature column';
44295         
44296         if(this.cls){
44297             cls += ' ' + this.cls;
44298         }
44299         
44300         var col_sizes = [
44301             'lg',
44302             'md',
44303             'sm',
44304             'xs'
44305         ];
44306         
44307         for(var i = 0; i < col_sizes.length; i++) {
44308             if(this[col_sizes[i]]) {
44309                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44310             }
44311         }
44312         
44313         var cfg = {
44314             tag: 'div',
44315             cls: cls,
44316             cn: [
44317                 {
44318                     tag: 'div',
44319                     cls: 'roo-signature-body',
44320                     cn: [
44321                         {
44322                             tag: 'canvas',
44323                             cls: 'roo-signature-body-canvas',
44324                             height: this.canvas_height,
44325                             width: this.canvas_width
44326                         }
44327                     ]
44328                 },
44329                 {
44330                     tag: 'input',
44331                     type: 'file',
44332                     style: 'display: none'
44333                 }
44334             ]
44335         };
44336         
44337         return cfg;
44338     },
44339     
44340     initEvents: function() 
44341     {
44342         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44343         
44344         var canvas = this.canvasEl();
44345         
44346         // mouse && touch event swapping...
44347         canvas.dom.style.touchAction = 'none';
44348         canvas.dom.style.msTouchAction = 'none';
44349         
44350         this.mouse_btn_down = false;
44351         canvas.on('mousedown', this._handleMouseDown, this);
44352         canvas.on('mousemove', this._handleMouseMove, this);
44353         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44354         
44355         if (window.PointerEvent) {
44356             canvas.on('pointerdown', this._handleMouseDown, this);
44357             canvas.on('pointermove', this._handleMouseMove, this);
44358             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44359         }
44360         
44361         if ('ontouchstart' in window) {
44362             canvas.on('touchstart', this._handleTouchStart, this);
44363             canvas.on('touchmove', this._handleTouchMove, this);
44364             canvas.on('touchend', this._handleTouchEnd, this);
44365         }
44366         
44367         Roo.EventManager.onWindowResize(this.resize, this, true);
44368         
44369         // file input event
44370         this.fileEl().on('change', this.uploadImage, this);
44371         
44372         this.clear();
44373         
44374         this.resize();
44375     },
44376     
44377     resize: function(){
44378         
44379         var canvas = this.canvasEl().dom;
44380         var ctx = this.canvasElCtx();
44381         var img_data = false;
44382         
44383         if(canvas.width > 0) {
44384             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44385         }
44386         // setting canvas width will clean img data
44387         canvas.width = 0;
44388         
44389         var style = window.getComputedStyle ? 
44390             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44391             
44392         var padding_left = parseInt(style.paddingLeft) || 0;
44393         var padding_right = parseInt(style.paddingRight) || 0;
44394         
44395         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44396         
44397         if(img_data) {
44398             ctx.putImageData(img_data, 0, 0);
44399         }
44400     },
44401     
44402     _handleMouseDown: function(e)
44403     {
44404         if (e.browserEvent.which === 1) {
44405             this.mouse_btn_down = true;
44406             this.strokeBegin(e);
44407         }
44408     },
44409     
44410     _handleMouseMove: function (e)
44411     {
44412         if (this.mouse_btn_down) {
44413             this.strokeMoveUpdate(e);
44414         }
44415     },
44416     
44417     _handleMouseUp: function (e)
44418     {
44419         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44420             this.mouse_btn_down = false;
44421             this.strokeEnd(e);
44422         }
44423     },
44424     
44425     _handleTouchStart: function (e) {
44426         
44427         e.preventDefault();
44428         if (e.browserEvent.targetTouches.length === 1) {
44429             // var touch = e.browserEvent.changedTouches[0];
44430             // this.strokeBegin(touch);
44431             
44432              this.strokeBegin(e); // assume e catching the correct xy...
44433         }
44434     },
44435     
44436     _handleTouchMove: function (e) {
44437         e.preventDefault();
44438         // var touch = event.targetTouches[0];
44439         // _this._strokeMoveUpdate(touch);
44440         this.strokeMoveUpdate(e);
44441     },
44442     
44443     _handleTouchEnd: function (e) {
44444         var wasCanvasTouched = e.target === this.canvasEl().dom;
44445         if (wasCanvasTouched) {
44446             e.preventDefault();
44447             // var touch = event.changedTouches[0];
44448             // _this._strokeEnd(touch);
44449             this.strokeEnd(e);
44450         }
44451     },
44452     
44453     reset: function () {
44454         this._lastPoints = [];
44455         this._lastVelocity = 0;
44456         this._lastWidth = (this.min_width + this.max_width) / 2;
44457         this.canvasElCtx().fillStyle = this.dot_color;
44458     },
44459     
44460     strokeMoveUpdate: function(e)
44461     {
44462         this.strokeUpdate(e);
44463         
44464         if (this.throttle) {
44465             this.throttleStroke(this.strokeUpdate, this.throttle);
44466         }
44467         else {
44468             this.strokeUpdate(e);
44469         }
44470     },
44471     
44472     strokeBegin: function(e)
44473     {
44474         var newPointGroup = {
44475             color: this.dot_color,
44476             points: []
44477         };
44478         
44479         if (typeof this.onBegin === 'function') {
44480             this.onBegin(e);
44481         }
44482         
44483         this.curve_data.push(newPointGroup);
44484         this.reset();
44485         this.strokeUpdate(e);
44486     },
44487     
44488     strokeUpdate: function(e)
44489     {
44490         var rect = this.canvasEl().dom.getBoundingClientRect();
44491         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44492         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44493         var lastPoints = lastPointGroup.points;
44494         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44495         var isLastPointTooClose = lastPoint
44496             ? point.distanceTo(lastPoint) <= this.min_distance
44497             : false;
44498         var color = lastPointGroup.color;
44499         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44500             var curve = this.addPoint(point);
44501             if (!lastPoint) {
44502                 this.drawDot({color: color, point: point});
44503             }
44504             else if (curve) {
44505                 this.drawCurve({color: color, curve: curve});
44506             }
44507             lastPoints.push({
44508                 time: point.time,
44509                 x: point.x,
44510                 y: point.y
44511             });
44512         }
44513     },
44514     
44515     strokeEnd: function(e)
44516     {
44517         this.strokeUpdate(e);
44518         if (typeof this.onEnd === 'function') {
44519             this.onEnd(e);
44520         }
44521     },
44522     
44523     addPoint:  function (point) {
44524         var _lastPoints = this._lastPoints;
44525         _lastPoints.push(point);
44526         if (_lastPoints.length > 2) {
44527             if (_lastPoints.length === 3) {
44528                 _lastPoints.unshift(_lastPoints[0]);
44529             }
44530             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44531             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44532             _lastPoints.shift();
44533             return curve;
44534         }
44535         return null;
44536     },
44537     
44538     calculateCurveWidths: function (startPoint, endPoint) {
44539         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44540             (1 - this.velocity_filter_weight) * this._lastVelocity;
44541
44542         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44543         var widths = {
44544             end: newWidth,
44545             start: this._lastWidth
44546         };
44547         
44548         this._lastVelocity = velocity;
44549         this._lastWidth = newWidth;
44550         return widths;
44551     },
44552     
44553     drawDot: function (_a) {
44554         var color = _a.color, point = _a.point;
44555         var ctx = this.canvasElCtx();
44556         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44557         ctx.beginPath();
44558         this.drawCurveSegment(point.x, point.y, width);
44559         ctx.closePath();
44560         ctx.fillStyle = color;
44561         ctx.fill();
44562     },
44563     
44564     drawCurve: function (_a) {
44565         var color = _a.color, curve = _a.curve;
44566         var ctx = this.canvasElCtx();
44567         var widthDelta = curve.endWidth - curve.startWidth;
44568         var drawSteps = Math.floor(curve.length()) * 2;
44569         ctx.beginPath();
44570         ctx.fillStyle = color;
44571         for (var i = 0; i < drawSteps; i += 1) {
44572         var t = i / drawSteps;
44573         var tt = t * t;
44574         var ttt = tt * t;
44575         var u = 1 - t;
44576         var uu = u * u;
44577         var uuu = uu * u;
44578         var x = uuu * curve.startPoint.x;
44579         x += 3 * uu * t * curve.control1.x;
44580         x += 3 * u * tt * curve.control2.x;
44581         x += ttt * curve.endPoint.x;
44582         var y = uuu * curve.startPoint.y;
44583         y += 3 * uu * t * curve.control1.y;
44584         y += 3 * u * tt * curve.control2.y;
44585         y += ttt * curve.endPoint.y;
44586         var width = curve.startWidth + ttt * widthDelta;
44587         this.drawCurveSegment(x, y, width);
44588         }
44589         ctx.closePath();
44590         ctx.fill();
44591     },
44592     
44593     drawCurveSegment: function (x, y, width) {
44594         var ctx = this.canvasElCtx();
44595         ctx.moveTo(x, y);
44596         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44597         this.is_empty = false;
44598     },
44599     
44600     clear: function()
44601     {
44602         var ctx = this.canvasElCtx();
44603         var canvas = this.canvasEl().dom;
44604         ctx.fillStyle = this.bg_color;
44605         ctx.clearRect(0, 0, canvas.width, canvas.height);
44606         ctx.fillRect(0, 0, canvas.width, canvas.height);
44607         this.curve_data = [];
44608         this.reset();
44609         this.is_empty = true;
44610     },
44611     
44612     fileEl: function()
44613     {
44614         return  this.el.select('input',true).first();
44615     },
44616     
44617     canvasEl: function()
44618     {
44619         return this.el.select('canvas',true).first();
44620     },
44621     
44622     canvasElCtx: function()
44623     {
44624         return this.el.select('canvas',true).first().dom.getContext('2d');
44625     },
44626     
44627     getImage: function(type)
44628     {
44629         if(this.is_empty) {
44630             return false;
44631         }
44632         
44633         // encryption ?
44634         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44635     },
44636     
44637     drawFromImage: function(img_src)
44638     {
44639         var img = new Image();
44640         
44641         img.onload = function(){
44642             this.canvasElCtx().drawImage(img, 0, 0);
44643         }.bind(this);
44644         
44645         img.src = img_src;
44646         
44647         this.is_empty = false;
44648     },
44649     
44650     selectImage: function()
44651     {
44652         this.fileEl().dom.click();
44653     },
44654     
44655     uploadImage: function(e)
44656     {
44657         var reader = new FileReader();
44658         
44659         reader.onload = function(e){
44660             var img = new Image();
44661             img.onload = function(){
44662                 this.reset();
44663                 this.canvasElCtx().drawImage(img, 0, 0);
44664             }.bind(this);
44665             img.src = e.target.result;
44666         }.bind(this);
44667         
44668         reader.readAsDataURL(e.target.files[0]);
44669     },
44670     
44671     // Bezier Point Constructor
44672     Point: (function () {
44673         function Point(x, y, time) {
44674             this.x = x;
44675             this.y = y;
44676             this.time = time || Date.now();
44677         }
44678         Point.prototype.distanceTo = function (start) {
44679             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44680         };
44681         Point.prototype.equals = function (other) {
44682             return this.x === other.x && this.y === other.y && this.time === other.time;
44683         };
44684         Point.prototype.velocityFrom = function (start) {
44685             return this.time !== start.time
44686             ? this.distanceTo(start) / (this.time - start.time)
44687             : 0;
44688         };
44689         return Point;
44690     }()),
44691     
44692     
44693     // Bezier Constructor
44694     Bezier: (function () {
44695         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44696             this.startPoint = startPoint;
44697             this.control2 = control2;
44698             this.control1 = control1;
44699             this.endPoint = endPoint;
44700             this.startWidth = startWidth;
44701             this.endWidth = endWidth;
44702         }
44703         Bezier.fromPoints = function (points, widths, scope) {
44704             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44705             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44706             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44707         };
44708         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44709             var dx1 = s1.x - s2.x;
44710             var dy1 = s1.y - s2.y;
44711             var dx2 = s2.x - s3.x;
44712             var dy2 = s2.y - s3.y;
44713             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44714             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44715             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44716             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44717             var dxm = m1.x - m2.x;
44718             var dym = m1.y - m2.y;
44719             var k = l2 / (l1 + l2);
44720             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44721             var tx = s2.x - cm.x;
44722             var ty = s2.y - cm.y;
44723             return {
44724                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44725                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44726             };
44727         };
44728         Bezier.prototype.length = function () {
44729             var steps = 10;
44730             var length = 0;
44731             var px;
44732             var py;
44733             for (var i = 0; i <= steps; i += 1) {
44734                 var t = i / steps;
44735                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44736                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44737                 if (i > 0) {
44738                     var xdiff = cx - px;
44739                     var ydiff = cy - py;
44740                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44741                 }
44742                 px = cx;
44743                 py = cy;
44744             }
44745             return length;
44746         };
44747         Bezier.prototype.point = function (t, start, c1, c2, end) {
44748             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44749             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44750             + (3.0 * c2 * (1.0 - t) * t * t)
44751             + (end * t * t * t);
44752         };
44753         return Bezier;
44754     }()),
44755     
44756     throttleStroke: function(fn, wait) {
44757       if (wait === void 0) { wait = 250; }
44758       var previous = 0;
44759       var timeout = null;
44760       var result;
44761       var storedContext;
44762       var storedArgs;
44763       var later = function () {
44764           previous = Date.now();
44765           timeout = null;
44766           result = fn.apply(storedContext, storedArgs);
44767           if (!timeout) {
44768               storedContext = null;
44769               storedArgs = [];
44770           }
44771       };
44772       return function wrapper() {
44773           var args = [];
44774           for (var _i = 0; _i < arguments.length; _i++) {
44775               args[_i] = arguments[_i];
44776           }
44777           var now = Date.now();
44778           var remaining = wait - (now - previous);
44779           storedContext = this;
44780           storedArgs = args;
44781           if (remaining <= 0 || remaining > wait) {
44782               if (timeout) {
44783                   clearTimeout(timeout);
44784                   timeout = null;
44785               }
44786               previous = now;
44787               result = fn.apply(storedContext, storedArgs);
44788               if (!timeout) {
44789                   storedContext = null;
44790                   storedArgs = [];
44791               }
44792           }
44793           else if (!timeout) {
44794               timeout = window.setTimeout(later, remaining);
44795           }
44796           return result;
44797       };
44798   }
44799   
44800 });
44801
44802  
44803
44804