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  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2871  *
2872  * @constructor
2873  * Create a new CardUploader
2874  * @param {Object} config The config object
2875  */
2876
2877 Roo.bootstrap.ButtonUploader = function(config){
2878     
2879  
2880     
2881     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2882     
2883      
2884      this.addEvents({
2885          // raw events
2886         /**
2887          * @event beforeselect
2888          * When button is pressed, before show upload files dialog is shown
2889          * @param {Roo.bootstrap.UploaderButton} this
2890          *
2891          */
2892         'beforeselect' : true,
2893          /**
2894          * @event fired when files have been selected, 
2895          * When a the download link is clicked
2896          * @param {Roo.bootstrap.UploaderButton} this
2897          * @param {Array} Array of files that have been uploaded
2898          */
2899         'uploaded' : true
2900         
2901     });
2902 };
2903  
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2905     
2906      
2907     errorTimeout : 3000,
2908      
2909     images : false,
2910    
2911     fileCollection : false,
2912     allowBlank : true,
2913     
2914     multiple : true,
2915     
2916     getAutoCreate : function()
2917     {
2918         var im = {
2919             tag: 'input',
2920             type : 'file',
2921             cls : 'd-none  roo-card-upload-selector' 
2922           
2923         };
2924         if (this.multiple) {
2925             im.multiple = 'multiple';
2926         }
2927         
2928         return  {
2929             cls :'div' ,
2930             cn : [
2931                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2932                 im
2933
2934             ]
2935         };
2936            
2937          
2938     },
2939      
2940    
2941     initEvents : function()
2942     {
2943         
2944         Roo.bootstrap.Button.prototype.initEvents.call(this);
2945         
2946         
2947         
2948         
2949         
2950         this.urlAPI = (window.createObjectURL && window) || 
2951                                 (window.URL && URL.revokeObjectURL && URL) || 
2952                                 (window.webkitURL && webkitURL);
2953                         
2954          
2955          
2956          
2957         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2958         
2959         this.selectorEl.on('change', this.onFileSelected, this);
2960          
2961          
2962        
2963     },
2964     
2965    
2966     onClick : function(e)
2967     {
2968         e.preventDefault();
2969         
2970         if ( this.fireEvent('beforeselect', this) === false) {
2971             return;
2972         }
2973          
2974         this.selectorEl.dom.click();
2975          
2976     },
2977     
2978     onFileSelected : function(e)
2979     {
2980         e.preventDefault();
2981         
2982         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2983             return;
2984         }
2985         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986         this.selectorEl.dom.reset();
2987         
2988         this.fireEvent('uploaded', this,  files );
2989         
2990     },
2991     
2992        
2993    
2994     
2995     /**
2996      * addCard - add an Attachment to the uploader
2997      * @param data - the data about the image to upload
2998      *
2999      * {
3000           id : 123
3001           title : "Title of file",
3002           is_uploaded : false,
3003           src : "http://.....",
3004           srcfile : { the File upload object },
3005           mimetype : file.type,
3006           preview : false,
3007           is_deleted : 0
3008           .. any other data...
3009         }
3010      *
3011      * 
3012     */
3013      
3014     reset: function()
3015     {
3016          
3017          this.selectorEl
3018     } 
3019     
3020     
3021     
3022     
3023 });
3024  /*
3025  * - LGPL
3026  *
3027  * image
3028  * 
3029  */
3030
3031
3032 /**
3033  * @class Roo.bootstrap.Img
3034  * @extends Roo.bootstrap.Component
3035  * Bootstrap Img class
3036  * @cfg {Boolean} imgResponsive false | true
3037  * @cfg {String} border rounded | circle | thumbnail
3038  * @cfg {String} src image source
3039  * @cfg {String} alt image alternative text
3040  * @cfg {String} href a tag href
3041  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042  * @cfg {String} xsUrl xs image source
3043  * @cfg {String} smUrl sm image source
3044  * @cfg {String} mdUrl md image source
3045  * @cfg {String} lgUrl lg image source
3046  * 
3047  * @constructor
3048  * Create a new Input
3049  * @param {Object} config The config object
3050  */
3051
3052 Roo.bootstrap.Img = function(config){
3053     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3054     
3055     this.addEvents({
3056         // img events
3057         /**
3058          * @event click
3059          * The img click event for the img.
3060          * @param {Roo.EventObject} e
3061          */
3062         "click" : true
3063     });
3064 };
3065
3066 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3067     
3068     imgResponsive: true,
3069     border: '',
3070     src: 'about:blank',
3071     href: false,
3072     target: false,
3073     xsUrl: '',
3074     smUrl: '',
3075     mdUrl: '',
3076     lgUrl: '',
3077
3078     getAutoCreate : function()
3079     {   
3080         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3081             return this.createSingleImg();
3082         }
3083         
3084         var cfg = {
3085             tag: 'div',
3086             cls: 'roo-image-responsive-group',
3087             cn: []
3088         };
3089         var _this = this;
3090         
3091         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3092             
3093             if(!_this[size + 'Url']){
3094                 return;
3095             }
3096             
3097             var img = {
3098                 tag: 'img',
3099                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3100                 html: _this.html || cfg.html,
3101                 src: _this[size + 'Url']
3102             };
3103             
3104             img.cls += ' roo-image-responsive-' + size;
3105             
3106             var s = ['xs', 'sm', 'md', 'lg'];
3107             
3108             s.splice(s.indexOf(size), 1);
3109             
3110             Roo.each(s, function(ss){
3111                 img.cls += ' hidden-' + ss;
3112             });
3113             
3114             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3115                 cfg.cls += ' img-' + _this.border;
3116             }
3117             
3118             if(_this.alt){
3119                 cfg.alt = _this.alt;
3120             }
3121             
3122             if(_this.href){
3123                 var a = {
3124                     tag: 'a',
3125                     href: _this.href,
3126                     cn: [
3127                         img
3128                     ]
3129                 };
3130
3131                 if(this.target){
3132                     a.target = _this.target;
3133                 }
3134             }
3135             
3136             cfg.cn.push((_this.href) ? a : img);
3137             
3138         });
3139         
3140         return cfg;
3141     },
3142     
3143     createSingleImg : function()
3144     {
3145         var cfg = {
3146             tag: 'img',
3147             cls: (this.imgResponsive) ? 'img-responsive' : '',
3148             html : null,
3149             src : 'about:blank'  // just incase src get's set to undefined?!?
3150         };
3151         
3152         cfg.html = this.html || cfg.html;
3153         
3154         cfg.src = this.src || cfg.src;
3155         
3156         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3157             cfg.cls += ' img-' + this.border;
3158         }
3159         
3160         if(this.alt){
3161             cfg.alt = this.alt;
3162         }
3163         
3164         if(this.href){
3165             var a = {
3166                 tag: 'a',
3167                 href: this.href,
3168                 cn: [
3169                     cfg
3170                 ]
3171             };
3172             
3173             if(this.target){
3174                 a.target = this.target;
3175             }
3176             
3177         }
3178         
3179         return (this.href) ? a : cfg;
3180     },
3181     
3182     initEvents: function() 
3183     {
3184         if(!this.href){
3185             this.el.on('click', this.onClick, this);
3186         }
3187         
3188     },
3189     
3190     onClick : function(e)
3191     {
3192         Roo.log('img onclick');
3193         this.fireEvent('click', this, e);
3194     },
3195     /**
3196      * Sets the url of the image - used to update it
3197      * @param {String} url the url of the image
3198      */
3199     
3200     setSrc : function(url)
3201     {
3202         this.src =  url;
3203         
3204         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205             this.el.dom.src =  url;
3206             return;
3207         }
3208         
3209         this.el.select('img', true).first().dom.src =  url;
3210     }
3211     
3212     
3213    
3214 });
3215
3216  /*
3217  * - LGPL
3218  *
3219  * image
3220  * 
3221  */
3222
3223
3224 /**
3225  * @class Roo.bootstrap.Link
3226  * @extends Roo.bootstrap.Component
3227  * Bootstrap Link Class
3228  * @cfg {String} alt image alternative text
3229  * @cfg {String} href a tag href
3230  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3231  * @cfg {String} html the content of the link.
3232  * @cfg {String} anchor name for the anchor link
3233  * @cfg {String} fa - favicon
3234
3235  * @cfg {Boolean} preventDefault (true | false) default false
3236
3237  * 
3238  * @constructor
3239  * Create a new Input
3240  * @param {Object} config The config object
3241  */
3242
3243 Roo.bootstrap.Link = function(config){
3244     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3245     
3246     this.addEvents({
3247         // img events
3248         /**
3249          * @event click
3250          * The img click event for the img.
3251          * @param {Roo.EventObject} e
3252          */
3253         "click" : true
3254     });
3255 };
3256
3257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3258     
3259     href: false,
3260     target: false,
3261     preventDefault: false,
3262     anchor : false,
3263     alt : false,
3264     fa: false,
3265
3266
3267     getAutoCreate : function()
3268     {
3269         var html = this.html || '';
3270         
3271         if (this.fa !== false) {
3272             html = '<i class="fa fa-' + this.fa + '"></i>';
3273         }
3274         var cfg = {
3275             tag: 'a'
3276         };
3277         // anchor's do not require html/href...
3278         if (this.anchor === false) {
3279             cfg.html = html;
3280             cfg.href = this.href || '#';
3281         } else {
3282             cfg.name = this.anchor;
3283             if (this.html !== false || this.fa !== false) {
3284                 cfg.html = html;
3285             }
3286             if (this.href !== false) {
3287                 cfg.href = this.href;
3288             }
3289         }
3290         
3291         if(this.alt !== false){
3292             cfg.alt = this.alt;
3293         }
3294         
3295         
3296         if(this.target !== false) {
3297             cfg.target = this.target;
3298         }
3299         
3300         return cfg;
3301     },
3302     
3303     initEvents: function() {
3304         
3305         if(!this.href || this.preventDefault){
3306             this.el.on('click', this.onClick, this);
3307         }
3308     },
3309     
3310     onClick : function(e)
3311     {
3312         if(this.preventDefault){
3313             e.preventDefault();
3314         }
3315         //Roo.log('img onclick');
3316         this.fireEvent('click', this, e);
3317     }
3318    
3319 });
3320
3321  /*
3322  * - LGPL
3323  *
3324  * header
3325  * 
3326  */
3327
3328 /**
3329  * @class Roo.bootstrap.Header
3330  * @extends Roo.bootstrap.Component
3331  * Bootstrap Header class
3332  * @cfg {String} html content of header
3333  * @cfg {Number} level (1|2|3|4|5|6) default 1
3334  * 
3335  * @constructor
3336  * Create a new Header
3337  * @param {Object} config The config object
3338  */
3339
3340
3341 Roo.bootstrap.Header  = function(config){
3342     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3343 };
3344
3345 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3346     
3347     //href : false,
3348     html : false,
3349     level : 1,
3350     
3351     
3352     
3353     getAutoCreate : function(){
3354         
3355         
3356         
3357         var cfg = {
3358             tag: 'h' + (1 *this.level),
3359             html: this.html || ''
3360         } ;
3361         
3362         return cfg;
3363     }
3364    
3365 });
3366
3367  
3368
3369  /*
3370  * Based on:
3371  * Ext JS Library 1.1.1
3372  * Copyright(c) 2006-2007, Ext JS, LLC.
3373  *
3374  * Originally Released Under LGPL - original licence link has changed is not relivant.
3375  *
3376  * Fork - LGPL
3377  * <script type="text/javascript">
3378  */
3379  
3380 /**
3381  * @class Roo.bootstrap.MenuMgr
3382  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3383  * @singleton
3384  */
3385 Roo.bootstrap.MenuMgr = function(){
3386    var menus, active, groups = {}, attached = false, lastShow = new Date();
3387
3388    // private - called when first menu is created
3389    function init(){
3390        menus = {};
3391        active = new Roo.util.MixedCollection();
3392        Roo.get(document).addKeyListener(27, function(){
3393            if(active.length > 0){
3394                hideAll();
3395            }
3396        });
3397    }
3398
3399    // private
3400    function hideAll(){
3401        if(active && active.length > 0){
3402            var c = active.clone();
3403            c.each(function(m){
3404                m.hide();
3405            });
3406        }
3407    }
3408
3409    // private
3410    function onHide(m){
3411        active.remove(m);
3412        if(active.length < 1){
3413            Roo.get(document).un("mouseup", onMouseDown);
3414             
3415            attached = false;
3416        }
3417    }
3418
3419    // private
3420    function onShow(m){
3421        var last = active.last();
3422        lastShow = new Date();
3423        active.add(m);
3424        if(!attached){
3425           Roo.get(document).on("mouseup", onMouseDown);
3426            
3427            attached = true;
3428        }
3429        if(m.parentMenu){
3430           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3431           m.parentMenu.activeChild = m;
3432        }else if(last && last.isVisible()){
3433           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3434        }
3435    }
3436
3437    // private
3438    function onBeforeHide(m){
3439        if(m.activeChild){
3440            m.activeChild.hide();
3441        }
3442        if(m.autoHideTimer){
3443            clearTimeout(m.autoHideTimer);
3444            delete m.autoHideTimer;
3445        }
3446    }
3447
3448    // private
3449    function onBeforeShow(m){
3450        var pm = m.parentMenu;
3451        if(!pm && !m.allowOtherMenus){
3452            hideAll();
3453        }else if(pm && pm.activeChild && active != m){
3454            pm.activeChild.hide();
3455        }
3456    }
3457
3458    // private this should really trigger on mouseup..
3459    function onMouseDown(e){
3460         Roo.log("on Mouse Up");
3461         
3462         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3463             Roo.log("MenuManager hideAll");
3464             hideAll();
3465             e.stopEvent();
3466         }
3467         
3468         
3469    }
3470
3471    // private
3472    function onBeforeCheck(mi, state){
3473        if(state){
3474            var g = groups[mi.group];
3475            for(var i = 0, l = g.length; i < l; i++){
3476                if(g[i] != mi){
3477                    g[i].setChecked(false);
3478                }
3479            }
3480        }
3481    }
3482
3483    return {
3484
3485        /**
3486         * Hides all menus that are currently visible
3487         */
3488        hideAll : function(){
3489             hideAll();  
3490        },
3491
3492        // private
3493        register : function(menu){
3494            if(!menus){
3495                init();
3496            }
3497            menus[menu.id] = menu;
3498            menu.on("beforehide", onBeforeHide);
3499            menu.on("hide", onHide);
3500            menu.on("beforeshow", onBeforeShow);
3501            menu.on("show", onShow);
3502            var g = menu.group;
3503            if(g && menu.events["checkchange"]){
3504                if(!groups[g]){
3505                    groups[g] = [];
3506                }
3507                groups[g].push(menu);
3508                menu.on("checkchange", onCheck);
3509            }
3510        },
3511
3512         /**
3513          * Returns a {@link Roo.menu.Menu} object
3514          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3515          * be used to generate and return a new Menu instance.
3516          */
3517        get : function(menu){
3518            if(typeof menu == "string"){ // menu id
3519                return menus[menu];
3520            }else if(menu.events){  // menu instance
3521                return menu;
3522            }
3523            /*else if(typeof menu.length == 'number'){ // array of menu items?
3524                return new Roo.bootstrap.Menu({items:menu});
3525            }else{ // otherwise, must be a config
3526                return new Roo.bootstrap.Menu(menu);
3527            }
3528            */
3529            return false;
3530        },
3531
3532        // private
3533        unregister : function(menu){
3534            delete menus[menu.id];
3535            menu.un("beforehide", onBeforeHide);
3536            menu.un("hide", onHide);
3537            menu.un("beforeshow", onBeforeShow);
3538            menu.un("show", onShow);
3539            var g = menu.group;
3540            if(g && menu.events["checkchange"]){
3541                groups[g].remove(menu);
3542                menu.un("checkchange", onCheck);
3543            }
3544        },
3545
3546        // private
3547        registerCheckable : function(menuItem){
3548            var g = menuItem.group;
3549            if(g){
3550                if(!groups[g]){
3551                    groups[g] = [];
3552                }
3553                groups[g].push(menuItem);
3554                menuItem.on("beforecheckchange", onBeforeCheck);
3555            }
3556        },
3557
3558        // private
3559        unregisterCheckable : function(menuItem){
3560            var g = menuItem.group;
3561            if(g){
3562                groups[g].remove(menuItem);
3563                menuItem.un("beforecheckchange", onBeforeCheck);
3564            }
3565        }
3566    };
3567 }();/*
3568  * - LGPL
3569  *
3570  * menu
3571  * 
3572  */
3573
3574 /**
3575  * @class Roo.bootstrap.Menu
3576  * @extends Roo.bootstrap.Component
3577  * Bootstrap Menu class - container for MenuItems
3578  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3579  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3580  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3581  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3582  * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3583  * 
3584  * @constructor
3585  * Create a new Menu
3586  * @param {Object} config The config object
3587  */
3588
3589
3590 Roo.bootstrap.Menu = function(config){
3591     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3592     if (this.registerMenu && this.type != 'treeview')  {
3593         Roo.bootstrap.MenuMgr.register(this);
3594     }
3595     
3596     
3597     this.addEvents({
3598         /**
3599          * @event beforeshow
3600          * Fires before this menu is displayed (return false to block)
3601          * @param {Roo.menu.Menu} this
3602          */
3603         beforeshow : true,
3604         /**
3605          * @event beforehide
3606          * Fires before this menu is hidden (return false to block)
3607          * @param {Roo.menu.Menu} this
3608          */
3609         beforehide : true,
3610         /**
3611          * @event show
3612          * Fires after this menu is displayed
3613          * @param {Roo.menu.Menu} this
3614          */
3615         show : true,
3616         /**
3617          * @event hide
3618          * Fires after this menu is hidden
3619          * @param {Roo.menu.Menu} this
3620          */
3621         hide : true,
3622         /**
3623          * @event click
3624          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3625          * @param {Roo.menu.Menu} this
3626          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3627          * @param {Roo.EventObject} e
3628          */
3629         click : true,
3630         /**
3631          * @event mouseover
3632          * Fires when the mouse is hovering over this menu
3633          * @param {Roo.menu.Menu} this
3634          * @param {Roo.EventObject} e
3635          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3636          */
3637         mouseover : true,
3638         /**
3639          * @event mouseout
3640          * Fires when the mouse exits this menu
3641          * @param {Roo.menu.Menu} this
3642          * @param {Roo.EventObject} e
3643          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3644          */
3645         mouseout : true,
3646         /**
3647          * @event itemclick
3648          * Fires when a menu item contained in this menu is clicked
3649          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3650          * @param {Roo.EventObject} e
3651          */
3652         itemclick: true
3653     });
3654     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3655 };
3656
3657 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3658     
3659    /// html : false,
3660     //align : '',
3661     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3662     type: false,
3663     /**
3664      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3665      */
3666     registerMenu : true,
3667     
3668     menuItems :false, // stores the menu items..
3669     
3670     hidden:true,
3671         
3672     parentMenu : false,
3673     
3674     stopEvent : true,
3675     
3676     isLink : false,
3677     
3678     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3679     
3680     hideTrigger : false,
3681     
3682     
3683     getChildContainer : function() {
3684         return this.el;  
3685     },
3686     
3687     getAutoCreate : function(){
3688          
3689         //if (['right'].indexOf(this.align)!==-1) {
3690         //    cfg.cn[1].cls += ' pull-right'
3691         //}
3692         
3693         
3694         var cfg = {
3695             tag : 'ul',
3696             cls : 'dropdown-menu shadow' ,
3697             style : 'z-index:1000'
3698             
3699         };
3700         
3701         if (this.type === 'submenu') {
3702             cfg.cls = 'submenu active';
3703         }
3704         if (this.type === 'treeview') {
3705             cfg.cls = 'treeview-menu';
3706         }
3707         
3708         return cfg;
3709     },
3710     initEvents : function() {
3711         
3712        // Roo.log("ADD event");
3713        // Roo.log(this.triggerEl.dom);
3714         
3715         this.triggerEl.on('click', this.onTriggerClick, this);
3716         
3717         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3718         
3719         if (!this.hideTrigger) {
3720             if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3721                 // dropdown toggle on the 'a' in BS4?
3722                 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3723             } else {
3724                 this.triggerEl.addClass('dropdown-toggle');
3725             }
3726         }
3727         if (Roo.isTouch) {
3728             this.el.on('touchstart'  , this.onTouch, this);
3729         }
3730         this.el.on('click' , this.onClick, this);
3731
3732         this.el.on("mouseover", this.onMouseOver, this);
3733         this.el.on("mouseout", this.onMouseOut, this);
3734         
3735     },
3736     
3737     findTargetItem : function(e)
3738     {
3739         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3740         if(!t){
3741             return false;
3742         }
3743         //Roo.log(t);         Roo.log(t.id);
3744         if(t && t.id){
3745             //Roo.log(this.menuitems);
3746             return this.menuitems.get(t.id);
3747             
3748             //return this.items.get(t.menuItemId);
3749         }
3750         
3751         return false;
3752     },
3753     
3754     onTouch : function(e) 
3755     {
3756         Roo.log("menu.onTouch");
3757         //e.stopEvent(); this make the user popdown broken
3758         this.onClick(e);
3759     },
3760     
3761     onClick : function(e)
3762     {
3763         Roo.log("menu.onClick");
3764         
3765         var t = this.findTargetItem(e);
3766         if(!t || t.isContainer){
3767             return;
3768         }
3769         Roo.log(e);
3770         /*
3771         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3772             if(t == this.activeItem && t.shouldDeactivate(e)){
3773                 this.activeItem.deactivate();
3774                 delete this.activeItem;
3775                 return;
3776             }
3777             if(t.canActivate){
3778                 this.setActiveItem(t, true);
3779             }
3780             return;
3781             
3782             
3783         }
3784         */
3785        
3786         Roo.log('pass click event');
3787         
3788         t.onClick(e);
3789         
3790         this.fireEvent("click", this, t, e);
3791         
3792         var _this = this;
3793         
3794         if(!t.href.length || t.href == '#'){
3795             (function() { _this.hide(); }).defer(100);
3796         }
3797         
3798     },
3799     
3800     onMouseOver : function(e){
3801         var t  = this.findTargetItem(e);
3802         //Roo.log(t);
3803         //if(t){
3804         //    if(t.canActivate && !t.disabled){
3805         //        this.setActiveItem(t, true);
3806         //    }
3807         //}
3808         
3809         this.fireEvent("mouseover", this, e, t);
3810     },
3811     isVisible : function(){
3812         return !this.hidden;
3813     },
3814     onMouseOut : function(e){
3815         var t  = this.findTargetItem(e);
3816         
3817         //if(t ){
3818         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3819         //        this.activeItem.deactivate();
3820         //        delete this.activeItem;
3821         //    }
3822         //}
3823         this.fireEvent("mouseout", this, e, t);
3824     },
3825     
3826     
3827     /**
3828      * Displays this menu relative to another element
3829      * @param {String/HTMLElement/Roo.Element} element The element to align to
3830      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3831      * the element (defaults to this.defaultAlign)
3832      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3833      */
3834     show : function(el, pos, parentMenu)
3835     {
3836         if (false === this.fireEvent("beforeshow", this)) {
3837             Roo.log("show canceled");
3838             return;
3839         }
3840         this.parentMenu = parentMenu;
3841         if(!this.el){
3842             this.render();
3843         }
3844         
3845         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3846     },
3847      /**
3848      * Displays this menu at a specific xy position
3849      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3850      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3851      */
3852     showAt : function(xy, parentMenu, /* private: */_e){
3853         this.parentMenu = parentMenu;
3854         if(!this.el){
3855             this.render();
3856         }
3857         if(_e !== false){
3858             this.fireEvent("beforeshow", this);
3859             //xy = this.el.adjustForConstraints(xy);
3860         }
3861         
3862         //this.el.show();
3863         this.hideMenuItems();
3864         this.hidden = false;
3865         this.triggerEl.addClass('open');
3866         this.el.addClass('show');
3867         
3868         // reassign x when hitting right
3869         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3870             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3871         }
3872         
3873         // reassign y when hitting bottom
3874         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3875             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3876         }
3877         
3878         // but the list may align on trigger left or trigger top... should it be a properity?
3879         
3880         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3881             this.el.setXY(xy);
3882         }
3883         
3884         this.focus();
3885         this.fireEvent("show", this);
3886     },
3887     
3888     focus : function(){
3889         return;
3890         if(!this.hidden){
3891             this.doFocus.defer(50, this);
3892         }
3893     },
3894
3895     doFocus : function(){
3896         if(!this.hidden){
3897             this.focusEl.focus();
3898         }
3899     },
3900
3901     /**
3902      * Hides this menu and optionally all parent menus
3903      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3904      */
3905     hide : function(deep)
3906     {
3907         if (false === this.fireEvent("beforehide", this)) {
3908             Roo.log("hide canceled");
3909             return;
3910         }
3911         this.hideMenuItems();
3912         if(this.el && this.isVisible()){
3913            
3914             if(this.activeItem){
3915                 this.activeItem.deactivate();
3916                 this.activeItem = null;
3917             }
3918             this.triggerEl.removeClass('open');;
3919             this.el.removeClass('show');
3920             this.hidden = true;
3921             this.fireEvent("hide", this);
3922         }
3923         if(deep === true && this.parentMenu){
3924             this.parentMenu.hide(true);
3925         }
3926     },
3927     
3928     onTriggerClick : function(e)
3929     {
3930         Roo.log('trigger click');
3931         
3932         var target = e.getTarget();
3933         
3934         Roo.log(target.nodeName.toLowerCase());
3935         
3936         if(target.nodeName.toLowerCase() === 'i'){
3937             e.preventDefault();
3938         }
3939         
3940     },
3941     
3942     onTriggerPress  : function(e)
3943     {
3944         Roo.log('trigger press');
3945         //Roo.log(e.getTarget());
3946        // Roo.log(this.triggerEl.dom);
3947        
3948         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3949         var pel = Roo.get(e.getTarget());
3950         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3951             Roo.log('is treeview or dropdown?');
3952             return;
3953         }
3954         
3955         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3956             return;
3957         }
3958         
3959         if (this.isVisible()) {
3960             Roo.log('hide');
3961             this.hide();
3962         } else {
3963             Roo.log('show');
3964             this.show(this.triggerEl, '?', false);
3965         }
3966         
3967         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3968             e.stopEvent();
3969         }
3970         
3971     },
3972        
3973     
3974     hideMenuItems : function()
3975     {
3976         Roo.log("hide Menu Items");
3977         if (!this.el) { 
3978             return;
3979         }
3980         
3981         this.el.select('.open',true).each(function(aa) {
3982             
3983             aa.removeClass('open');
3984          
3985         });
3986     },
3987     addxtypeChild : function (tree, cntr) {
3988         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3989           
3990         this.menuitems.add(comp);
3991         return comp;
3992
3993     },
3994     getEl : function()
3995     {
3996         Roo.log(this.el);
3997         return this.el;
3998     },
3999     
4000     clear : function()
4001     {
4002         this.getEl().dom.innerHTML = '';
4003         this.menuitems.clear();
4004     }
4005 });
4006
4007  
4008  /*
4009  * - LGPL
4010  *
4011  * menu item
4012  * 
4013  */
4014
4015
4016 /**
4017  * @class Roo.bootstrap.MenuItem
4018  * @extends Roo.bootstrap.Component
4019  * Bootstrap MenuItem class
4020  * @cfg {String} html the menu label
4021  * @cfg {String} href the link
4022  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4023  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4024  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4025  * @cfg {String} fa favicon to show on left of menu item.
4026  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4027  * 
4028  * 
4029  * @constructor
4030  * Create a new MenuItem
4031  * @param {Object} config The config object
4032  */
4033
4034
4035 Roo.bootstrap.MenuItem = function(config){
4036     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4037     this.addEvents({
4038         // raw events
4039         /**
4040          * @event click
4041          * The raw click event for the entire grid.
4042          * @param {Roo.bootstrap.MenuItem} this
4043          * @param {Roo.EventObject} e
4044          */
4045         "click" : true
4046     });
4047 };
4048
4049 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4050     
4051     href : false,
4052     html : false,
4053     preventDefault: false,
4054     isContainer : false,
4055     active : false,
4056     fa: false,
4057     
4058     getAutoCreate : function(){
4059         
4060         if(this.isContainer){
4061             return {
4062                 tag: 'li',
4063                 cls: 'dropdown-menu-item '
4064             };
4065         }
4066         var ctag = {
4067             tag: 'span',
4068             html: 'Link'
4069         };
4070         
4071         var anc = {
4072             tag : 'a',
4073             cls : 'dropdown-item',
4074             href : '#',
4075             cn : [  ]
4076         };
4077         
4078         if (this.fa !== false) {
4079             anc.cn.push({
4080                 tag : 'i',
4081                 cls : 'fa fa-' + this.fa
4082             });
4083         }
4084         
4085         anc.cn.push(ctag);
4086         
4087         
4088         var cfg= {
4089             tag: 'li',
4090             cls: 'dropdown-menu-item',
4091             cn: [ anc ]
4092         };
4093         if (this.parent().type == 'treeview') {
4094             cfg.cls = 'treeview-menu';
4095         }
4096         if (this.active) {
4097             cfg.cls += ' active';
4098         }
4099         
4100         
4101         
4102         anc.href = this.href || cfg.cn[0].href ;
4103         ctag.html = this.html || cfg.cn[0].html ;
4104         return cfg;
4105     },
4106     
4107     initEvents: function()
4108     {
4109         if (this.parent().type == 'treeview') {
4110             this.el.select('a').on('click', this.onClick, this);
4111         }
4112         
4113         if (this.menu) {
4114             this.menu.parentType = this.xtype;
4115             this.menu.triggerEl = this.el;
4116             this.menu = this.addxtype(Roo.apply({}, this.menu));
4117         }
4118         
4119     },
4120     onClick : function(e)
4121     {
4122         Roo.log('item on click ');
4123         
4124         if(this.preventDefault){
4125             e.preventDefault();
4126         }
4127         //this.parent().hideMenuItems();
4128         
4129         this.fireEvent('click', this, e);
4130     },
4131     getEl : function()
4132     {
4133         return this.el;
4134     } 
4135 });
4136
4137  
4138
4139  /*
4140  * - LGPL
4141  *
4142  * menu separator
4143  * 
4144  */
4145
4146
4147 /**
4148  * @class Roo.bootstrap.MenuSeparator
4149  * @extends Roo.bootstrap.Component
4150  * Bootstrap MenuSeparator class
4151  * 
4152  * @constructor
4153  * Create a new MenuItem
4154  * @param {Object} config The config object
4155  */
4156
4157
4158 Roo.bootstrap.MenuSeparator = function(config){
4159     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4160 };
4161
4162 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4163     
4164     getAutoCreate : function(){
4165         var cfg = {
4166             cls: 'divider',
4167             tag : 'li'
4168         };
4169         
4170         return cfg;
4171     }
4172    
4173 });
4174
4175  
4176
4177  
4178 /*
4179 * Licence: LGPL
4180 */
4181
4182 /**
4183  * @class Roo.bootstrap.Modal
4184  * @extends Roo.bootstrap.Component
4185  * Bootstrap Modal class
4186  * @cfg {String} title Title of dialog
4187  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4188  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4189  * @cfg {Boolean} specificTitle default false
4190  * @cfg {Array} buttons Array of buttons or standard button set..
4191  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4192  * @cfg {Boolean} animate default true
4193  * @cfg {Boolean} allow_close default true
4194  * @cfg {Boolean} fitwindow default false
4195  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4196  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4197  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4198  * @cfg {String} size (sm|lg|xl) default empty
4199  * @cfg {Number} max_width set the max width of modal
4200  * @cfg {Boolean} editableTitle can the title be edited
4201
4202  *
4203  *
4204  * @constructor
4205  * Create a new Modal Dialog
4206  * @param {Object} config The config object
4207  */
4208
4209 Roo.bootstrap.Modal = function(config){
4210     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4211     this.addEvents({
4212         // raw events
4213         /**
4214          * @event btnclick
4215          * The raw btnclick event for the button
4216          * @param {Roo.EventObject} e
4217          */
4218         "btnclick" : true,
4219         /**
4220          * @event resize
4221          * Fire when dialog resize
4222          * @param {Roo.bootstrap.Modal} this
4223          * @param {Roo.EventObject} e
4224          */
4225         "resize" : true,
4226         /**
4227          * @event titlechanged
4228          * Fire when the editable title has been changed
4229          * @param {Roo.bootstrap.Modal} this
4230          * @param {Roo.EventObject} value
4231          */
4232         "titlechanged" : true 
4233         
4234     });
4235     this.buttons = this.buttons || [];
4236
4237     if (this.tmpl) {
4238         this.tmpl = Roo.factory(this.tmpl);
4239     }
4240
4241 };
4242
4243 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4244
4245     title : 'test dialog',
4246
4247     buttons : false,
4248
4249     // set on load...
4250
4251     html: false,
4252
4253     tmp: false,
4254
4255     specificTitle: false,
4256
4257     buttonPosition: 'right',
4258
4259     allow_close : true,
4260
4261     animate : true,
4262
4263     fitwindow: false,
4264     
4265      // private
4266     dialogEl: false,
4267     bodyEl:  false,
4268     footerEl:  false,
4269     titleEl:  false,
4270     closeEl:  false,
4271
4272     size: '',
4273     
4274     max_width: 0,
4275     
4276     max_height: 0,
4277     
4278     fit_content: false,
4279     editableTitle  : false,
4280
4281     onRender : function(ct, position)
4282     {
4283         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4284
4285         if(!this.el){
4286             var cfg = Roo.apply({},  this.getAutoCreate());
4287             cfg.id = Roo.id();
4288             //if(!cfg.name){
4289             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4290             //}
4291             //if (!cfg.name.length) {
4292             //    delete cfg.name;
4293            // }
4294             if (this.cls) {
4295                 cfg.cls += ' ' + this.cls;
4296             }
4297             if (this.style) {
4298                 cfg.style = this.style;
4299             }
4300             this.el = Roo.get(document.body).createChild(cfg, position);
4301         }
4302         //var type = this.el.dom.type;
4303
4304
4305         if(this.tabIndex !== undefined){
4306             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4307         }
4308
4309         this.dialogEl = this.el.select('.modal-dialog',true).first();
4310         this.bodyEl = this.el.select('.modal-body',true).first();
4311         this.closeEl = this.el.select('.modal-header .close', true).first();
4312         this.headerEl = this.el.select('.modal-header',true).first();
4313         this.titleEl = this.el.select('.modal-title',true).first();
4314         this.footerEl = this.el.select('.modal-footer',true).first();
4315
4316         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4317         
4318         //this.el.addClass("x-dlg-modal");
4319
4320         if (this.buttons.length) {
4321             Roo.each(this.buttons, function(bb) {
4322                 var b = Roo.apply({}, bb);
4323                 b.xns = b.xns || Roo.bootstrap;
4324                 b.xtype = b.xtype || 'Button';
4325                 if (typeof(b.listeners) == 'undefined') {
4326                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4327                 }
4328
4329                 var btn = Roo.factory(b);
4330
4331                 btn.render(this.getButtonContainer());
4332
4333             },this);
4334         }
4335         // render the children.
4336         var nitems = [];
4337
4338         if(typeof(this.items) != 'undefined'){
4339             var items = this.items;
4340             delete this.items;
4341
4342             for(var i =0;i < items.length;i++) {
4343                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4344             }
4345         }
4346
4347         this.items = nitems;
4348
4349         // where are these used - they used to be body/close/footer
4350
4351
4352         this.initEvents();
4353         //this.el.addClass([this.fieldClass, this.cls]);
4354
4355     },
4356
4357     getAutoCreate : function()
4358     {
4359         // we will default to modal-body-overflow - might need to remove or make optional later.
4360         var bdy = {
4361                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4362                 html : this.html || ''
4363         };
4364
4365         var title = {
4366             tag: 'h5',
4367             cls : 'modal-title',
4368             html : this.title
4369         };
4370
4371         if(this.specificTitle){ // WTF is this?
4372             title = this.title;
4373         }
4374
4375         var header = [];
4376         if (this.allow_close && Roo.bootstrap.version == 3) {
4377             header.push({
4378                 tag: 'button',
4379                 cls : 'close',
4380                 html : '&times'
4381             });
4382         }
4383
4384         header.push(title);
4385
4386         if (this.editableTitle) {
4387             header.push({
4388                 cls: 'form-control roo-editable-title d-none',
4389                 tag: 'input',
4390                 type: 'text'
4391             });
4392         }
4393         
4394         if (this.allow_close && Roo.bootstrap.version == 4) {
4395             header.push({
4396                 tag: 'button',
4397                 cls : 'close',
4398                 html : '&times'
4399             });
4400         }
4401         
4402         var size = '';
4403
4404         if(this.size.length){
4405             size = 'modal-' + this.size;
4406         }
4407         
4408         var footer = Roo.bootstrap.version == 3 ?
4409             {
4410                 cls : 'modal-footer',
4411                 cn : [
4412                     {
4413                         tag: 'div',
4414                         cls: 'btn-' + this.buttonPosition
4415                     }
4416                 ]
4417
4418             } :
4419             {  // BS4 uses mr-auto on left buttons....
4420                 cls : 'modal-footer'
4421             };
4422
4423             
4424
4425         
4426         
4427         var modal = {
4428             cls: "modal",
4429              cn : [
4430                 {
4431                     cls: "modal-dialog " + size,
4432                     cn : [
4433                         {
4434                             cls : "modal-content",
4435                             cn : [
4436                                 {
4437                                     cls : 'modal-header',
4438                                     cn : header
4439                                 },
4440                                 bdy,
4441                                 footer
4442                             ]
4443
4444                         }
4445                     ]
4446
4447                 }
4448             ]
4449         };
4450
4451         if(this.animate){
4452             modal.cls += ' fade';
4453         }
4454
4455         return modal;
4456
4457     },
4458     getChildContainer : function() {
4459
4460          return this.bodyEl;
4461
4462     },
4463     getButtonContainer : function() {
4464         
4465          return Roo.bootstrap.version == 4 ?
4466             this.el.select('.modal-footer',true).first()
4467             : this.el.select('.modal-footer div',true).first();
4468
4469     },
4470     initEvents : function()
4471     {
4472         if (this.allow_close) {
4473             this.closeEl.on('click', this.hide, this);
4474         }
4475         Roo.EventManager.onWindowResize(this.resize, this, true);
4476         if (this.editableTitle) {
4477             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4478             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4479             this.headerEditEl.on('keyup', function(e) {
4480                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4481                         this.toggleHeaderInput(false)
4482                     }
4483                 }, this);
4484             this.headerEditEl.on('blur', function(e) {
4485                 this.toggleHeaderInput(false)
4486             },this);
4487         }
4488
4489     },
4490   
4491
4492     resize : function()
4493     {
4494         this.maskEl.setSize(
4495             Roo.lib.Dom.getViewWidth(true),
4496             Roo.lib.Dom.getViewHeight(true)
4497         );
4498         
4499         if (this.fitwindow) {
4500             
4501            this.dialogEl.setStyle( { 'max-width' : '100%' });
4502             this.setSize(
4503                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4504                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4505             );
4506             return;
4507         }
4508         
4509         if(this.max_width !== 0) {
4510             
4511             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4512             
4513             if(this.height) {
4514                 this.setSize(w, this.height);
4515                 return;
4516             }
4517             
4518             if(this.max_height) {
4519                 this.setSize(w,Math.min(
4520                     this.max_height,
4521                     Roo.lib.Dom.getViewportHeight(true) - 60
4522                 ));
4523                 
4524                 return;
4525             }
4526             
4527             if(!this.fit_content) {
4528                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4529                 return;
4530             }
4531             
4532             this.setSize(w, Math.min(
4533                 60 +
4534                 this.headerEl.getHeight() + 
4535                 this.footerEl.getHeight() + 
4536                 this.getChildHeight(this.bodyEl.dom.childNodes),
4537                 Roo.lib.Dom.getViewportHeight(true) - 60)
4538             );
4539         }
4540         
4541     },
4542
4543     setSize : function(w,h)
4544     {
4545         if (!w && !h) {
4546             return;
4547         }
4548         
4549         this.resizeTo(w,h);
4550     },
4551
4552     show : function() {
4553
4554         if (!this.rendered) {
4555             this.render();
4556         }
4557         this.toggleHeaderInput(false);
4558         //this.el.setStyle('display', 'block');
4559         this.el.removeClass('hideing');
4560         this.el.dom.style.display='block';
4561         
4562         Roo.get(document.body).addClass('modal-open');
4563  
4564         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4565             
4566             (function(){
4567                 this.el.addClass('show');
4568                 this.el.addClass('in');
4569             }).defer(50, this);
4570         }else{
4571             this.el.addClass('show');
4572             this.el.addClass('in');
4573         }
4574
4575         // not sure how we can show data in here..
4576         //if (this.tmpl) {
4577         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4578         //}
4579
4580         Roo.get(document.body).addClass("x-body-masked");
4581         
4582         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4583         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4584         this.maskEl.dom.style.display = 'block';
4585         this.maskEl.addClass('show');
4586         
4587         
4588         this.resize();
4589         
4590         this.fireEvent('show', this);
4591
4592         // set zindex here - otherwise it appears to be ignored...
4593         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4594
4595         (function () {
4596             this.items.forEach( function(e) {
4597                 e.layout ? e.layout() : false;
4598
4599             });
4600         }).defer(100,this);
4601
4602     },
4603     hide : function()
4604     {
4605         if(this.fireEvent("beforehide", this) !== false){
4606             
4607             this.maskEl.removeClass('show');
4608             
4609             this.maskEl.dom.style.display = '';
4610             Roo.get(document.body).removeClass("x-body-masked");
4611             this.el.removeClass('in');
4612             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4613
4614             if(this.animate){ // why
4615                 this.el.addClass('hideing');
4616                 this.el.removeClass('show');
4617                 (function(){
4618                     if (!this.el.hasClass('hideing')) {
4619                         return; // it's been shown again...
4620                     }
4621                     
4622                     this.el.dom.style.display='';
4623
4624                     Roo.get(document.body).removeClass('modal-open');
4625                     this.el.removeClass('hideing');
4626                 }).defer(150,this);
4627                 
4628             }else{
4629                 this.el.removeClass('show');
4630                 this.el.dom.style.display='';
4631                 Roo.get(document.body).removeClass('modal-open');
4632
4633             }
4634             this.fireEvent('hide', this);
4635         }
4636     },
4637     isVisible : function()
4638     {
4639         
4640         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4641         
4642     },
4643
4644     addButton : function(str, cb)
4645     {
4646
4647
4648         var b = Roo.apply({}, { html : str } );
4649         b.xns = b.xns || Roo.bootstrap;
4650         b.xtype = b.xtype || 'Button';
4651         if (typeof(b.listeners) == 'undefined') {
4652             b.listeners = { click : cb.createDelegate(this)  };
4653         }
4654
4655         var btn = Roo.factory(b);
4656
4657         btn.render(this.getButtonContainer());
4658
4659         return btn;
4660
4661     },
4662
4663     setDefaultButton : function(btn)
4664     {
4665         //this.el.select('.modal-footer').()
4666     },
4667
4668     resizeTo: function(w,h)
4669     {
4670         this.dialogEl.setWidth(w);
4671         
4672         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4673
4674         this.bodyEl.setHeight(h - diff);
4675         
4676         this.fireEvent('resize', this);
4677     },
4678     
4679     setContentSize  : function(w, h)
4680     {
4681
4682     },
4683     onButtonClick: function(btn,e)
4684     {
4685         //Roo.log([a,b,c]);
4686         this.fireEvent('btnclick', btn.name, e);
4687     },
4688      /**
4689      * Set the title of the Dialog
4690      * @param {String} str new Title
4691      */
4692     setTitle: function(str) {
4693         this.titleEl.dom.innerHTML = str;
4694         this.title = str;
4695     },
4696     /**
4697      * Set the body of the Dialog
4698      * @param {String} str new Title
4699      */
4700     setBody: function(str) {
4701         this.bodyEl.dom.innerHTML = str;
4702     },
4703     /**
4704      * Set the body of the Dialog using the template
4705      * @param {Obj} data - apply this data to the template and replace the body contents.
4706      */
4707     applyBody: function(obj)
4708     {
4709         if (!this.tmpl) {
4710             Roo.log("Error - using apply Body without a template");
4711             //code
4712         }
4713         this.tmpl.overwrite(this.bodyEl, obj);
4714     },
4715     
4716     getChildHeight : function(child_nodes)
4717     {
4718         if(
4719             !child_nodes ||
4720             child_nodes.length == 0
4721         ) {
4722             return 0;
4723         }
4724         
4725         var child_height = 0;
4726         
4727         for(var i = 0; i < child_nodes.length; i++) {
4728             
4729             /*
4730             * for modal with tabs...
4731             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4732                 
4733                 var layout_childs = child_nodes[i].childNodes;
4734                 
4735                 for(var j = 0; j < layout_childs.length; j++) {
4736                     
4737                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4738                         
4739                         var layout_body_childs = layout_childs[j].childNodes;
4740                         
4741                         for(var k = 0; k < layout_body_childs.length; k++) {
4742                             
4743                             if(layout_body_childs[k].classList.contains('navbar')) {
4744                                 child_height += layout_body_childs[k].offsetHeight;
4745                                 continue;
4746                             }
4747                             
4748                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4749                                 
4750                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4751                                 
4752                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4753                                     
4754                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4755                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4756                                         continue;
4757                                     }
4758                                     
4759                                 }
4760                                 
4761                             }
4762                             
4763                         }
4764                     }
4765                 }
4766                 continue;
4767             }
4768             */
4769             
4770             child_height += child_nodes[i].offsetHeight;
4771             // Roo.log(child_nodes[i].offsetHeight);
4772         }
4773         
4774         return child_height;
4775     },
4776     toggleHeaderInput : function(is_edit)
4777     {
4778         if (!this.editableTitle) {
4779             return; // not editable.
4780         }
4781         if (is_edit && this.is_header_editing) {
4782             return; // already editing..
4783         }
4784         if (is_edit) {
4785     
4786             this.headerEditEl.dom.value = this.title;
4787             this.headerEditEl.removeClass('d-none');
4788             this.headerEditEl.dom.focus();
4789             this.titleEl.addClass('d-none');
4790             
4791             this.is_header_editing = true;
4792             return
4793         }
4794         // flip back to not editing.
4795         this.title = this.headerEditEl.dom.value;
4796         this.headerEditEl.addClass('d-none');
4797         this.titleEl.removeClass('d-none');
4798         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4799         this.is_header_editing = false;
4800         this.fireEvent('titlechanged', this, this.title);
4801     
4802             
4803         
4804     }
4805
4806 });
4807
4808
4809 Roo.apply(Roo.bootstrap.Modal,  {
4810     /**
4811          * Button config that displays a single OK button
4812          * @type Object
4813          */
4814         OK :  [{
4815             name : 'ok',
4816             weight : 'primary',
4817             html : 'OK'
4818         }],
4819         /**
4820          * Button config that displays Yes and No buttons
4821          * @type Object
4822          */
4823         YESNO : [
4824             {
4825                 name  : 'no',
4826                 html : 'No'
4827             },
4828             {
4829                 name  :'yes',
4830                 weight : 'primary',
4831                 html : 'Yes'
4832             }
4833         ],
4834
4835         /**
4836          * Button config that displays OK and Cancel buttons
4837          * @type Object
4838          */
4839         OKCANCEL : [
4840             {
4841                name : 'cancel',
4842                 html : 'Cancel'
4843             },
4844             {
4845                 name : 'ok',
4846                 weight : 'primary',
4847                 html : 'OK'
4848             }
4849         ],
4850         /**
4851          * Button config that displays Yes, No and Cancel buttons
4852          * @type Object
4853          */
4854         YESNOCANCEL : [
4855             {
4856                 name : 'yes',
4857                 weight : 'primary',
4858                 html : 'Yes'
4859             },
4860             {
4861                 name : 'no',
4862                 html : 'No'
4863             },
4864             {
4865                 name : 'cancel',
4866                 html : 'Cancel'
4867             }
4868         ],
4869         
4870         zIndex : 10001
4871 });
4872
4873 /*
4874  * - LGPL
4875  *
4876  * messagebox - can be used as a replace
4877  * 
4878  */
4879 /**
4880  * @class Roo.MessageBox
4881  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4882  * Example usage:
4883  *<pre><code>
4884 // Basic alert:
4885 Roo.Msg.alert('Status', 'Changes saved successfully.');
4886
4887 // Prompt for user data:
4888 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4889     if (btn == 'ok'){
4890         // process text value...
4891     }
4892 });
4893
4894 // Show a dialog using config options:
4895 Roo.Msg.show({
4896    title:'Save Changes?',
4897    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4898    buttons: Roo.Msg.YESNOCANCEL,
4899    fn: processResult,
4900    animEl: 'elId'
4901 });
4902 </code></pre>
4903  * @singleton
4904  */
4905 Roo.bootstrap.MessageBox = function(){
4906     var dlg, opt, mask, waitTimer;
4907     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4908     var buttons, activeTextEl, bwidth;
4909
4910     
4911     // private
4912     var handleButton = function(button){
4913         dlg.hide();
4914         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4915     };
4916
4917     // private
4918     var handleHide = function(){
4919         if(opt && opt.cls){
4920             dlg.el.removeClass(opt.cls);
4921         }
4922         //if(waitTimer){
4923         //    Roo.TaskMgr.stop(waitTimer);
4924         //    waitTimer = null;
4925         //}
4926     };
4927
4928     // private
4929     var updateButtons = function(b){
4930         var width = 0;
4931         if(!b){
4932             buttons["ok"].hide();
4933             buttons["cancel"].hide();
4934             buttons["yes"].hide();
4935             buttons["no"].hide();
4936             dlg.footerEl.hide();
4937             
4938             return width;
4939         }
4940         dlg.footerEl.show();
4941         for(var k in buttons){
4942             if(typeof buttons[k] != "function"){
4943                 if(b[k]){
4944                     buttons[k].show();
4945                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4946                     width += buttons[k].el.getWidth()+15;
4947                 }else{
4948                     buttons[k].hide();
4949                 }
4950             }
4951         }
4952         return width;
4953     };
4954
4955     // private
4956     var handleEsc = function(d, k, e){
4957         if(opt && opt.closable !== false){
4958             dlg.hide();
4959         }
4960         if(e){
4961             e.stopEvent();
4962         }
4963     };
4964
4965     return {
4966         /**
4967          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4968          * @return {Roo.BasicDialog} The BasicDialog element
4969          */
4970         getDialog : function(){
4971            if(!dlg){
4972                 dlg = new Roo.bootstrap.Modal( {
4973                     //draggable: true,
4974                     //resizable:false,
4975                     //constraintoviewport:false,
4976                     //fixedcenter:true,
4977                     //collapsible : false,
4978                     //shim:true,
4979                     //modal: true,
4980                 //    width: 'auto',
4981                   //  height:100,
4982                     //buttonAlign:"center",
4983                     closeClick : function(){
4984                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4985                             handleButton("no");
4986                         }else{
4987                             handleButton("cancel");
4988                         }
4989                     }
4990                 });
4991                 dlg.render();
4992                 dlg.on("hide", handleHide);
4993                 mask = dlg.mask;
4994                 //dlg.addKeyListener(27, handleEsc);
4995                 buttons = {};
4996                 this.buttons = buttons;
4997                 var bt = this.buttonText;
4998                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4999                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5000                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5001                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5002                 //Roo.log(buttons);
5003                 bodyEl = dlg.bodyEl.createChild({
5004
5005                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5006                         '<textarea class="roo-mb-textarea"></textarea>' +
5007                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5008                 });
5009                 msgEl = bodyEl.dom.firstChild;
5010                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5011                 textboxEl.enableDisplayMode();
5012                 textboxEl.addKeyListener([10,13], function(){
5013                     if(dlg.isVisible() && opt && opt.buttons){
5014                         if(opt.buttons.ok){
5015                             handleButton("ok");
5016                         }else if(opt.buttons.yes){
5017                             handleButton("yes");
5018                         }
5019                     }
5020                 });
5021                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5022                 textareaEl.enableDisplayMode();
5023                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5024                 progressEl.enableDisplayMode();
5025                 
5026                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5027                 var pf = progressEl.dom.firstChild;
5028                 if (pf) {
5029                     pp = Roo.get(pf.firstChild);
5030                     pp.setHeight(pf.offsetHeight);
5031                 }
5032                 
5033             }
5034             return dlg;
5035         },
5036
5037         /**
5038          * Updates the message box body text
5039          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5040          * the XHTML-compliant non-breaking space character '&amp;#160;')
5041          * @return {Roo.MessageBox} This message box
5042          */
5043         updateText : function(text)
5044         {
5045             if(!dlg.isVisible() && !opt.width){
5046                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5047                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5048             }
5049             msgEl.innerHTML = text || '&#160;';
5050       
5051             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5052             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5053             var w = Math.max(
5054                     Math.min(opt.width || cw , this.maxWidth), 
5055                     Math.max(opt.minWidth || this.minWidth, bwidth)
5056             );
5057             if(opt.prompt){
5058                 activeTextEl.setWidth(w);
5059             }
5060             if(dlg.isVisible()){
5061                 dlg.fixedcenter = false;
5062             }
5063             // to big, make it scroll. = But as usual stupid IE does not support
5064             // !important..
5065             
5066             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5067                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5068                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5069             } else {
5070                 bodyEl.dom.style.height = '';
5071                 bodyEl.dom.style.overflowY = '';
5072             }
5073             if (cw > w) {
5074                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5075             } else {
5076                 bodyEl.dom.style.overflowX = '';
5077             }
5078             
5079             dlg.setContentSize(w, bodyEl.getHeight());
5080             if(dlg.isVisible()){
5081                 dlg.fixedcenter = true;
5082             }
5083             return this;
5084         },
5085
5086         /**
5087          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5088          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5089          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5090          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5091          * @return {Roo.MessageBox} This message box
5092          */
5093         updateProgress : function(value, text){
5094             if(text){
5095                 this.updateText(text);
5096             }
5097             
5098             if (pp) { // weird bug on my firefox - for some reason this is not defined
5099                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5100                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5101             }
5102             return this;
5103         },        
5104
5105         /**
5106          * Returns true if the message box is currently displayed
5107          * @return {Boolean} True if the message box is visible, else false
5108          */
5109         isVisible : function(){
5110             return dlg && dlg.isVisible();  
5111         },
5112
5113         /**
5114          * Hides the message box if it is displayed
5115          */
5116         hide : function(){
5117             if(this.isVisible()){
5118                 dlg.hide();
5119             }  
5120         },
5121
5122         /**
5123          * Displays a new message box, or reinitializes an existing message box, based on the config options
5124          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5125          * The following config object properties are supported:
5126          * <pre>
5127 Property    Type             Description
5128 ----------  ---------------  ------------------------------------------------------------------------------------
5129 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5130                                    closes (defaults to undefined)
5131 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5132                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5133 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5134                                    progress and wait dialogs will ignore this property and always hide the
5135                                    close button as they can only be closed programmatically.
5136 cls               String           A custom CSS class to apply to the message box element
5137 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5138                                    displayed (defaults to 75)
5139 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5140                                    function will be btn (the name of the button that was clicked, if applicable,
5141                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5142                                    Progress and wait dialogs will ignore this option since they do not respond to
5143                                    user actions and can only be closed programmatically, so any required function
5144                                    should be called by the same code after it closes the dialog.
5145 icon              String           A CSS class that provides a background image to be used as an icon for
5146                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5147 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5148 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5149 modal             Boolean          False to allow user interaction with the page while the message box is
5150                                    displayed (defaults to true)
5151 msg               String           A string that will replace the existing message box body text (defaults
5152                                    to the XHTML-compliant non-breaking space character '&#160;')
5153 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5154 progress          Boolean          True to display a progress bar (defaults to false)
5155 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5156 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5157 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5158 title             String           The title text
5159 value             String           The string value to set into the active textbox element if displayed
5160 wait              Boolean          True to display a progress bar (defaults to false)
5161 width             Number           The width of the dialog in pixels
5162 </pre>
5163          *
5164          * Example usage:
5165          * <pre><code>
5166 Roo.Msg.show({
5167    title: 'Address',
5168    msg: 'Please enter your address:',
5169    width: 300,
5170    buttons: Roo.MessageBox.OKCANCEL,
5171    multiline: true,
5172    fn: saveAddress,
5173    animEl: 'addAddressBtn'
5174 });
5175 </code></pre>
5176          * @param {Object} config Configuration options
5177          * @return {Roo.MessageBox} This message box
5178          */
5179         show : function(options)
5180         {
5181             
5182             // this causes nightmares if you show one dialog after another
5183             // especially on callbacks..
5184              
5185             if(this.isVisible()){
5186                 
5187                 this.hide();
5188                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5189                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5190                 Roo.log("New Dialog Message:" +  options.msg )
5191                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5192                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5193                 
5194             }
5195             var d = this.getDialog();
5196             opt = options;
5197             d.setTitle(opt.title || "&#160;");
5198             d.closeEl.setDisplayed(opt.closable !== false);
5199             activeTextEl = textboxEl;
5200             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5201             if(opt.prompt){
5202                 if(opt.multiline){
5203                     textboxEl.hide();
5204                     textareaEl.show();
5205                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5206                         opt.multiline : this.defaultTextHeight);
5207                     activeTextEl = textareaEl;
5208                 }else{
5209                     textboxEl.show();
5210                     textareaEl.hide();
5211                 }
5212             }else{
5213                 textboxEl.hide();
5214                 textareaEl.hide();
5215             }
5216             progressEl.setDisplayed(opt.progress === true);
5217             if (opt.progress) {
5218                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5219             }
5220             this.updateProgress(0);
5221             activeTextEl.dom.value = opt.value || "";
5222             if(opt.prompt){
5223                 dlg.setDefaultButton(activeTextEl);
5224             }else{
5225                 var bs = opt.buttons;
5226                 var db = null;
5227                 if(bs && bs.ok){
5228                     db = buttons["ok"];
5229                 }else if(bs && bs.yes){
5230                     db = buttons["yes"];
5231                 }
5232                 dlg.setDefaultButton(db);
5233             }
5234             bwidth = updateButtons(opt.buttons);
5235             this.updateText(opt.msg);
5236             if(opt.cls){
5237                 d.el.addClass(opt.cls);
5238             }
5239             d.proxyDrag = opt.proxyDrag === true;
5240             d.modal = opt.modal !== false;
5241             d.mask = opt.modal !== false ? mask : false;
5242             if(!d.isVisible()){
5243                 // force it to the end of the z-index stack so it gets a cursor in FF
5244                 document.body.appendChild(dlg.el.dom);
5245                 d.animateTarget = null;
5246                 d.show(options.animEl);
5247             }
5248             return this;
5249         },
5250
5251         /**
5252          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5253          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5254          * and closing the message box when the process is complete.
5255          * @param {String} title The title bar text
5256          * @param {String} msg The message box body text
5257          * @return {Roo.MessageBox} This message box
5258          */
5259         progress : function(title, msg){
5260             this.show({
5261                 title : title,
5262                 msg : msg,
5263                 buttons: false,
5264                 progress:true,
5265                 closable:false,
5266                 minWidth: this.minProgressWidth,
5267                 modal : true
5268             });
5269             return this;
5270         },
5271
5272         /**
5273          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5274          * If a callback function is passed it will be called after the user clicks the button, and the
5275          * id of the button that was clicked will be passed as the only parameter to the callback
5276          * (could also be the top-right close button).
5277          * @param {String} title The title bar text
5278          * @param {String} msg The message box body text
5279          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5280          * @param {Object} scope (optional) The scope of the callback function
5281          * @return {Roo.MessageBox} This message box
5282          */
5283         alert : function(title, msg, fn, scope)
5284         {
5285             this.show({
5286                 title : title,
5287                 msg : msg,
5288                 buttons: this.OK,
5289                 fn: fn,
5290                 closable : false,
5291                 scope : scope,
5292                 modal : true
5293             });
5294             return this;
5295         },
5296
5297         /**
5298          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5299          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5300          * You are responsible for closing the message box when the process is complete.
5301          * @param {String} msg The message box body text
5302          * @param {String} title (optional) The title bar text
5303          * @return {Roo.MessageBox} This message box
5304          */
5305         wait : function(msg, title){
5306             this.show({
5307                 title : title,
5308                 msg : msg,
5309                 buttons: false,
5310                 closable:false,
5311                 progress:true,
5312                 modal:true,
5313                 width:300,
5314                 wait:true
5315             });
5316             waitTimer = Roo.TaskMgr.start({
5317                 run: function(i){
5318                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5319                 },
5320                 interval: 1000
5321             });
5322             return this;
5323         },
5324
5325         /**
5326          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5327          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5328          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5329          * @param {String} title The title bar text
5330          * @param {String} msg The message box body text
5331          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5332          * @param {Object} scope (optional) The scope of the callback function
5333          * @return {Roo.MessageBox} This message box
5334          */
5335         confirm : function(title, msg, fn, scope){
5336             this.show({
5337                 title : title,
5338                 msg : msg,
5339                 buttons: this.YESNO,
5340                 fn: fn,
5341                 scope : scope,
5342                 modal : true
5343             });
5344             return this;
5345         },
5346
5347         /**
5348          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5349          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5350          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5351          * (could also be the top-right close button) and the text that was entered will be passed as the two
5352          * parameters to the callback.
5353          * @param {String} title The title bar text
5354          * @param {String} msg The message box body text
5355          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5356          * @param {Object} scope (optional) The scope of the callback function
5357          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5358          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5359          * @return {Roo.MessageBox} This message box
5360          */
5361         prompt : function(title, msg, fn, scope, multiline){
5362             this.show({
5363                 title : title,
5364                 msg : msg,
5365                 buttons: this.OKCANCEL,
5366                 fn: fn,
5367                 minWidth:250,
5368                 scope : scope,
5369                 prompt:true,
5370                 multiline: multiline,
5371                 modal : true
5372             });
5373             return this;
5374         },
5375
5376         /**
5377          * Button config that displays a single OK button
5378          * @type Object
5379          */
5380         OK : {ok:true},
5381         /**
5382          * Button config that displays Yes and No buttons
5383          * @type Object
5384          */
5385         YESNO : {yes:true, no:true},
5386         /**
5387          * Button config that displays OK and Cancel buttons
5388          * @type Object
5389          */
5390         OKCANCEL : {ok:true, cancel:true},
5391         /**
5392          * Button config that displays Yes, No and Cancel buttons
5393          * @type Object
5394          */
5395         YESNOCANCEL : {yes:true, no:true, cancel:true},
5396
5397         /**
5398          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5399          * @type Number
5400          */
5401         defaultTextHeight : 75,
5402         /**
5403          * The maximum width in pixels of the message box (defaults to 600)
5404          * @type Number
5405          */
5406         maxWidth : 600,
5407         /**
5408          * The minimum width in pixels of the message box (defaults to 100)
5409          * @type Number
5410          */
5411         minWidth : 100,
5412         /**
5413          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5414          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5415          * @type Number
5416          */
5417         minProgressWidth : 250,
5418         /**
5419          * An object containing the default button text strings that can be overriden for localized language support.
5420          * Supported properties are: ok, cancel, yes and no.
5421          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5422          * @type Object
5423          */
5424         buttonText : {
5425             ok : "OK",
5426             cancel : "Cancel",
5427             yes : "Yes",
5428             no : "No"
5429         }
5430     };
5431 }();
5432
5433 /**
5434  * Shorthand for {@link Roo.MessageBox}
5435  */
5436 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5437 Roo.Msg = Roo.Msg || Roo.MessageBox;
5438 /*
5439  * - LGPL
5440  *
5441  * navbar
5442  * 
5443  */
5444
5445 /**
5446  * @class Roo.bootstrap.Navbar
5447  * @extends Roo.bootstrap.Component
5448  * Bootstrap Navbar class
5449
5450  * @constructor
5451  * Create a new Navbar
5452  * @param {Object} config The config object
5453  */
5454
5455
5456 Roo.bootstrap.Navbar = function(config){
5457     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5458     this.addEvents({
5459         // raw events
5460         /**
5461          * @event beforetoggle
5462          * Fire before toggle the menu
5463          * @param {Roo.EventObject} e
5464          */
5465         "beforetoggle" : true
5466     });
5467 };
5468
5469 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5470     
5471     
5472    
5473     // private
5474     navItems : false,
5475     loadMask : false,
5476     
5477     
5478     getAutoCreate : function(){
5479         
5480         
5481         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5482         
5483     },
5484     
5485     initEvents :function ()
5486     {
5487         //Roo.log(this.el.select('.navbar-toggle',true));
5488         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5489         
5490         var mark = {
5491             tag: "div",
5492             cls:"x-dlg-mask"
5493         };
5494         
5495         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5496         
5497         var size = this.el.getSize();
5498         this.maskEl.setSize(size.width, size.height);
5499         this.maskEl.enableDisplayMode("block");
5500         this.maskEl.hide();
5501         
5502         if(this.loadMask){
5503             this.maskEl.show();
5504         }
5505     },
5506     
5507     
5508     getChildContainer : function()
5509     {
5510         if (this.el && this.el.select('.collapse').getCount()) {
5511             return this.el.select('.collapse',true).first();
5512         }
5513         
5514         return this.el;
5515     },
5516     
5517     mask : function()
5518     {
5519         this.maskEl.show();
5520     },
5521     
5522     unmask : function()
5523     {
5524         this.maskEl.hide();
5525     },
5526     onToggle : function()
5527     {
5528         
5529         if(this.fireEvent('beforetoggle', this) === false){
5530             return;
5531         }
5532         var ce = this.el.select('.navbar-collapse',true).first();
5533       
5534         if (!ce.hasClass('show')) {
5535            this.expand();
5536         } else {
5537             this.collapse();
5538         }
5539         
5540         
5541     
5542     },
5543     /**
5544      * Expand the navbar pulldown 
5545      */
5546     expand : function ()
5547     {
5548        
5549         var ce = this.el.select('.navbar-collapse',true).first();
5550         if (ce.hasClass('collapsing')) {
5551             return;
5552         }
5553         ce.dom.style.height = '';
5554                // show it...
5555         ce.addClass('in'); // old...
5556         ce.removeClass('collapse');
5557         ce.addClass('show');
5558         var h = ce.getHeight();
5559         Roo.log(h);
5560         ce.removeClass('show');
5561         // at this point we should be able to see it..
5562         ce.addClass('collapsing');
5563         
5564         ce.setHeight(0); // resize it ...
5565         ce.on('transitionend', function() {
5566             //Roo.log('done transition');
5567             ce.removeClass('collapsing');
5568             ce.addClass('show');
5569             ce.removeClass('collapse');
5570
5571             ce.dom.style.height = '';
5572         }, this, { single: true} );
5573         ce.setHeight(h);
5574         ce.dom.scrollTop = 0;
5575     },
5576     /**
5577      * Collapse the navbar pulldown 
5578      */
5579     collapse : function()
5580     {
5581          var ce = this.el.select('.navbar-collapse',true).first();
5582        
5583         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5584             // it's collapsed or collapsing..
5585             return;
5586         }
5587         ce.removeClass('in'); // old...
5588         ce.setHeight(ce.getHeight());
5589         ce.removeClass('show');
5590         ce.addClass('collapsing');
5591         
5592         ce.on('transitionend', function() {
5593             ce.dom.style.height = '';
5594             ce.removeClass('collapsing');
5595             ce.addClass('collapse');
5596         }, this, { single: true} );
5597         ce.setHeight(0);
5598     }
5599     
5600     
5601     
5602 });
5603
5604
5605
5606  
5607
5608  /*
5609  * - LGPL
5610  *
5611  * navbar
5612  * 
5613  */
5614
5615 /**
5616  * @class Roo.bootstrap.NavSimplebar
5617  * @extends Roo.bootstrap.Navbar
5618  * Bootstrap Sidebar class
5619  *
5620  * @cfg {Boolean} inverse is inverted color
5621  * 
5622  * @cfg {String} type (nav | pills | tabs)
5623  * @cfg {Boolean} arrangement stacked | justified
5624  * @cfg {String} align (left | right) alignment
5625  * 
5626  * @cfg {Boolean} main (true|false) main nav bar? default false
5627  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5628  * 
5629  * @cfg {String} tag (header|footer|nav|div) default is nav 
5630
5631  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5632  * 
5633  * 
5634  * @constructor
5635  * Create a new Sidebar
5636  * @param {Object} config The config object
5637  */
5638
5639
5640 Roo.bootstrap.NavSimplebar = function(config){
5641     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5642 };
5643
5644 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5645     
5646     inverse: false,
5647     
5648     type: false,
5649     arrangement: '',
5650     align : false,
5651     
5652     weight : 'light',
5653     
5654     main : false,
5655     
5656     
5657     tag : false,
5658     
5659     
5660     getAutoCreate : function(){
5661         
5662         
5663         var cfg = {
5664             tag : this.tag || 'div',
5665             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5666         };
5667         if (['light','white'].indexOf(this.weight) > -1) {
5668             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5669         }
5670         cfg.cls += ' bg-' + this.weight;
5671         
5672         if (this.inverse) {
5673             cfg.cls += ' navbar-inverse';
5674             
5675         }
5676         
5677         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5678         
5679         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5680             return cfg;
5681         }
5682         
5683         
5684     
5685         
5686         cfg.cn = [
5687             {
5688                 cls: 'nav nav-' + this.xtype,
5689                 tag : 'ul'
5690             }
5691         ];
5692         
5693          
5694         this.type = this.type || 'nav';
5695         if (['tabs','pills'].indexOf(this.type) != -1) {
5696             cfg.cn[0].cls += ' nav-' + this.type
5697         
5698         
5699         } else {
5700             if (this.type!=='nav') {
5701                 Roo.log('nav type must be nav/tabs/pills')
5702             }
5703             cfg.cn[0].cls += ' navbar-nav'
5704         }
5705         
5706         
5707         
5708         
5709         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5710             cfg.cn[0].cls += ' nav-' + this.arrangement;
5711         }
5712         
5713         
5714         if (this.align === 'right') {
5715             cfg.cn[0].cls += ' navbar-right';
5716         }
5717         
5718         
5719         
5720         
5721         return cfg;
5722     
5723         
5724     }
5725     
5726     
5727     
5728 });
5729
5730
5731
5732  
5733
5734  
5735        /*
5736  * - LGPL
5737  *
5738  * navbar
5739  * navbar-fixed-top
5740  * navbar-expand-md  fixed-top 
5741  */
5742
5743 /**
5744  * @class Roo.bootstrap.NavHeaderbar
5745  * @extends Roo.bootstrap.NavSimplebar
5746  * Bootstrap Sidebar class
5747  *
5748  * @cfg {String} brand what is brand
5749  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5750  * @cfg {String} brand_href href of the brand
5751  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5752  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5753  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5754  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5755  * 
5756  * @constructor
5757  * Create a new Sidebar
5758  * @param {Object} config The config object
5759  */
5760
5761
5762 Roo.bootstrap.NavHeaderbar = function(config){
5763     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5764       
5765 };
5766
5767 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5768     
5769     position: '',
5770     brand: '',
5771     brand_href: false,
5772     srButton : true,
5773     autohide : false,
5774     desktopCenter : false,
5775    
5776     
5777     getAutoCreate : function(){
5778         
5779         var   cfg = {
5780             tag: this.nav || 'nav',
5781             cls: 'navbar navbar-expand-md',
5782             role: 'navigation',
5783             cn: []
5784         };
5785         
5786         var cn = cfg.cn;
5787         if (this.desktopCenter) {
5788             cn.push({cls : 'container', cn : []});
5789             cn = cn[0].cn;
5790         }
5791         
5792         if(this.srButton){
5793             var btn = {
5794                 tag: 'button',
5795                 type: 'button',
5796                 cls: 'navbar-toggle navbar-toggler',
5797                 'data-toggle': 'collapse',
5798                 cn: [
5799                     {
5800                         tag: 'span',
5801                         cls: 'sr-only',
5802                         html: 'Toggle navigation'
5803                     },
5804                     {
5805                         tag: 'span',
5806                         cls: 'icon-bar navbar-toggler-icon'
5807                     },
5808                     {
5809                         tag: 'span',
5810                         cls: 'icon-bar'
5811                     },
5812                     {
5813                         tag: 'span',
5814                         cls: 'icon-bar'
5815                     }
5816                 ]
5817             };
5818             
5819             cn.push( Roo.bootstrap.version == 4 ? btn : {
5820                 tag: 'div',
5821                 cls: 'navbar-header',
5822                 cn: [
5823                     btn
5824                 ]
5825             });
5826         }
5827         
5828         cn.push({
5829             tag: 'div',
5830             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5831             cn : []
5832         });
5833         
5834         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5835         
5836         if (['light','white'].indexOf(this.weight) > -1) {
5837             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5838         }
5839         cfg.cls += ' bg-' + this.weight;
5840         
5841         
5842         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5843             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5844             
5845             // tag can override this..
5846             
5847             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5848         }
5849         
5850         if (this.brand !== '') {
5851             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5852             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5853                 tag: 'a',
5854                 href: this.brand_href ? this.brand_href : '#',
5855                 cls: 'navbar-brand',
5856                 cn: [
5857                 this.brand
5858                 ]
5859             });
5860         }
5861         
5862         if(this.main){
5863             cfg.cls += ' main-nav';
5864         }
5865         
5866         
5867         return cfg;
5868
5869         
5870     },
5871     getHeaderChildContainer : function()
5872     {
5873         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5874             return this.el.select('.navbar-header',true).first();
5875         }
5876         
5877         return this.getChildContainer();
5878     },
5879     
5880     getChildContainer : function()
5881     {
5882          
5883         return this.el.select('.roo-navbar-collapse',true).first();
5884          
5885         
5886     },
5887     
5888     initEvents : function()
5889     {
5890         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5891         
5892         if (this.autohide) {
5893             
5894             var prevScroll = 0;
5895             var ft = this.el;
5896             
5897             Roo.get(document).on('scroll',function(e) {
5898                 var ns = Roo.get(document).getScroll().top;
5899                 var os = prevScroll;
5900                 prevScroll = ns;
5901                 
5902                 if(ns > os){
5903                     ft.removeClass('slideDown');
5904                     ft.addClass('slideUp');
5905                     return;
5906                 }
5907                 ft.removeClass('slideUp');
5908                 ft.addClass('slideDown');
5909                  
5910               
5911           },this);
5912         }
5913     }    
5914     
5915 });
5916
5917
5918
5919  
5920
5921  /*
5922  * - LGPL
5923  *
5924  * navbar
5925  * 
5926  */
5927
5928 /**
5929  * @class Roo.bootstrap.NavSidebar
5930  * @extends Roo.bootstrap.Navbar
5931  * Bootstrap Sidebar class
5932  * 
5933  * @constructor
5934  * Create a new Sidebar
5935  * @param {Object} config The config object
5936  */
5937
5938
5939 Roo.bootstrap.NavSidebar = function(config){
5940     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5941 };
5942
5943 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5944     
5945     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5946     
5947     getAutoCreate : function(){
5948         
5949         
5950         return  {
5951             tag: 'div',
5952             cls: 'sidebar sidebar-nav'
5953         };
5954     
5955         
5956     }
5957     
5958     
5959     
5960 });
5961
5962
5963
5964  
5965
5966  /*
5967  * - LGPL
5968  *
5969  * nav group
5970  * 
5971  */
5972
5973 /**
5974  * @class Roo.bootstrap.NavGroup
5975  * @extends Roo.bootstrap.Component
5976  * Bootstrap NavGroup class
5977  * @cfg {String} align (left|right)
5978  * @cfg {Boolean} inverse
5979  * @cfg {String} type (nav|pills|tab) default nav
5980  * @cfg {String} navId - reference Id for navbar.
5981  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5982  * 
5983  * @constructor
5984  * Create a new nav group
5985  * @param {Object} config The config object
5986  */
5987
5988 Roo.bootstrap.NavGroup = function(config){
5989     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5990     this.navItems = [];
5991    
5992     Roo.bootstrap.NavGroup.register(this);
5993      this.addEvents({
5994         /**
5995              * @event changed
5996              * Fires when the active item changes
5997              * @param {Roo.bootstrap.NavGroup} this
5998              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5999              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6000          */
6001         'changed': true
6002      });
6003     
6004 };
6005
6006 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6007     
6008     align: '',
6009     inverse: false,
6010     form: false,
6011     type: 'nav',
6012     navId : '',
6013     // private
6014     pilltype : true,
6015     
6016     navItems : false, 
6017     
6018     getAutoCreate : function()
6019     {
6020         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6021         
6022         cfg = {
6023             tag : 'ul',
6024             cls: 'nav' 
6025         };
6026         if (Roo.bootstrap.version == 4) {
6027             if (['tabs','pills'].indexOf(this.type) != -1) {
6028                 cfg.cls += ' nav-' + this.type; 
6029             } else {
6030                 // trying to remove so header bar can right align top?
6031                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6032                     // do not use on header bar... 
6033                     cfg.cls += ' navbar-nav';
6034                 }
6035             }
6036             
6037         } else {
6038             if (['tabs','pills'].indexOf(this.type) != -1) {
6039                 cfg.cls += ' nav-' + this.type
6040             } else {
6041                 if (this.type !== 'nav') {
6042                     Roo.log('nav type must be nav/tabs/pills')
6043                 }
6044                 cfg.cls += ' navbar-nav'
6045             }
6046         }
6047         
6048         if (this.parent() && this.parent().sidebar) {
6049             cfg = {
6050                 tag: 'ul',
6051                 cls: 'dashboard-menu sidebar-menu'
6052             };
6053             
6054             return cfg;
6055         }
6056         
6057         if (this.form === true) {
6058             cfg = {
6059                 tag: 'form',
6060                 cls: 'navbar-form form-inline'
6061             };
6062             //nav navbar-right ml-md-auto
6063             if (this.align === 'right') {
6064                 cfg.cls += ' navbar-right ml-md-auto';
6065             } else {
6066                 cfg.cls += ' navbar-left';
6067             }
6068         }
6069         
6070         if (this.align === 'right') {
6071             cfg.cls += ' navbar-right ml-md-auto';
6072         } else {
6073             cfg.cls += ' mr-auto';
6074         }
6075         
6076         if (this.inverse) {
6077             cfg.cls += ' navbar-inverse';
6078             
6079         }
6080         
6081         
6082         return cfg;
6083     },
6084     /**
6085     * sets the active Navigation item
6086     * @param {Roo.bootstrap.NavItem} the new current navitem
6087     */
6088     setActiveItem : function(item)
6089     {
6090         var prev = false;
6091         Roo.each(this.navItems, function(v){
6092             if (v == item) {
6093                 return ;
6094             }
6095             if (v.isActive()) {
6096                 v.setActive(false, true);
6097                 prev = v;
6098                 
6099             }
6100             
6101         });
6102
6103         item.setActive(true, true);
6104         this.fireEvent('changed', this, item, prev);
6105         
6106         
6107     },
6108     /**
6109     * gets the active Navigation item
6110     * @return {Roo.bootstrap.NavItem} the current navitem
6111     */
6112     getActive : function()
6113     {
6114         
6115         var prev = false;
6116         Roo.each(this.navItems, function(v){
6117             
6118             if (v.isActive()) {
6119                 prev = v;
6120                 
6121             }
6122             
6123         });
6124         return prev;
6125     },
6126     
6127     indexOfNav : function()
6128     {
6129         
6130         var prev = false;
6131         Roo.each(this.navItems, function(v,i){
6132             
6133             if (v.isActive()) {
6134                 prev = i;
6135                 
6136             }
6137             
6138         });
6139         return prev;
6140     },
6141     /**
6142     * adds a Navigation item
6143     * @param {Roo.bootstrap.NavItem} the navitem to add
6144     */
6145     addItem : function(cfg)
6146     {
6147         if (this.form && Roo.bootstrap.version == 4) {
6148             cfg.tag = 'div';
6149         }
6150         var cn = new Roo.bootstrap.NavItem(cfg);
6151         this.register(cn);
6152         cn.parentId = this.id;
6153         cn.onRender(this.el, null);
6154         return cn;
6155     },
6156     /**
6157     * register a Navigation item
6158     * @param {Roo.bootstrap.NavItem} the navitem to add
6159     */
6160     register : function(item)
6161     {
6162         this.navItems.push( item);
6163         item.navId = this.navId;
6164     
6165     },
6166     
6167     /**
6168     * clear all the Navigation item
6169     */
6170    
6171     clearAll : function()
6172     {
6173         this.navItems = [];
6174         this.el.dom.innerHTML = '';
6175     },
6176     
6177     getNavItem: function(tabId)
6178     {
6179         var ret = false;
6180         Roo.each(this.navItems, function(e) {
6181             if (e.tabId == tabId) {
6182                ret =  e;
6183                return false;
6184             }
6185             return true;
6186             
6187         });
6188         return ret;
6189     },
6190     
6191     setActiveNext : function()
6192     {
6193         var i = this.indexOfNav(this.getActive());
6194         if (i > this.navItems.length) {
6195             return;
6196         }
6197         this.setActiveItem(this.navItems[i+1]);
6198     },
6199     setActivePrev : function()
6200     {
6201         var i = this.indexOfNav(this.getActive());
6202         if (i  < 1) {
6203             return;
6204         }
6205         this.setActiveItem(this.navItems[i-1]);
6206     },
6207     clearWasActive : function(except) {
6208         Roo.each(this.navItems, function(e) {
6209             if (e.tabId != except.tabId && e.was_active) {
6210                e.was_active = false;
6211                return false;
6212             }
6213             return true;
6214             
6215         });
6216     },
6217     getWasActive : function ()
6218     {
6219         var r = false;
6220         Roo.each(this.navItems, function(e) {
6221             if (e.was_active) {
6222                r = e;
6223                return false;
6224             }
6225             return true;
6226             
6227         });
6228         return r;
6229     }
6230     
6231     
6232 });
6233
6234  
6235 Roo.apply(Roo.bootstrap.NavGroup, {
6236     
6237     groups: {},
6238      /**
6239     * register a Navigation Group
6240     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6241     */
6242     register : function(navgrp)
6243     {
6244         this.groups[navgrp.navId] = navgrp;
6245         
6246     },
6247     /**
6248     * fetch a Navigation Group based on the navigation ID
6249     * @param {string} the navgroup to add
6250     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6251     */
6252     get: function(navId) {
6253         if (typeof(this.groups[navId]) == 'undefined') {
6254             return false;
6255             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6256         }
6257         return this.groups[navId] ;
6258     }
6259     
6260     
6261     
6262 });
6263
6264  /*
6265  * - LGPL
6266  *
6267  * row
6268  * 
6269  */
6270
6271 /**
6272  * @class Roo.bootstrap.NavItem
6273  * @extends Roo.bootstrap.Component
6274  * Bootstrap Navbar.NavItem class
6275  * @cfg {String} href  link to
6276  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6277  * @cfg {Boolean} button_outline show and outlined button
6278  * @cfg {String} html content of button
6279  * @cfg {String} badge text inside badge
6280  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6281  * @cfg {String} glyphicon DEPRICATED - use fa
6282  * @cfg {String} icon DEPRICATED - use fa
6283  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6284  * @cfg {Boolean} active Is item active
6285  * @cfg {Boolean} disabled Is item disabled
6286  * @cfg {String} linkcls  Link Class
6287  * @cfg {Boolean} preventDefault (true | false) default false
6288  * @cfg {String} tabId the tab that this item activates.
6289  * @cfg {String} tagtype (a|span) render as a href or span?
6290  * @cfg {Boolean} animateRef (true|false) link to element default false  
6291   
6292  * @constructor
6293  * Create a new Navbar Item
6294  * @param {Object} config The config object
6295  */
6296 Roo.bootstrap.NavItem = function(config){
6297     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6298     this.addEvents({
6299         // raw events
6300         /**
6301          * @event click
6302          * The raw click event for the entire grid.
6303          * @param {Roo.EventObject} e
6304          */
6305         "click" : true,
6306          /**
6307             * @event changed
6308             * Fires when the active item active state changes
6309             * @param {Roo.bootstrap.NavItem} this
6310             * @param {boolean} state the new state
6311              
6312          */
6313         'changed': true,
6314         /**
6315             * @event scrollto
6316             * Fires when scroll to element
6317             * @param {Roo.bootstrap.NavItem} this
6318             * @param {Object} options
6319             * @param {Roo.EventObject} e
6320              
6321          */
6322         'scrollto': true
6323     });
6324    
6325 };
6326
6327 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6328     
6329     href: false,
6330     html: '',
6331     badge: '',
6332     icon: false,
6333     fa : false,
6334     glyphicon: false,
6335     active: false,
6336     preventDefault : false,
6337     tabId : false,
6338     tagtype : 'a',
6339     tag: 'li',
6340     disabled : false,
6341     animateRef : false,
6342     was_active : false,
6343     button_weight : '',
6344     button_outline : false,
6345     linkcls : '',
6346     navLink: false,
6347     
6348     getAutoCreate : function(){
6349          
6350         var cfg = {
6351             tag: this.tag,
6352             cls: 'nav-item'
6353         };
6354         
6355         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6356         
6357         if (this.active) {
6358             cfg.cls +=  ' active' ;
6359         }
6360         if (this.disabled) {
6361             cfg.cls += ' disabled';
6362         }
6363         
6364         // BS4 only?
6365         if (this.button_weight.length) {
6366             cfg.tag = this.href ? 'a' : 'button';
6367             cfg.html = this.html || '';
6368             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6369             if (this.href) {
6370                 cfg.href = this.href;
6371             }
6372             if (this.fa) {
6373                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6374             } else {
6375                 cfg.cls += " nav-html";
6376             }
6377             
6378             // menu .. should add dropdown-menu class - so no need for carat..
6379             
6380             if (this.badge !== '') {
6381                  
6382                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6383             }
6384             return cfg;
6385         }
6386         
6387         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6388             cfg.cn = [
6389                 {
6390                     tag: this.tagtype,
6391                     href : this.href || "#",
6392                     html: this.html || '',
6393                     cls : ''
6394                 }
6395             ];
6396             if (this.tagtype == 'a') {
6397                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6398         
6399             }
6400             if (this.icon) {
6401                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6402             } else  if (this.fa) {
6403                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6404             } else if(this.glyphicon) {
6405                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6406             } else {
6407                 cfg.cn[0].cls += " nav-html";
6408             }
6409             
6410             if (this.menu) {
6411                 cfg.cn[0].html += " <span class='caret'></span>";
6412              
6413             }
6414             
6415             if (this.badge !== '') {
6416                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6417             }
6418         }
6419         
6420         
6421         
6422         return cfg;
6423     },
6424     onRender : function(ct, position)
6425     {
6426        // Roo.log("Call onRender: " + this.xtype);
6427         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6428             this.tag = 'div';
6429         }
6430         
6431         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6432         this.navLink = this.el.select('.nav-link',true).first();
6433         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6434         return ret;
6435     },
6436       
6437     
6438     initEvents: function() 
6439     {
6440         if (typeof (this.menu) != 'undefined') {
6441             this.menu.parentType = this.xtype;
6442             this.menu.triggerEl = this.el;
6443             this.menu = this.addxtype(Roo.apply({}, this.menu));
6444         }
6445         
6446         this.el.on('click', this.onClick, this);
6447         
6448         //if(this.tagtype == 'span'){
6449         //    this.el.select('span',true).on('click', this.onClick, this);
6450         //}
6451        
6452         // at this point parent should be available..
6453         this.parent().register(this);
6454     },
6455     
6456     onClick : function(e)
6457     {
6458         if (e.getTarget('.dropdown-menu-item')) {
6459             // did you click on a menu itemm.... - then don't trigger onclick..
6460             return;
6461         }
6462         
6463         if(
6464                 this.preventDefault || 
6465                 this.href == '#' 
6466         ){
6467             Roo.log("NavItem - prevent Default?");
6468             e.preventDefault();
6469         }
6470         
6471         if (this.disabled) {
6472             return;
6473         }
6474         
6475         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6476         if (tg && tg.transition) {
6477             Roo.log("waiting for the transitionend");
6478             return;
6479         }
6480         
6481         
6482         
6483         //Roo.log("fire event clicked");
6484         if(this.fireEvent('click', this, e) === false){
6485             return;
6486         };
6487         
6488         if(this.tagtype == 'span'){
6489             return;
6490         }
6491         
6492         //Roo.log(this.href);
6493         var ael = this.el.select('a',true).first();
6494         //Roo.log(ael);
6495         
6496         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6497             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6498             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6499                 return; // ignore... - it's a 'hash' to another page.
6500             }
6501             Roo.log("NavItem - prevent Default?");
6502             e.preventDefault();
6503             this.scrollToElement(e);
6504         }
6505         
6506         
6507         var p =  this.parent();
6508    
6509         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6510             if (typeof(p.setActiveItem) !== 'undefined') {
6511                 p.setActiveItem(this);
6512             }
6513         }
6514         
6515         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6516         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6517             // remove the collapsed menu expand...
6518             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6519         }
6520     },
6521     
6522     isActive: function () {
6523         return this.active
6524     },
6525     setActive : function(state, fire, is_was_active)
6526     {
6527         if (this.active && !state && this.navId) {
6528             this.was_active = true;
6529             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6530             if (nv) {
6531                 nv.clearWasActive(this);
6532             }
6533             
6534         }
6535         this.active = state;
6536         
6537         if (!state ) {
6538             this.el.removeClass('active');
6539             this.navLink ? this.navLink.removeClass('active') : false;
6540         } else if (!this.el.hasClass('active')) {
6541             
6542             this.el.addClass('active');
6543             if (Roo.bootstrap.version == 4 && this.navLink ) {
6544                 this.navLink.addClass('active');
6545             }
6546             
6547         }
6548         if (fire) {
6549             this.fireEvent('changed', this, state);
6550         }
6551         
6552         // show a panel if it's registered and related..
6553         
6554         if (!this.navId || !this.tabId || !state || is_was_active) {
6555             return;
6556         }
6557         
6558         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6559         if (!tg) {
6560             return;
6561         }
6562         var pan = tg.getPanelByName(this.tabId);
6563         if (!pan) {
6564             return;
6565         }
6566         // if we can not flip to new panel - go back to old nav highlight..
6567         if (false == tg.showPanel(pan)) {
6568             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6569             if (nv) {
6570                 var onav = nv.getWasActive();
6571                 if (onav) {
6572                     onav.setActive(true, false, true);
6573                 }
6574             }
6575             
6576         }
6577         
6578         
6579         
6580     },
6581      // this should not be here...
6582     setDisabled : function(state)
6583     {
6584         this.disabled = state;
6585         if (!state ) {
6586             this.el.removeClass('disabled');
6587         } else if (!this.el.hasClass('disabled')) {
6588             this.el.addClass('disabled');
6589         }
6590         
6591     },
6592     
6593     /**
6594      * Fetch the element to display the tooltip on.
6595      * @return {Roo.Element} defaults to this.el
6596      */
6597     tooltipEl : function()
6598     {
6599         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6600     },
6601     
6602     scrollToElement : function(e)
6603     {
6604         var c = document.body;
6605         
6606         /*
6607          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6608          */
6609         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6610             c = document.documentElement;
6611         }
6612         
6613         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6614         
6615         if(!target){
6616             return;
6617         }
6618
6619         var o = target.calcOffsetsTo(c);
6620         
6621         var options = {
6622             target : target,
6623             value : o[1]
6624         };
6625         
6626         this.fireEvent('scrollto', this, options, e);
6627         
6628         Roo.get(c).scrollTo('top', options.value, true);
6629         
6630         return;
6631     },
6632     /**
6633      * Set the HTML (text content) of the item
6634      * @param {string} html  content for the nav item
6635      */
6636     setHtml : function(html)
6637     {
6638         this.html = html;
6639         this.htmlEl.dom.innerHTML = html;
6640         
6641     } 
6642 });
6643  
6644
6645  /*
6646  * - LGPL
6647  *
6648  * sidebar item
6649  *
6650  *  li
6651  *    <span> icon </span>
6652  *    <span> text </span>
6653  *    <span>badge </span>
6654  */
6655
6656 /**
6657  * @class Roo.bootstrap.NavSidebarItem
6658  * @extends Roo.bootstrap.NavItem
6659  * Bootstrap Navbar.NavSidebarItem class
6660  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6661  * {Boolean} open is the menu open
6662  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6663  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6664  * {String} buttonSize (sm|md|lg)the extra classes for the button
6665  * {Boolean} showArrow show arrow next to the text (default true)
6666  * @constructor
6667  * Create a new Navbar Button
6668  * @param {Object} config The config object
6669  */
6670 Roo.bootstrap.NavSidebarItem = function(config){
6671     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6672     this.addEvents({
6673         // raw events
6674         /**
6675          * @event click
6676          * The raw click event for the entire grid.
6677          * @param {Roo.EventObject} e
6678          */
6679         "click" : true,
6680          /**
6681             * @event changed
6682             * Fires when the active item active state changes
6683             * @param {Roo.bootstrap.NavSidebarItem} this
6684             * @param {boolean} state the new state
6685              
6686          */
6687         'changed': true
6688     });
6689    
6690 };
6691
6692 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6693     
6694     badgeWeight : 'default',
6695     
6696     open: false,
6697     
6698     buttonView : false,
6699     
6700     buttonWeight : 'default',
6701     
6702     buttonSize : 'md',
6703     
6704     showArrow : true,
6705     
6706     getAutoCreate : function(){
6707         
6708         
6709         var a = {
6710                 tag: 'a',
6711                 href : this.href || '#',
6712                 cls: '',
6713                 html : '',
6714                 cn : []
6715         };
6716         
6717         if(this.buttonView){
6718             a = {
6719                 tag: 'button',
6720                 href : this.href || '#',
6721                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6722                 html : this.html,
6723                 cn : []
6724             };
6725         }
6726         
6727         var cfg = {
6728             tag: 'li',
6729             cls: '',
6730             cn: [ a ]
6731         };
6732         
6733         if (this.active) {
6734             cfg.cls += ' active';
6735         }
6736         
6737         if (this.disabled) {
6738             cfg.cls += ' disabled';
6739         }
6740         if (this.open) {
6741             cfg.cls += ' open x-open';
6742         }
6743         // left icon..
6744         if (this.glyphicon || this.icon) {
6745             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6746             a.cn.push({ tag : 'i', cls : c }) ;
6747         }
6748         
6749         if(!this.buttonView){
6750             var span = {
6751                 tag: 'span',
6752                 html : this.html || ''
6753             };
6754
6755             a.cn.push(span);
6756             
6757         }
6758         
6759         if (this.badge !== '') {
6760             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6761         }
6762         
6763         if (this.menu) {
6764             
6765             if(this.showArrow){
6766                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6767             }
6768             
6769             a.cls += ' dropdown-toggle treeview' ;
6770         }
6771         
6772         return cfg;
6773     },
6774     
6775     initEvents : function()
6776     { 
6777         if (typeof (this.menu) != 'undefined') {
6778             this.menu.parentType = this.xtype;
6779             this.menu.triggerEl = this.el;
6780             this.menu = this.addxtype(Roo.apply({}, this.menu));
6781         }
6782         
6783         this.el.on('click', this.onClick, this);
6784         
6785         if(this.badge !== ''){
6786             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6787         }
6788         
6789     },
6790     
6791     onClick : function(e)
6792     {
6793         if(this.disabled){
6794             e.preventDefault();
6795             return;
6796         }
6797         
6798         if(this.preventDefault){
6799             e.preventDefault();
6800         }
6801         
6802         this.fireEvent('click', this, e);
6803     },
6804     
6805     disable : function()
6806     {
6807         this.setDisabled(true);
6808     },
6809     
6810     enable : function()
6811     {
6812         this.setDisabled(false);
6813     },
6814     
6815     setDisabled : function(state)
6816     {
6817         if(this.disabled == state){
6818             return;
6819         }
6820         
6821         this.disabled = state;
6822         
6823         if (state) {
6824             this.el.addClass('disabled');
6825             return;
6826         }
6827         
6828         this.el.removeClass('disabled');
6829         
6830         return;
6831     },
6832     
6833     setActive : function(state)
6834     {
6835         if(this.active == state){
6836             return;
6837         }
6838         
6839         this.active = state;
6840         
6841         if (state) {
6842             this.el.addClass('active');
6843             return;
6844         }
6845         
6846         this.el.removeClass('active');
6847         
6848         return;
6849     },
6850     
6851     isActive: function () 
6852     {
6853         return this.active;
6854     },
6855     
6856     setBadge : function(str)
6857     {
6858         if(!this.badgeEl){
6859             return;
6860         }
6861         
6862         this.badgeEl.dom.innerHTML = str;
6863     }
6864     
6865    
6866      
6867  
6868 });
6869  
6870
6871  /*
6872  * - LGPL
6873  *
6874  *  Breadcrumb Nav
6875  * 
6876  */
6877 Roo.namespace('Roo.bootstrap.breadcrumb');
6878
6879
6880 /**
6881  * @class Roo.bootstrap.breadcrumb.Nav
6882  * @extends Roo.bootstrap.Component
6883  * Bootstrap Breadcrumb Nav Class
6884  *  
6885  * @children Roo.bootstrap.breadcrumb.Item
6886  * 
6887  * @constructor
6888  * Create a new breadcrumb.Nav
6889  * @param {Object} config The config object
6890  */
6891
6892
6893 Roo.bootstrap.breadcrumb.Nav = function(config){
6894     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6895     
6896     
6897 };
6898
6899 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6900     
6901     getAutoCreate : function()
6902     {
6903
6904         var cfg = {
6905             tag: 'nav',
6906             cn : [
6907                 {
6908                     tag : 'ol',
6909                     cls : 'breadcrumb'
6910                 }
6911             ]
6912             
6913         };
6914           
6915         return cfg;
6916     },
6917     
6918     initEvents: function()
6919     {
6920         this.olEl = this.el.select('ol',true).first();    
6921     },
6922     getChildContainer : function()
6923     {
6924         return this.olEl;  
6925     }
6926     
6927 });
6928
6929  /*
6930  * - LGPL
6931  *
6932  *  Breadcrumb Item
6933  * 
6934  */
6935
6936
6937 /**
6938  * @class Roo.bootstrap.breadcrumb.Nav
6939  * @extends Roo.bootstrap.Component
6940  * Bootstrap Breadcrumb Nav Class
6941  *  
6942  * @children Roo.bootstrap.breadcrumb.Component
6943  * @cfg {String} html the content of the link.
6944  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6945  * @cfg {Boolean} active is it active
6946
6947  * 
6948  * @constructor
6949  * Create a new breadcrumb.Nav
6950  * @param {Object} config The config object
6951  */
6952
6953 Roo.bootstrap.breadcrumb.Item = function(config){
6954     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6955     this.addEvents({
6956         // img events
6957         /**
6958          * @event click
6959          * The img click event for the img.
6960          * @param {Roo.EventObject} e
6961          */
6962         "click" : true
6963     });
6964     
6965 };
6966
6967 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6968     
6969     href: false,
6970     html : '',
6971     
6972     getAutoCreate : function()
6973     {
6974
6975         var cfg = {
6976             tag: 'li',
6977             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6978         };
6979         if (this.href !== false) {
6980             cfg.cn = [{
6981                 tag : 'a',
6982                 href : this.href,
6983                 html : this.html
6984             }];
6985         } else {
6986             cfg.html = this.html;
6987         }
6988         
6989         return cfg;
6990     },
6991     
6992     initEvents: function()
6993     {
6994         if (this.href) {
6995             this.el.select('a', true).first().on('click',this.onClick, this)
6996         }
6997         
6998     },
6999     onClick : function(e)
7000     {
7001         e.preventDefault();
7002         this.fireEvent('click',this,  e);
7003     }
7004     
7005 });
7006
7007  /*
7008  * - LGPL
7009  *
7010  * row
7011  * 
7012  */
7013
7014 /**
7015  * @class Roo.bootstrap.Row
7016  * @extends Roo.bootstrap.Component
7017  * Bootstrap Row class (contains columns...)
7018  * 
7019  * @constructor
7020  * Create a new Row
7021  * @param {Object} config The config object
7022  */
7023
7024 Roo.bootstrap.Row = function(config){
7025     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7026 };
7027
7028 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7029     
7030     getAutoCreate : function(){
7031        return {
7032             cls: 'row clearfix'
7033        };
7034     }
7035     
7036     
7037 });
7038
7039  
7040
7041  /*
7042  * - LGPL
7043  *
7044  * pagination
7045  * 
7046  */
7047
7048 /**
7049  * @class Roo.bootstrap.Pagination
7050  * @extends Roo.bootstrap.Component
7051  * Bootstrap Pagination class
7052  * @cfg {String} size xs | sm | md | lg
7053  * @cfg {Boolean} inverse false | true
7054  * 
7055  * @constructor
7056  * Create a new Pagination
7057  * @param {Object} config The config object
7058  */
7059
7060 Roo.bootstrap.Pagination = function(config){
7061     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7062 };
7063
7064 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7065     
7066     cls: false,
7067     size: false,
7068     inverse: false,
7069     
7070     getAutoCreate : function(){
7071         var cfg = {
7072             tag: 'ul',
7073                 cls: 'pagination'
7074         };
7075         if (this.inverse) {
7076             cfg.cls += ' inverse';
7077         }
7078         if (this.html) {
7079             cfg.html=this.html;
7080         }
7081         if (this.cls) {
7082             cfg.cls += " " + this.cls;
7083         }
7084         return cfg;
7085     }
7086    
7087 });
7088
7089  
7090
7091  /*
7092  * - LGPL
7093  *
7094  * Pagination item
7095  * 
7096  */
7097
7098
7099 /**
7100  * @class Roo.bootstrap.PaginationItem
7101  * @extends Roo.bootstrap.Component
7102  * Bootstrap PaginationItem class
7103  * @cfg {String} html text
7104  * @cfg {String} href the link
7105  * @cfg {Boolean} preventDefault (true | false) default true
7106  * @cfg {Boolean} active (true | false) default false
7107  * @cfg {Boolean} disabled default false
7108  * 
7109  * 
7110  * @constructor
7111  * Create a new PaginationItem
7112  * @param {Object} config The config object
7113  */
7114
7115
7116 Roo.bootstrap.PaginationItem = function(config){
7117     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7118     this.addEvents({
7119         // raw events
7120         /**
7121          * @event click
7122          * The raw click event for the entire grid.
7123          * @param {Roo.EventObject} e
7124          */
7125         "click" : true
7126     });
7127 };
7128
7129 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7130     
7131     href : false,
7132     html : false,
7133     preventDefault: true,
7134     active : false,
7135     cls : false,
7136     disabled: false,
7137     
7138     getAutoCreate : function(){
7139         var cfg= {
7140             tag: 'li',
7141             cn: [
7142                 {
7143                     tag : 'a',
7144                     href : this.href ? this.href : '#',
7145                     html : this.html ? this.html : ''
7146                 }
7147             ]
7148         };
7149         
7150         if(this.cls){
7151             cfg.cls = this.cls;
7152         }
7153         
7154         if(this.disabled){
7155             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7156         }
7157         
7158         if(this.active){
7159             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7160         }
7161         
7162         return cfg;
7163     },
7164     
7165     initEvents: function() {
7166         
7167         this.el.on('click', this.onClick, this);
7168         
7169     },
7170     onClick : function(e)
7171     {
7172         Roo.log('PaginationItem on click ');
7173         if(this.preventDefault){
7174             e.preventDefault();
7175         }
7176         
7177         if(this.disabled){
7178             return;
7179         }
7180         
7181         this.fireEvent('click', this, e);
7182     }
7183    
7184 });
7185
7186  
7187
7188  /*
7189  * - LGPL
7190  *
7191  * slider
7192  * 
7193  */
7194
7195
7196 /**
7197  * @class Roo.bootstrap.Slider
7198  * @extends Roo.bootstrap.Component
7199  * Bootstrap Slider class
7200  *    
7201  * @constructor
7202  * Create a new Slider
7203  * @param {Object} config The config object
7204  */
7205
7206 Roo.bootstrap.Slider = function(config){
7207     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7208 };
7209
7210 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7211     
7212     getAutoCreate : function(){
7213         
7214         var cfg = {
7215             tag: 'div',
7216             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7217             cn: [
7218                 {
7219                     tag: 'a',
7220                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7221                 }
7222             ]
7223         };
7224         
7225         return cfg;
7226     }
7227    
7228 });
7229
7230  /*
7231  * Based on:
7232  * Ext JS Library 1.1.1
7233  * Copyright(c) 2006-2007, Ext JS, LLC.
7234  *
7235  * Originally Released Under LGPL - original licence link has changed is not relivant.
7236  *
7237  * Fork - LGPL
7238  * <script type="text/javascript">
7239  */
7240  
7241
7242 /**
7243  * @class Roo.grid.ColumnModel
7244  * @extends Roo.util.Observable
7245  * This is the default implementation of a ColumnModel used by the Grid. It defines
7246  * the columns in the grid.
7247  * <br>Usage:<br>
7248  <pre><code>
7249  var colModel = new Roo.grid.ColumnModel([
7250         {header: "Ticker", width: 60, sortable: true, locked: true},
7251         {header: "Company Name", width: 150, sortable: true},
7252         {header: "Market Cap.", width: 100, sortable: true},
7253         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7254         {header: "Employees", width: 100, sortable: true, resizable: false}
7255  ]);
7256  </code></pre>
7257  * <p>
7258  
7259  * The config options listed for this class are options which may appear in each
7260  * individual column definition.
7261  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7262  * @constructor
7263  * @param {Object} config An Array of column config objects. See this class's
7264  * config objects for details.
7265 */
7266 Roo.grid.ColumnModel = function(config){
7267         /**
7268      * The config passed into the constructor
7269      */
7270     this.config = config;
7271     this.lookup = {};
7272
7273     // if no id, create one
7274     // if the column does not have a dataIndex mapping,
7275     // map it to the order it is in the config
7276     for(var i = 0, len = config.length; i < len; i++){
7277         var c = config[i];
7278         if(typeof c.dataIndex == "undefined"){
7279             c.dataIndex = i;
7280         }
7281         if(typeof c.renderer == "string"){
7282             c.renderer = Roo.util.Format[c.renderer];
7283         }
7284         if(typeof c.id == "undefined"){
7285             c.id = Roo.id();
7286         }
7287         if(c.editor && c.editor.xtype){
7288             c.editor  = Roo.factory(c.editor, Roo.grid);
7289         }
7290         if(c.editor && c.editor.isFormField){
7291             c.editor = new Roo.grid.GridEditor(c.editor);
7292         }
7293         this.lookup[c.id] = c;
7294     }
7295
7296     /**
7297      * The width of columns which have no width specified (defaults to 100)
7298      * @type Number
7299      */
7300     this.defaultWidth = 100;
7301
7302     /**
7303      * Default sortable of columns which have no sortable specified (defaults to false)
7304      * @type Boolean
7305      */
7306     this.defaultSortable = false;
7307
7308     this.addEvents({
7309         /**
7310              * @event widthchange
7311              * Fires when the width of a column changes.
7312              * @param {ColumnModel} this
7313              * @param {Number} columnIndex The column index
7314              * @param {Number} newWidth The new width
7315              */
7316             "widthchange": true,
7317         /**
7318              * @event headerchange
7319              * Fires when the text of a header changes.
7320              * @param {ColumnModel} this
7321              * @param {Number} columnIndex The column index
7322              * @param {Number} newText The new header text
7323              */
7324             "headerchange": true,
7325         /**
7326              * @event hiddenchange
7327              * Fires when a column is hidden or "unhidden".
7328              * @param {ColumnModel} this
7329              * @param {Number} columnIndex The column index
7330              * @param {Boolean} hidden true if hidden, false otherwise
7331              */
7332             "hiddenchange": true,
7333             /**
7334          * @event columnmoved
7335          * Fires when a column is moved.
7336          * @param {ColumnModel} this
7337          * @param {Number} oldIndex
7338          * @param {Number} newIndex
7339          */
7340         "columnmoved" : true,
7341         /**
7342          * @event columlockchange
7343          * Fires when a column's locked state is changed
7344          * @param {ColumnModel} this
7345          * @param {Number} colIndex
7346          * @param {Boolean} locked true if locked
7347          */
7348         "columnlockchange" : true
7349     });
7350     Roo.grid.ColumnModel.superclass.constructor.call(this);
7351 };
7352 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7353     /**
7354      * @cfg {String} header The header text to display in the Grid view.
7355      */
7356     /**
7357      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7358      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7359      * specified, the column's index is used as an index into the Record's data Array.
7360      */
7361     /**
7362      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7363      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7364      */
7365     /**
7366      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7367      * Defaults to the value of the {@link #defaultSortable} property.
7368      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7369      */
7370     /**
7371      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7372      */
7373     /**
7374      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7375      */
7376     /**
7377      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7378      */
7379     /**
7380      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7381      */
7382     /**
7383      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7384      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7385      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7386      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7387      */
7388        /**
7389      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7390      */
7391     /**
7392      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7393      */
7394     /**
7395      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7396      */
7397     /**
7398      * @cfg {String} cursor (Optional)
7399      */
7400     /**
7401      * @cfg {String} tooltip (Optional)
7402      */
7403     /**
7404      * @cfg {Number} xs (Optional)
7405      */
7406     /**
7407      * @cfg {Number} sm (Optional)
7408      */
7409     /**
7410      * @cfg {Number} md (Optional)
7411      */
7412     /**
7413      * @cfg {Number} lg (Optional)
7414      */
7415     /**
7416      * Returns the id of the column at the specified index.
7417      * @param {Number} index The column index
7418      * @return {String} the id
7419      */
7420     getColumnId : function(index){
7421         return this.config[index].id;
7422     },
7423
7424     /**
7425      * Returns the column for a specified id.
7426      * @param {String} id The column id
7427      * @return {Object} the column
7428      */
7429     getColumnById : function(id){
7430         return this.lookup[id];
7431     },
7432
7433     
7434     /**
7435      * Returns the column for a specified dataIndex.
7436      * @param {String} dataIndex The column dataIndex
7437      * @return {Object|Boolean} the column or false if not found
7438      */
7439     getColumnByDataIndex: function(dataIndex){
7440         var index = this.findColumnIndex(dataIndex);
7441         return index > -1 ? this.config[index] : false;
7442     },
7443     
7444     /**
7445      * Returns the index for a specified column id.
7446      * @param {String} id The column id
7447      * @return {Number} the index, or -1 if not found
7448      */
7449     getIndexById : function(id){
7450         for(var i = 0, len = this.config.length; i < len; i++){
7451             if(this.config[i].id == id){
7452                 return i;
7453             }
7454         }
7455         return -1;
7456     },
7457     
7458     /**
7459      * Returns the index for a specified column dataIndex.
7460      * @param {String} dataIndex The column dataIndex
7461      * @return {Number} the index, or -1 if not found
7462      */
7463     
7464     findColumnIndex : function(dataIndex){
7465         for(var i = 0, len = this.config.length; i < len; i++){
7466             if(this.config[i].dataIndex == dataIndex){
7467                 return i;
7468             }
7469         }
7470         return -1;
7471     },
7472     
7473     
7474     moveColumn : function(oldIndex, newIndex){
7475         var c = this.config[oldIndex];
7476         this.config.splice(oldIndex, 1);
7477         this.config.splice(newIndex, 0, c);
7478         this.dataMap = null;
7479         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7480     },
7481
7482     isLocked : function(colIndex){
7483         return this.config[colIndex].locked === true;
7484     },
7485
7486     setLocked : function(colIndex, value, suppressEvent){
7487         if(this.isLocked(colIndex) == value){
7488             return;
7489         }
7490         this.config[colIndex].locked = value;
7491         if(!suppressEvent){
7492             this.fireEvent("columnlockchange", this, colIndex, value);
7493         }
7494     },
7495
7496     getTotalLockedWidth : function(){
7497         var totalWidth = 0;
7498         for(var i = 0; i < this.config.length; i++){
7499             if(this.isLocked(i) && !this.isHidden(i)){
7500                 this.totalWidth += this.getColumnWidth(i);
7501             }
7502         }
7503         return totalWidth;
7504     },
7505
7506     getLockedCount : function(){
7507         for(var i = 0, len = this.config.length; i < len; i++){
7508             if(!this.isLocked(i)){
7509                 return i;
7510             }
7511         }
7512         
7513         return this.config.length;
7514     },
7515
7516     /**
7517      * Returns the number of columns.
7518      * @return {Number}
7519      */
7520     getColumnCount : function(visibleOnly){
7521         if(visibleOnly === true){
7522             var c = 0;
7523             for(var i = 0, len = this.config.length; i < len; i++){
7524                 if(!this.isHidden(i)){
7525                     c++;
7526                 }
7527             }
7528             return c;
7529         }
7530         return this.config.length;
7531     },
7532
7533     /**
7534      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7535      * @param {Function} fn
7536      * @param {Object} scope (optional)
7537      * @return {Array} result
7538      */
7539     getColumnsBy : function(fn, scope){
7540         var r = [];
7541         for(var i = 0, len = this.config.length; i < len; i++){
7542             var c = this.config[i];
7543             if(fn.call(scope||this, c, i) === true){
7544                 r[r.length] = c;
7545             }
7546         }
7547         return r;
7548     },
7549
7550     /**
7551      * Returns true if the specified column is sortable.
7552      * @param {Number} col The column index
7553      * @return {Boolean}
7554      */
7555     isSortable : function(col){
7556         if(typeof this.config[col].sortable == "undefined"){
7557             return this.defaultSortable;
7558         }
7559         return this.config[col].sortable;
7560     },
7561
7562     /**
7563      * Returns the rendering (formatting) function defined for the column.
7564      * @param {Number} col The column index.
7565      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7566      */
7567     getRenderer : function(col){
7568         if(!this.config[col].renderer){
7569             return Roo.grid.ColumnModel.defaultRenderer;
7570         }
7571         return this.config[col].renderer;
7572     },
7573
7574     /**
7575      * Sets the rendering (formatting) function for a column.
7576      * @param {Number} col The column index
7577      * @param {Function} fn The function to use to process the cell's raw data
7578      * to return HTML markup for the grid view. The render function is called with
7579      * the following parameters:<ul>
7580      * <li>Data value.</li>
7581      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7582      * <li>css A CSS style string to apply to the table cell.</li>
7583      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7584      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7585      * <li>Row index</li>
7586      * <li>Column index</li>
7587      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7588      */
7589     setRenderer : function(col, fn){
7590         this.config[col].renderer = fn;
7591     },
7592
7593     /**
7594      * Returns the width for the specified column.
7595      * @param {Number} col The column index
7596      * @return {Number}
7597      */
7598     getColumnWidth : function(col){
7599         return this.config[col].width * 1 || this.defaultWidth;
7600     },
7601
7602     /**
7603      * Sets the width for a column.
7604      * @param {Number} col The column index
7605      * @param {Number} width The new width
7606      */
7607     setColumnWidth : function(col, width, suppressEvent){
7608         this.config[col].width = width;
7609         this.totalWidth = null;
7610         if(!suppressEvent){
7611              this.fireEvent("widthchange", this, col, width);
7612         }
7613     },
7614
7615     /**
7616      * Returns the total width of all columns.
7617      * @param {Boolean} includeHidden True to include hidden column widths
7618      * @return {Number}
7619      */
7620     getTotalWidth : function(includeHidden){
7621         if(!this.totalWidth){
7622             this.totalWidth = 0;
7623             for(var i = 0, len = this.config.length; i < len; i++){
7624                 if(includeHidden || !this.isHidden(i)){
7625                     this.totalWidth += this.getColumnWidth(i);
7626                 }
7627             }
7628         }
7629         return this.totalWidth;
7630     },
7631
7632     /**
7633      * Returns the header for the specified column.
7634      * @param {Number} col The column index
7635      * @return {String}
7636      */
7637     getColumnHeader : function(col){
7638         return this.config[col].header;
7639     },
7640
7641     /**
7642      * Sets the header for a column.
7643      * @param {Number} col The column index
7644      * @param {String} header The new header
7645      */
7646     setColumnHeader : function(col, header){
7647         this.config[col].header = header;
7648         this.fireEvent("headerchange", this, col, header);
7649     },
7650
7651     /**
7652      * Returns the tooltip for the specified column.
7653      * @param {Number} col The column index
7654      * @return {String}
7655      */
7656     getColumnTooltip : function(col){
7657             return this.config[col].tooltip;
7658     },
7659     /**
7660      * Sets the tooltip for a column.
7661      * @param {Number} col The column index
7662      * @param {String} tooltip The new tooltip
7663      */
7664     setColumnTooltip : function(col, tooltip){
7665             this.config[col].tooltip = tooltip;
7666     },
7667
7668     /**
7669      * Returns the dataIndex for the specified column.
7670      * @param {Number} col The column index
7671      * @return {Number}
7672      */
7673     getDataIndex : function(col){
7674         return this.config[col].dataIndex;
7675     },
7676
7677     /**
7678      * Sets the dataIndex for a column.
7679      * @param {Number} col The column index
7680      * @param {Number} dataIndex The new dataIndex
7681      */
7682     setDataIndex : function(col, dataIndex){
7683         this.config[col].dataIndex = dataIndex;
7684     },
7685
7686     
7687     
7688     /**
7689      * Returns true if the cell is editable.
7690      * @param {Number} colIndex The column index
7691      * @param {Number} rowIndex The row index - this is nto actually used..?
7692      * @return {Boolean}
7693      */
7694     isCellEditable : function(colIndex, rowIndex){
7695         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7696     },
7697
7698     /**
7699      * Returns the editor defined for the cell/column.
7700      * return false or null to disable editing.
7701      * @param {Number} colIndex The column index
7702      * @param {Number} rowIndex The row index
7703      * @return {Object}
7704      */
7705     getCellEditor : function(colIndex, rowIndex){
7706         return this.config[colIndex].editor;
7707     },
7708
7709     /**
7710      * Sets if a column is editable.
7711      * @param {Number} col The column index
7712      * @param {Boolean} editable True if the column is editable
7713      */
7714     setEditable : function(col, editable){
7715         this.config[col].editable = editable;
7716     },
7717
7718
7719     /**
7720      * Returns true if the column is hidden.
7721      * @param {Number} colIndex The column index
7722      * @return {Boolean}
7723      */
7724     isHidden : function(colIndex){
7725         return this.config[colIndex].hidden;
7726     },
7727
7728
7729     /**
7730      * Returns true if the column width cannot be changed
7731      */
7732     isFixed : function(colIndex){
7733         return this.config[colIndex].fixed;
7734     },
7735
7736     /**
7737      * Returns true if the column can be resized
7738      * @return {Boolean}
7739      */
7740     isResizable : function(colIndex){
7741         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7742     },
7743     /**
7744      * Sets if a column is hidden.
7745      * @param {Number} colIndex The column index
7746      * @param {Boolean} hidden True if the column is hidden
7747      */
7748     setHidden : function(colIndex, hidden){
7749         this.config[colIndex].hidden = hidden;
7750         this.totalWidth = null;
7751         this.fireEvent("hiddenchange", this, colIndex, hidden);
7752     },
7753
7754     /**
7755      * Sets the editor for a column.
7756      * @param {Number} col The column index
7757      * @param {Object} editor The editor object
7758      */
7759     setEditor : function(col, editor){
7760         this.config[col].editor = editor;
7761     }
7762 });
7763
7764 Roo.grid.ColumnModel.defaultRenderer = function(value)
7765 {
7766     if(typeof value == "object") {
7767         return value;
7768     }
7769         if(typeof value == "string" && value.length < 1){
7770             return "&#160;";
7771         }
7772     
7773         return String.format("{0}", value);
7774 };
7775
7776 // Alias for backwards compatibility
7777 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7778 /*
7779  * Based on:
7780  * Ext JS Library 1.1.1
7781  * Copyright(c) 2006-2007, Ext JS, LLC.
7782  *
7783  * Originally Released Under LGPL - original licence link has changed is not relivant.
7784  *
7785  * Fork - LGPL
7786  * <script type="text/javascript">
7787  */
7788  
7789 /**
7790  * @class Roo.LoadMask
7791  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7792  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7793  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7794  * element's UpdateManager load indicator and will be destroyed after the initial load.
7795  * @constructor
7796  * Create a new LoadMask
7797  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7798  * @param {Object} config The config object
7799  */
7800 Roo.LoadMask = function(el, config){
7801     this.el = Roo.get(el);
7802     Roo.apply(this, config);
7803     if(this.store){
7804         this.store.on('beforeload', this.onBeforeLoad, this);
7805         this.store.on('load', this.onLoad, this);
7806         this.store.on('loadexception', this.onLoadException, this);
7807         this.removeMask = false;
7808     }else{
7809         var um = this.el.getUpdateManager();
7810         um.showLoadIndicator = false; // disable the default indicator
7811         um.on('beforeupdate', this.onBeforeLoad, this);
7812         um.on('update', this.onLoad, this);
7813         um.on('failure', this.onLoad, this);
7814         this.removeMask = true;
7815     }
7816 };
7817
7818 Roo.LoadMask.prototype = {
7819     /**
7820      * @cfg {Boolean} removeMask
7821      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7822      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7823      */
7824     /**
7825      * @cfg {String} msg
7826      * The text to display in a centered loading message box (defaults to 'Loading...')
7827      */
7828     msg : 'Loading...',
7829     /**
7830      * @cfg {String} msgCls
7831      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7832      */
7833     msgCls : 'x-mask-loading',
7834
7835     /**
7836      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7837      * @type Boolean
7838      */
7839     disabled: false,
7840
7841     /**
7842      * Disables the mask to prevent it from being displayed
7843      */
7844     disable : function(){
7845        this.disabled = true;
7846     },
7847
7848     /**
7849      * Enables the mask so that it can be displayed
7850      */
7851     enable : function(){
7852         this.disabled = false;
7853     },
7854     
7855     onLoadException : function()
7856     {
7857         Roo.log(arguments);
7858         
7859         if (typeof(arguments[3]) != 'undefined') {
7860             Roo.MessageBox.alert("Error loading",arguments[3]);
7861         } 
7862         /*
7863         try {
7864             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7865                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7866             }   
7867         } catch(e) {
7868             
7869         }
7870         */
7871     
7872         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7873     },
7874     // private
7875     onLoad : function()
7876     {
7877         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7878     },
7879
7880     // private
7881     onBeforeLoad : function(){
7882         if(!this.disabled){
7883             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7884         }
7885     },
7886
7887     // private
7888     destroy : function(){
7889         if(this.store){
7890             this.store.un('beforeload', this.onBeforeLoad, this);
7891             this.store.un('load', this.onLoad, this);
7892             this.store.un('loadexception', this.onLoadException, this);
7893         }else{
7894             var um = this.el.getUpdateManager();
7895             um.un('beforeupdate', this.onBeforeLoad, this);
7896             um.un('update', this.onLoad, this);
7897             um.un('failure', this.onLoad, this);
7898         }
7899     }
7900 };/*
7901  * - LGPL
7902  *
7903  * table
7904  * 
7905  */
7906
7907 /**
7908  * @class Roo.bootstrap.Table
7909  * @extends Roo.bootstrap.Component
7910  * Bootstrap Table class
7911  * @cfg {String} cls table class
7912  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7913  * @cfg {String} bgcolor Specifies the background color for a table
7914  * @cfg {Number} border Specifies whether the table cells should have borders or not
7915  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7916  * @cfg {Number} cellspacing Specifies the space between cells
7917  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7918  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7919  * @cfg {String} sortable Specifies that the table should be sortable
7920  * @cfg {String} summary Specifies a summary of the content of a table
7921  * @cfg {Number} width Specifies the width of a table
7922  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7923  * 
7924  * @cfg {boolean} striped Should the rows be alternative striped
7925  * @cfg {boolean} bordered Add borders to the table
7926  * @cfg {boolean} hover Add hover highlighting
7927  * @cfg {boolean} condensed Format condensed
7928  * @cfg {boolean} responsive Format condensed
7929  * @cfg {Boolean} loadMask (true|false) default false
7930  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7931  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7932  * @cfg {Boolean} rowSelection (true|false) default false
7933  * @cfg {Boolean} cellSelection (true|false) default false
7934  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7935  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7936  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7937  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7938  
7939  * 
7940  * @constructor
7941  * Create a new Table
7942  * @param {Object} config The config object
7943  */
7944
7945 Roo.bootstrap.Table = function(config){
7946     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7947     
7948   
7949     
7950     // BC...
7951     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7952     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7953     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7954     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7955     
7956     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7957     if (this.sm) {
7958         this.sm.grid = this;
7959         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7960         this.sm = this.selModel;
7961         this.sm.xmodule = this.xmodule || false;
7962     }
7963     
7964     if (this.cm && typeof(this.cm.config) == 'undefined') {
7965         this.colModel = new Roo.grid.ColumnModel(this.cm);
7966         this.cm = this.colModel;
7967         this.cm.xmodule = this.xmodule || false;
7968     }
7969     if (this.store) {
7970         this.store= Roo.factory(this.store, Roo.data);
7971         this.ds = this.store;
7972         this.ds.xmodule = this.xmodule || false;
7973          
7974     }
7975     if (this.footer && this.store) {
7976         this.footer.dataSource = this.ds;
7977         this.footer = Roo.factory(this.footer);
7978     }
7979     
7980     /** @private */
7981     this.addEvents({
7982         /**
7983          * @event cellclick
7984          * Fires when a cell is clicked
7985          * @param {Roo.bootstrap.Table} this
7986          * @param {Roo.Element} el
7987          * @param {Number} rowIndex
7988          * @param {Number} columnIndex
7989          * @param {Roo.EventObject} e
7990          */
7991         "cellclick" : true,
7992         /**
7993          * @event celldblclick
7994          * Fires when a cell is double clicked
7995          * @param {Roo.bootstrap.Table} this
7996          * @param {Roo.Element} el
7997          * @param {Number} rowIndex
7998          * @param {Number} columnIndex
7999          * @param {Roo.EventObject} e
8000          */
8001         "celldblclick" : true,
8002         /**
8003          * @event rowclick
8004          * Fires when a row is clicked
8005          * @param {Roo.bootstrap.Table} this
8006          * @param {Roo.Element} el
8007          * @param {Number} rowIndex
8008          * @param {Roo.EventObject} e
8009          */
8010         "rowclick" : true,
8011         /**
8012          * @event rowdblclick
8013          * Fires when a row is double clicked
8014          * @param {Roo.bootstrap.Table} this
8015          * @param {Roo.Element} el
8016          * @param {Number} rowIndex
8017          * @param {Roo.EventObject} e
8018          */
8019         "rowdblclick" : true,
8020         /**
8021          * @event mouseover
8022          * Fires when a mouseover occur
8023          * @param {Roo.bootstrap.Table} this
8024          * @param {Roo.Element} el
8025          * @param {Number} rowIndex
8026          * @param {Number} columnIndex
8027          * @param {Roo.EventObject} e
8028          */
8029         "mouseover" : true,
8030         /**
8031          * @event mouseout
8032          * Fires when a mouseout occur
8033          * @param {Roo.bootstrap.Table} this
8034          * @param {Roo.Element} el
8035          * @param {Number} rowIndex
8036          * @param {Number} columnIndex
8037          * @param {Roo.EventObject} e
8038          */
8039         "mouseout" : true,
8040         /**
8041          * @event rowclass
8042          * Fires when a row is rendered, so you can change add a style to it.
8043          * @param {Roo.bootstrap.Table} this
8044          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8045          */
8046         'rowclass' : true,
8047           /**
8048          * @event rowsrendered
8049          * Fires when all the  rows have been rendered
8050          * @param {Roo.bootstrap.Table} this
8051          */
8052         'rowsrendered' : true,
8053         /**
8054          * @event contextmenu
8055          * The raw contextmenu event for the entire grid.
8056          * @param {Roo.EventObject} e
8057          */
8058         "contextmenu" : true,
8059         /**
8060          * @event rowcontextmenu
8061          * Fires when a row is right clicked
8062          * @param {Roo.bootstrap.Table} this
8063          * @param {Number} rowIndex
8064          * @param {Roo.EventObject} e
8065          */
8066         "rowcontextmenu" : true,
8067         /**
8068          * @event cellcontextmenu
8069          * Fires when a cell is right clicked
8070          * @param {Roo.bootstrap.Table} this
8071          * @param {Number} rowIndex
8072          * @param {Number} cellIndex
8073          * @param {Roo.EventObject} e
8074          */
8075          "cellcontextmenu" : true,
8076          /**
8077          * @event headercontextmenu
8078          * Fires when a header is right clicked
8079          * @param {Roo.bootstrap.Table} this
8080          * @param {Number} columnIndex
8081          * @param {Roo.EventObject} e
8082          */
8083         "headercontextmenu" : true
8084     });
8085 };
8086
8087 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8088     
8089     cls: false,
8090     align: false,
8091     bgcolor: false,
8092     border: false,
8093     cellpadding: false,
8094     cellspacing: false,
8095     frame: false,
8096     rules: false,
8097     sortable: false,
8098     summary: false,
8099     width: false,
8100     striped : false,
8101     scrollBody : false,
8102     bordered: false,
8103     hover:  false,
8104     condensed : false,
8105     responsive : false,
8106     sm : false,
8107     cm : false,
8108     store : false,
8109     loadMask : false,
8110     footerShow : true,
8111     headerShow : true,
8112   
8113     rowSelection : false,
8114     cellSelection : false,
8115     layout : false,
8116     
8117     // Roo.Element - the tbody
8118     mainBody: false,
8119     // Roo.Element - thead element
8120     mainHead: false,
8121     
8122     container: false, // used by gridpanel...
8123     
8124     lazyLoad : false,
8125     
8126     CSS : Roo.util.CSS,
8127     
8128     auto_hide_footer : false,
8129     
8130     getAutoCreate : function()
8131     {
8132         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8133         
8134         cfg = {
8135             tag: 'table',
8136             cls : 'table',
8137             cn : []
8138         };
8139         if (this.scrollBody) {
8140             cfg.cls += ' table-body-fixed';
8141         }    
8142         if (this.striped) {
8143             cfg.cls += ' table-striped';
8144         }
8145         
8146         if (this.hover) {
8147             cfg.cls += ' table-hover';
8148         }
8149         if (this.bordered) {
8150             cfg.cls += ' table-bordered';
8151         }
8152         if (this.condensed) {
8153             cfg.cls += ' table-condensed';
8154         }
8155         if (this.responsive) {
8156             cfg.cls += ' table-responsive';
8157         }
8158         
8159         if (this.cls) {
8160             cfg.cls+=  ' ' +this.cls;
8161         }
8162         
8163         // this lot should be simplifed...
8164         var _t = this;
8165         var cp = [
8166             'align',
8167             'bgcolor',
8168             'border',
8169             'cellpadding',
8170             'cellspacing',
8171             'frame',
8172             'rules',
8173             'sortable',
8174             'summary',
8175             'width'
8176         ].forEach(function(k) {
8177             if (_t[k]) {
8178                 cfg[k] = _t[k];
8179             }
8180         });
8181         
8182         
8183         if (this.layout) {
8184             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8185         }
8186         
8187         if(this.store || this.cm){
8188             if(this.headerShow){
8189                 cfg.cn.push(this.renderHeader());
8190             }
8191             
8192             cfg.cn.push(this.renderBody());
8193             
8194             if(this.footerShow){
8195                 cfg.cn.push(this.renderFooter());
8196             }
8197             // where does this come from?
8198             //cfg.cls+=  ' TableGrid';
8199         }
8200         
8201         return { cn : [ cfg ] };
8202     },
8203     
8204     initEvents : function()
8205     {   
8206         if(!this.store || !this.cm){
8207             return;
8208         }
8209         if (this.selModel) {
8210             this.selModel.initEvents();
8211         }
8212         
8213         
8214         //Roo.log('initEvents with ds!!!!');
8215         
8216         this.mainBody = this.el.select('tbody', true).first();
8217         this.mainHead = this.el.select('thead', true).first();
8218         this.mainFoot = this.el.select('tfoot', true).first();
8219         
8220         
8221         
8222         var _this = this;
8223         
8224         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8225             e.on('click', _this.sort, _this);
8226         });
8227         
8228         this.mainBody.on("click", this.onClick, this);
8229         this.mainBody.on("dblclick", this.onDblClick, this);
8230         
8231         // why is this done????? = it breaks dialogs??
8232         //this.parent().el.setStyle('position', 'relative');
8233         
8234         
8235         if (this.footer) {
8236             this.footer.parentId = this.id;
8237             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8238             
8239             if(this.lazyLoad){
8240                 this.el.select('tfoot tr td').first().addClass('hide');
8241             }
8242         } 
8243         
8244         if(this.loadMask) {
8245             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8246         }
8247         
8248         this.store.on('load', this.onLoad, this);
8249         this.store.on('beforeload', this.onBeforeLoad, this);
8250         this.store.on('update', this.onUpdate, this);
8251         this.store.on('add', this.onAdd, this);
8252         this.store.on("clear", this.clear, this);
8253         
8254         this.el.on("contextmenu", this.onContextMenu, this);
8255         
8256         this.mainBody.on('scroll', this.onBodyScroll, this);
8257         
8258         this.cm.on("headerchange", this.onHeaderChange, this);
8259         
8260         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8261         
8262     },
8263     
8264     onContextMenu : function(e, t)
8265     {
8266         this.processEvent("contextmenu", e);
8267     },
8268     
8269     processEvent : function(name, e)
8270     {
8271         if (name != 'touchstart' ) {
8272             this.fireEvent(name, e);    
8273         }
8274         
8275         var t = e.getTarget();
8276         
8277         var cell = Roo.get(t);
8278         
8279         if(!cell){
8280             return;
8281         }
8282         
8283         if(cell.findParent('tfoot', false, true)){
8284             return;
8285         }
8286         
8287         if(cell.findParent('thead', false, true)){
8288             
8289             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8290                 cell = Roo.get(t).findParent('th', false, true);
8291                 if (!cell) {
8292                     Roo.log("failed to find th in thead?");
8293                     Roo.log(e.getTarget());
8294                     return;
8295                 }
8296             }
8297             
8298             var cellIndex = cell.dom.cellIndex;
8299             
8300             var ename = name == 'touchstart' ? 'click' : name;
8301             this.fireEvent("header" + ename, this, cellIndex, e);
8302             
8303             return;
8304         }
8305         
8306         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8307             cell = Roo.get(t).findParent('td', false, true);
8308             if (!cell) {
8309                 Roo.log("failed to find th in tbody?");
8310                 Roo.log(e.getTarget());
8311                 return;
8312             }
8313         }
8314         
8315         var row = cell.findParent('tr', false, true);
8316         var cellIndex = cell.dom.cellIndex;
8317         var rowIndex = row.dom.rowIndex - 1;
8318         
8319         if(row !== false){
8320             
8321             this.fireEvent("row" + name, this, rowIndex, e);
8322             
8323             if(cell !== false){
8324             
8325                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8326             }
8327         }
8328         
8329     },
8330     
8331     onMouseover : function(e, el)
8332     {
8333         var cell = Roo.get(el);
8334         
8335         if(!cell){
8336             return;
8337         }
8338         
8339         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8340             cell = cell.findParent('td', false, true);
8341         }
8342         
8343         var row = cell.findParent('tr', false, true);
8344         var cellIndex = cell.dom.cellIndex;
8345         var rowIndex = row.dom.rowIndex - 1; // start from 0
8346         
8347         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8348         
8349     },
8350     
8351     onMouseout : function(e, el)
8352     {
8353         var cell = Roo.get(el);
8354         
8355         if(!cell){
8356             return;
8357         }
8358         
8359         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8360             cell = cell.findParent('td', false, true);
8361         }
8362         
8363         var row = cell.findParent('tr', false, true);
8364         var cellIndex = cell.dom.cellIndex;
8365         var rowIndex = row.dom.rowIndex - 1; // start from 0
8366         
8367         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8368         
8369     },
8370     
8371     onClick : function(e, el)
8372     {
8373         var cell = Roo.get(el);
8374         
8375         if(!cell || (!this.cellSelection && !this.rowSelection)){
8376             return;
8377         }
8378         
8379         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8380             cell = cell.findParent('td', false, true);
8381         }
8382         
8383         if(!cell || typeof(cell) == 'undefined'){
8384             return;
8385         }
8386         
8387         var row = cell.findParent('tr', false, true);
8388         
8389         if(!row || typeof(row) == 'undefined'){
8390             return;
8391         }
8392         
8393         var cellIndex = cell.dom.cellIndex;
8394         var rowIndex = this.getRowIndex(row);
8395         
8396         // why??? - should these not be based on SelectionModel?
8397         if(this.cellSelection){
8398             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8399         }
8400         
8401         if(this.rowSelection){
8402             this.fireEvent('rowclick', this, row, rowIndex, e);
8403         }
8404         
8405         
8406     },
8407         
8408     onDblClick : function(e,el)
8409     {
8410         var cell = Roo.get(el);
8411         
8412         if(!cell || (!this.cellSelection && !this.rowSelection)){
8413             return;
8414         }
8415         
8416         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8417             cell = cell.findParent('td', false, true);
8418         }
8419         
8420         if(!cell || typeof(cell) == 'undefined'){
8421             return;
8422         }
8423         
8424         var row = cell.findParent('tr', false, true);
8425         
8426         if(!row || typeof(row) == 'undefined'){
8427             return;
8428         }
8429         
8430         var cellIndex = cell.dom.cellIndex;
8431         var rowIndex = this.getRowIndex(row);
8432         
8433         if(this.cellSelection){
8434             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8435         }
8436         
8437         if(this.rowSelection){
8438             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8439         }
8440     },
8441     
8442     sort : function(e,el)
8443     {
8444         var col = Roo.get(el);
8445         
8446         if(!col.hasClass('sortable')){
8447             return;
8448         }
8449         
8450         var sort = col.attr('sort');
8451         var dir = 'ASC';
8452         
8453         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8454             dir = 'DESC';
8455         }
8456         
8457         this.store.sortInfo = {field : sort, direction : dir};
8458         
8459         if (this.footer) {
8460             Roo.log("calling footer first");
8461             this.footer.onClick('first');
8462         } else {
8463         
8464             this.store.load({ params : { start : 0 } });
8465         }
8466     },
8467     
8468     renderHeader : function()
8469     {
8470         var header = {
8471             tag: 'thead',
8472             cn : []
8473         };
8474         
8475         var cm = this.cm;
8476         this.totalWidth = 0;
8477         
8478         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8479             
8480             var config = cm.config[i];
8481             
8482             var c = {
8483                 tag: 'th',
8484                 cls : 'x-hcol-' + i,
8485                 style : '',
8486                 html: cm.getColumnHeader(i)
8487             };
8488             
8489             var hh = '';
8490             
8491             if(typeof(config.sortable) != 'undefined' && config.sortable){
8492                 c.cls = 'sortable';
8493                 c.html = '<i class="glyphicon"></i>' + c.html;
8494             }
8495             
8496             // could use BS4 hidden-..-down 
8497             
8498             if(typeof(config.lgHeader) != 'undefined'){
8499                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8500             }
8501             
8502             if(typeof(config.mdHeader) != 'undefined'){
8503                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8504             }
8505             
8506             if(typeof(config.smHeader) != 'undefined'){
8507                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8508             }
8509             
8510             if(typeof(config.xsHeader) != 'undefined'){
8511                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8512             }
8513             
8514             if(hh.length){
8515                 c.html = hh;
8516             }
8517             
8518             if(typeof(config.tooltip) != 'undefined'){
8519                 c.tooltip = config.tooltip;
8520             }
8521             
8522             if(typeof(config.colspan) != 'undefined'){
8523                 c.colspan = config.colspan;
8524             }
8525             
8526             if(typeof(config.hidden) != 'undefined' && config.hidden){
8527                 c.style += ' display:none;';
8528             }
8529             
8530             if(typeof(config.dataIndex) != 'undefined'){
8531                 c.sort = config.dataIndex;
8532             }
8533             
8534            
8535             
8536             if(typeof(config.align) != 'undefined' && config.align.length){
8537                 c.style += ' text-align:' + config.align + ';';
8538             }
8539             
8540             if(typeof(config.width) != 'undefined'){
8541                 c.style += ' width:' + config.width + 'px;';
8542                 this.totalWidth += config.width;
8543             } else {
8544                 this.totalWidth += 100; // assume minimum of 100 per column?
8545             }
8546             
8547             if(typeof(config.cls) != 'undefined'){
8548                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8549             }
8550             
8551             ['xs','sm','md','lg'].map(function(size){
8552                 
8553                 if(typeof(config[size]) == 'undefined'){
8554                     return;
8555                 }
8556                  
8557                 if (!config[size]) { // 0 = hidden
8558                     // BS 4 '0' is treated as hide that column and below.
8559                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8560                     return;
8561                 }
8562                 
8563                 c.cls += ' col-' + size + '-' + config[size] + (
8564                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8565                 );
8566                 
8567                 
8568             });
8569             
8570             header.cn.push(c)
8571         }
8572         
8573         return header;
8574     },
8575     
8576     renderBody : function()
8577     {
8578         var body = {
8579             tag: 'tbody',
8580             cn : [
8581                 {
8582                     tag: 'tr',
8583                     cn : [
8584                         {
8585                             tag : 'td',
8586                             colspan :  this.cm.getColumnCount()
8587                         }
8588                     ]
8589                 }
8590             ]
8591         };
8592         
8593         return body;
8594     },
8595     
8596     renderFooter : function()
8597     {
8598         var footer = {
8599             tag: 'tfoot',
8600             cn : [
8601                 {
8602                     tag: 'tr',
8603                     cn : [
8604                         {
8605                             tag : 'td',
8606                             colspan :  this.cm.getColumnCount()
8607                         }
8608                     ]
8609                 }
8610             ]
8611         };
8612         
8613         return footer;
8614     },
8615     
8616     
8617     
8618     onLoad : function()
8619     {
8620 //        Roo.log('ds onload');
8621         this.clear();
8622         
8623         var _this = this;
8624         var cm = this.cm;
8625         var ds = this.store;
8626         
8627         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8628             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8629             if (_this.store.sortInfo) {
8630                     
8631                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8632                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8633                 }
8634                 
8635                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8636                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8637                 }
8638             }
8639         });
8640         
8641         var tbody =  this.mainBody;
8642               
8643         if(ds.getCount() > 0){
8644             ds.data.each(function(d,rowIndex){
8645                 var row =  this.renderRow(cm, ds, rowIndex);
8646                 
8647                 tbody.createChild(row);
8648                 
8649                 var _this = this;
8650                 
8651                 if(row.cellObjects.length){
8652                     Roo.each(row.cellObjects, function(r){
8653                         _this.renderCellObject(r);
8654                     })
8655                 }
8656                 
8657             }, this);
8658         }
8659         
8660         var tfoot = this.el.select('tfoot', true).first();
8661         
8662         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8663             
8664             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8665             
8666             var total = this.ds.getTotalCount();
8667             
8668             if(this.footer.pageSize < total){
8669                 this.mainFoot.show();
8670             }
8671         }
8672         
8673         Roo.each(this.el.select('tbody td', true).elements, function(e){
8674             e.on('mouseover', _this.onMouseover, _this);
8675         });
8676         
8677         Roo.each(this.el.select('tbody td', true).elements, function(e){
8678             e.on('mouseout', _this.onMouseout, _this);
8679         });
8680         this.fireEvent('rowsrendered', this);
8681         
8682         this.autoSize();
8683     },
8684     
8685     
8686     onUpdate : function(ds,record)
8687     {
8688         this.refreshRow(record);
8689         this.autoSize();
8690     },
8691     
8692     onRemove : function(ds, record, index, isUpdate){
8693         if(isUpdate !== true){
8694             this.fireEvent("beforerowremoved", this, index, record);
8695         }
8696         var bt = this.mainBody.dom;
8697         
8698         var rows = this.el.select('tbody > tr', true).elements;
8699         
8700         if(typeof(rows[index]) != 'undefined'){
8701             bt.removeChild(rows[index].dom);
8702         }
8703         
8704 //        if(bt.rows[index]){
8705 //            bt.removeChild(bt.rows[index]);
8706 //        }
8707         
8708         if(isUpdate !== true){
8709             //this.stripeRows(index);
8710             //this.syncRowHeights(index, index);
8711             //this.layout();
8712             this.fireEvent("rowremoved", this, index, record);
8713         }
8714     },
8715     
8716     onAdd : function(ds, records, rowIndex)
8717     {
8718         //Roo.log('on Add called');
8719         // - note this does not handle multiple adding very well..
8720         var bt = this.mainBody.dom;
8721         for (var i =0 ; i < records.length;i++) {
8722             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8723             //Roo.log(records[i]);
8724             //Roo.log(this.store.getAt(rowIndex+i));
8725             this.insertRow(this.store, rowIndex + i, false);
8726             return;
8727         }
8728         
8729     },
8730     
8731     
8732     refreshRow : function(record){
8733         var ds = this.store, index;
8734         if(typeof record == 'number'){
8735             index = record;
8736             record = ds.getAt(index);
8737         }else{
8738             index = ds.indexOf(record);
8739             if (index < 0) {
8740                 return; // should not happen - but seems to 
8741             }
8742         }
8743         this.insertRow(ds, index, true);
8744         this.autoSize();
8745         this.onRemove(ds, record, index+1, true);
8746         this.autoSize();
8747         //this.syncRowHeights(index, index);
8748         //this.layout();
8749         this.fireEvent("rowupdated", this, index, record);
8750     },
8751     
8752     insertRow : function(dm, rowIndex, isUpdate){
8753         
8754         if(!isUpdate){
8755             this.fireEvent("beforerowsinserted", this, rowIndex);
8756         }
8757             //var s = this.getScrollState();
8758         var row = this.renderRow(this.cm, this.store, rowIndex);
8759         // insert before rowIndex..
8760         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8761         
8762         var _this = this;
8763                 
8764         if(row.cellObjects.length){
8765             Roo.each(row.cellObjects, function(r){
8766                 _this.renderCellObject(r);
8767             })
8768         }
8769             
8770         if(!isUpdate){
8771             this.fireEvent("rowsinserted", this, rowIndex);
8772             //this.syncRowHeights(firstRow, lastRow);
8773             //this.stripeRows(firstRow);
8774             //this.layout();
8775         }
8776         
8777     },
8778     
8779     
8780     getRowDom : function(rowIndex)
8781     {
8782         var rows = this.el.select('tbody > tr', true).elements;
8783         
8784         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8785         
8786     },
8787     // returns the object tree for a tr..
8788   
8789     
8790     renderRow : function(cm, ds, rowIndex) 
8791     {
8792         var d = ds.getAt(rowIndex);
8793         
8794         var row = {
8795             tag : 'tr',
8796             cls : 'x-row-' + rowIndex,
8797             cn : []
8798         };
8799             
8800         var cellObjects = [];
8801         
8802         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8803             var config = cm.config[i];
8804             
8805             var renderer = cm.getRenderer(i);
8806             var value = '';
8807             var id = false;
8808             
8809             if(typeof(renderer) !== 'undefined'){
8810                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8811             }
8812             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8813             // and are rendered into the cells after the row is rendered - using the id for the element.
8814             
8815             if(typeof(value) === 'object'){
8816                 id = Roo.id();
8817                 cellObjects.push({
8818                     container : id,
8819                     cfg : value 
8820                 })
8821             }
8822             
8823             var rowcfg = {
8824                 record: d,
8825                 rowIndex : rowIndex,
8826                 colIndex : i,
8827                 rowClass : ''
8828             };
8829
8830             this.fireEvent('rowclass', this, rowcfg);
8831             
8832             var td = {
8833                 tag: 'td',
8834                 cls : rowcfg.rowClass + ' x-col-' + i,
8835                 style: '',
8836                 html: (typeof(value) === 'object') ? '' : value
8837             };
8838             
8839             if (id) {
8840                 td.id = id;
8841             }
8842             
8843             if(typeof(config.colspan) != 'undefined'){
8844                 td.colspan = config.colspan;
8845             }
8846             
8847             if(typeof(config.hidden) != 'undefined' && config.hidden){
8848                 td.style += ' display:none;';
8849             }
8850             
8851             if(typeof(config.align) != 'undefined' && config.align.length){
8852                 td.style += ' text-align:' + config.align + ';';
8853             }
8854             if(typeof(config.valign) != 'undefined' && config.valign.length){
8855                 td.style += ' vertical-align:' + config.valign + ';';
8856             }
8857             
8858             if(typeof(config.width) != 'undefined'){
8859                 td.style += ' width:' +  config.width + 'px;';
8860             }
8861             
8862             if(typeof(config.cursor) != 'undefined'){
8863                 td.style += ' cursor:' +  config.cursor + ';';
8864             }
8865             
8866             if(typeof(config.cls) != 'undefined'){
8867                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8868             }
8869             
8870             ['xs','sm','md','lg'].map(function(size){
8871                 
8872                 if(typeof(config[size]) == 'undefined'){
8873                     return;
8874                 }
8875                 
8876                 
8877                   
8878                 if (!config[size]) { // 0 = hidden
8879                     // BS 4 '0' is treated as hide that column and below.
8880                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8881                     return;
8882                 }
8883                 
8884                 td.cls += ' col-' + size + '-' + config[size] + (
8885                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8886                 );
8887                  
8888
8889             });
8890             
8891             row.cn.push(td);
8892            
8893         }
8894         
8895         row.cellObjects = cellObjects;
8896         
8897         return row;
8898           
8899     },
8900     
8901     
8902     
8903     onBeforeLoad : function()
8904     {
8905         
8906     },
8907      /**
8908      * Remove all rows
8909      */
8910     clear : function()
8911     {
8912         this.el.select('tbody', true).first().dom.innerHTML = '';
8913     },
8914     /**
8915      * Show or hide a row.
8916      * @param {Number} rowIndex to show or hide
8917      * @param {Boolean} state hide
8918      */
8919     setRowVisibility : function(rowIndex, state)
8920     {
8921         var bt = this.mainBody.dom;
8922         
8923         var rows = this.el.select('tbody > tr', true).elements;
8924         
8925         if(typeof(rows[rowIndex]) == 'undefined'){
8926             return;
8927         }
8928         rows[rowIndex].dom.style.display = state ? '' : 'none';
8929     },
8930     
8931     
8932     getSelectionModel : function(){
8933         if(!this.selModel){
8934             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8935         }
8936         return this.selModel;
8937     },
8938     /*
8939      * Render the Roo.bootstrap object from renderder
8940      */
8941     renderCellObject : function(r)
8942     {
8943         var _this = this;
8944         
8945         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8946         
8947         var t = r.cfg.render(r.container);
8948         
8949         if(r.cfg.cn){
8950             Roo.each(r.cfg.cn, function(c){
8951                 var child = {
8952                     container: t.getChildContainer(),
8953                     cfg: c
8954                 };
8955                 _this.renderCellObject(child);
8956             })
8957         }
8958     },
8959     
8960     getRowIndex : function(row)
8961     {
8962         var rowIndex = -1;
8963         
8964         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8965             if(el != row){
8966                 return;
8967             }
8968             
8969             rowIndex = index;
8970         });
8971         
8972         return rowIndex;
8973     },
8974      /**
8975      * Returns the grid's underlying element = used by panel.Grid
8976      * @return {Element} The element
8977      */
8978     getGridEl : function(){
8979         return this.el;
8980     },
8981      /**
8982      * Forces a resize - used by panel.Grid
8983      * @return {Element} The element
8984      */
8985     autoSize : function()
8986     {
8987         //var ctr = Roo.get(this.container.dom.parentElement);
8988         var ctr = Roo.get(this.el.dom);
8989         
8990         var thd = this.getGridEl().select('thead',true).first();
8991         var tbd = this.getGridEl().select('tbody', true).first();
8992         var tfd = this.getGridEl().select('tfoot', true).first();
8993         
8994         var cw = ctr.getWidth();
8995         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8996         
8997         if (tbd) {
8998             
8999             tbd.setWidth(ctr.getWidth());
9000             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9001             // this needs fixing for various usage - currently only hydra job advers I think..
9002             //tdb.setHeight(
9003             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9004             //); 
9005             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9006             cw -= barsize;
9007         }
9008         cw = Math.max(cw, this.totalWidth);
9009         this.getGridEl().select('tbody tr',true).setWidth(cw);
9010         
9011         // resize 'expandable coloumn?
9012         
9013         return; // we doe not have a view in this design..
9014         
9015     },
9016     onBodyScroll: function()
9017     {
9018         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9019         if(this.mainHead){
9020             this.mainHead.setStyle({
9021                 'position' : 'relative',
9022                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9023             });
9024         }
9025         
9026         if(this.lazyLoad){
9027             
9028             var scrollHeight = this.mainBody.dom.scrollHeight;
9029             
9030             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9031             
9032             var height = this.mainBody.getHeight();
9033             
9034             if(scrollHeight - height == scrollTop) {
9035                 
9036                 var total = this.ds.getTotalCount();
9037                 
9038                 if(this.footer.cursor + this.footer.pageSize < total){
9039                     
9040                     this.footer.ds.load({
9041                         params : {
9042                             start : this.footer.cursor + this.footer.pageSize,
9043                             limit : this.footer.pageSize
9044                         },
9045                         add : true
9046                     });
9047                 }
9048             }
9049             
9050         }
9051     },
9052     
9053     onHeaderChange : function()
9054     {
9055         var header = this.renderHeader();
9056         var table = this.el.select('table', true).first();
9057         
9058         this.mainHead.remove();
9059         this.mainHead = table.createChild(header, this.mainBody, false);
9060     },
9061     
9062     onHiddenChange : function(colModel, colIndex, hidden)
9063     {
9064         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9065         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9066         
9067         this.CSS.updateRule(thSelector, "display", "");
9068         this.CSS.updateRule(tdSelector, "display", "");
9069         
9070         if(hidden){
9071             this.CSS.updateRule(thSelector, "display", "none");
9072             this.CSS.updateRule(tdSelector, "display", "none");
9073         }
9074         
9075         this.onHeaderChange();
9076         this.onLoad();
9077     },
9078     
9079     setColumnWidth: function(col_index, width)
9080     {
9081         // width = "md-2 xs-2..."
9082         if(!this.colModel.config[col_index]) {
9083             return;
9084         }
9085         
9086         var w = width.split(" ");
9087         
9088         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9089         
9090         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9091         
9092         
9093         for(var j = 0; j < w.length; j++) {
9094             
9095             if(!w[j]) {
9096                 continue;
9097             }
9098             
9099             var size_cls = w[j].split("-");
9100             
9101             if(!Number.isInteger(size_cls[1] * 1)) {
9102                 continue;
9103             }
9104             
9105             if(!this.colModel.config[col_index][size_cls[0]]) {
9106                 continue;
9107             }
9108             
9109             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9110                 continue;
9111             }
9112             
9113             h_row[0].classList.replace(
9114                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9115                 "col-"+size_cls[0]+"-"+size_cls[1]
9116             );
9117             
9118             for(var i = 0; i < rows.length; i++) {
9119                 
9120                 var size_cls = w[j].split("-");
9121                 
9122                 if(!Number.isInteger(size_cls[1] * 1)) {
9123                     continue;
9124                 }
9125                 
9126                 if(!this.colModel.config[col_index][size_cls[0]]) {
9127                     continue;
9128                 }
9129                 
9130                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9131                     continue;
9132                 }
9133                 
9134                 rows[i].classList.replace(
9135                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9136                     "col-"+size_cls[0]+"-"+size_cls[1]
9137                 );
9138             }
9139             
9140             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9141         }
9142     }
9143 });
9144
9145  
9146
9147  /*
9148  * - LGPL
9149  *
9150  * table cell
9151  * 
9152  */
9153
9154 /**
9155  * @class Roo.bootstrap.TableCell
9156  * @extends Roo.bootstrap.Component
9157  * Bootstrap TableCell class
9158  * @cfg {String} html cell contain text
9159  * @cfg {String} cls cell class
9160  * @cfg {String} tag cell tag (td|th) default td
9161  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9162  * @cfg {String} align Aligns the content in a cell
9163  * @cfg {String} axis Categorizes cells
9164  * @cfg {String} bgcolor Specifies the background color of a cell
9165  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9166  * @cfg {Number} colspan Specifies the number of columns a cell should span
9167  * @cfg {String} headers Specifies one or more header cells a cell is related to
9168  * @cfg {Number} height Sets the height of a cell
9169  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9170  * @cfg {Number} rowspan Sets the number of rows a cell should span
9171  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9172  * @cfg {String} valign Vertical aligns the content in a cell
9173  * @cfg {Number} width Specifies the width of a cell
9174  * 
9175  * @constructor
9176  * Create a new TableCell
9177  * @param {Object} config The config object
9178  */
9179
9180 Roo.bootstrap.TableCell = function(config){
9181     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9182 };
9183
9184 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9185     
9186     html: false,
9187     cls: false,
9188     tag: false,
9189     abbr: false,
9190     align: false,
9191     axis: false,
9192     bgcolor: false,
9193     charoff: false,
9194     colspan: false,
9195     headers: false,
9196     height: false,
9197     nowrap: false,
9198     rowspan: false,
9199     scope: false,
9200     valign: false,
9201     width: false,
9202     
9203     
9204     getAutoCreate : function(){
9205         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9206         
9207         cfg = {
9208             tag: 'td'
9209         };
9210         
9211         if(this.tag){
9212             cfg.tag = this.tag;
9213         }
9214         
9215         if (this.html) {
9216             cfg.html=this.html
9217         }
9218         if (this.cls) {
9219             cfg.cls=this.cls
9220         }
9221         if (this.abbr) {
9222             cfg.abbr=this.abbr
9223         }
9224         if (this.align) {
9225             cfg.align=this.align
9226         }
9227         if (this.axis) {
9228             cfg.axis=this.axis
9229         }
9230         if (this.bgcolor) {
9231             cfg.bgcolor=this.bgcolor
9232         }
9233         if (this.charoff) {
9234             cfg.charoff=this.charoff
9235         }
9236         if (this.colspan) {
9237             cfg.colspan=this.colspan
9238         }
9239         if (this.headers) {
9240             cfg.headers=this.headers
9241         }
9242         if (this.height) {
9243             cfg.height=this.height
9244         }
9245         if (this.nowrap) {
9246             cfg.nowrap=this.nowrap
9247         }
9248         if (this.rowspan) {
9249             cfg.rowspan=this.rowspan
9250         }
9251         if (this.scope) {
9252             cfg.scope=this.scope
9253         }
9254         if (this.valign) {
9255             cfg.valign=this.valign
9256         }
9257         if (this.width) {
9258             cfg.width=this.width
9259         }
9260         
9261         
9262         return cfg;
9263     }
9264    
9265 });
9266
9267  
9268
9269  /*
9270  * - LGPL
9271  *
9272  * table row
9273  * 
9274  */
9275
9276 /**
9277  * @class Roo.bootstrap.TableRow
9278  * @extends Roo.bootstrap.Component
9279  * Bootstrap TableRow class
9280  * @cfg {String} cls row class
9281  * @cfg {String} align Aligns the content in a table row
9282  * @cfg {String} bgcolor Specifies a background color for a table row
9283  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9284  * @cfg {String} valign Vertical aligns the content in a table row
9285  * 
9286  * @constructor
9287  * Create a new TableRow
9288  * @param {Object} config The config object
9289  */
9290
9291 Roo.bootstrap.TableRow = function(config){
9292     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9293 };
9294
9295 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9296     
9297     cls: false,
9298     align: false,
9299     bgcolor: false,
9300     charoff: false,
9301     valign: false,
9302     
9303     getAutoCreate : function(){
9304         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9305         
9306         cfg = {
9307             tag: 'tr'
9308         };
9309             
9310         if(this.cls){
9311             cfg.cls = this.cls;
9312         }
9313         if(this.align){
9314             cfg.align = this.align;
9315         }
9316         if(this.bgcolor){
9317             cfg.bgcolor = this.bgcolor;
9318         }
9319         if(this.charoff){
9320             cfg.charoff = this.charoff;
9321         }
9322         if(this.valign){
9323             cfg.valign = this.valign;
9324         }
9325         
9326         return cfg;
9327     }
9328    
9329 });
9330
9331  
9332
9333  /*
9334  * - LGPL
9335  *
9336  * table body
9337  * 
9338  */
9339
9340 /**
9341  * @class Roo.bootstrap.TableBody
9342  * @extends Roo.bootstrap.Component
9343  * Bootstrap TableBody class
9344  * @cfg {String} cls element class
9345  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9346  * @cfg {String} align Aligns the content inside the element
9347  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9348  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9349  * 
9350  * @constructor
9351  * Create a new TableBody
9352  * @param {Object} config The config object
9353  */
9354
9355 Roo.bootstrap.TableBody = function(config){
9356     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9357 };
9358
9359 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9360     
9361     cls: false,
9362     tag: false,
9363     align: false,
9364     charoff: false,
9365     valign: false,
9366     
9367     getAutoCreate : function(){
9368         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9369         
9370         cfg = {
9371             tag: 'tbody'
9372         };
9373             
9374         if (this.cls) {
9375             cfg.cls=this.cls
9376         }
9377         if(this.tag){
9378             cfg.tag = this.tag;
9379         }
9380         
9381         if(this.align){
9382             cfg.align = this.align;
9383         }
9384         if(this.charoff){
9385             cfg.charoff = this.charoff;
9386         }
9387         if(this.valign){
9388             cfg.valign = this.valign;
9389         }
9390         
9391         return cfg;
9392     }
9393     
9394     
9395 //    initEvents : function()
9396 //    {
9397 //        
9398 //        if(!this.store){
9399 //            return;
9400 //        }
9401 //        
9402 //        this.store = Roo.factory(this.store, Roo.data);
9403 //        this.store.on('load', this.onLoad, this);
9404 //        
9405 //        this.store.load();
9406 //        
9407 //    },
9408 //    
9409 //    onLoad: function () 
9410 //    {   
9411 //        this.fireEvent('load', this);
9412 //    }
9413 //    
9414 //   
9415 });
9416
9417  
9418
9419  /*
9420  * Based on:
9421  * Ext JS Library 1.1.1
9422  * Copyright(c) 2006-2007, Ext JS, LLC.
9423  *
9424  * Originally Released Under LGPL - original licence link has changed is not relivant.
9425  *
9426  * Fork - LGPL
9427  * <script type="text/javascript">
9428  */
9429
9430 // as we use this in bootstrap.
9431 Roo.namespace('Roo.form');
9432  /**
9433  * @class Roo.form.Action
9434  * Internal Class used to handle form actions
9435  * @constructor
9436  * @param {Roo.form.BasicForm} el The form element or its id
9437  * @param {Object} config Configuration options
9438  */
9439
9440  
9441  
9442 // define the action interface
9443 Roo.form.Action = function(form, options){
9444     this.form = form;
9445     this.options = options || {};
9446 };
9447 /**
9448  * Client Validation Failed
9449  * @const 
9450  */
9451 Roo.form.Action.CLIENT_INVALID = 'client';
9452 /**
9453  * Server Validation Failed
9454  * @const 
9455  */
9456 Roo.form.Action.SERVER_INVALID = 'server';
9457  /**
9458  * Connect to Server Failed
9459  * @const 
9460  */
9461 Roo.form.Action.CONNECT_FAILURE = 'connect';
9462 /**
9463  * Reading Data from Server Failed
9464  * @const 
9465  */
9466 Roo.form.Action.LOAD_FAILURE = 'load';
9467
9468 Roo.form.Action.prototype = {
9469     type : 'default',
9470     failureType : undefined,
9471     response : undefined,
9472     result : undefined,
9473
9474     // interface method
9475     run : function(options){
9476
9477     },
9478
9479     // interface method
9480     success : function(response){
9481
9482     },
9483
9484     // interface method
9485     handleResponse : function(response){
9486
9487     },
9488
9489     // default connection failure
9490     failure : function(response){
9491         
9492         this.response = response;
9493         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9494         this.form.afterAction(this, false);
9495     },
9496
9497     processResponse : function(response){
9498         this.response = response;
9499         if(!response.responseText){
9500             return true;
9501         }
9502         this.result = this.handleResponse(response);
9503         return this.result;
9504     },
9505
9506     // utility functions used internally
9507     getUrl : function(appendParams){
9508         var url = this.options.url || this.form.url || this.form.el.dom.action;
9509         if(appendParams){
9510             var p = this.getParams();
9511             if(p){
9512                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9513             }
9514         }
9515         return url;
9516     },
9517
9518     getMethod : function(){
9519         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9520     },
9521
9522     getParams : function(){
9523         var bp = this.form.baseParams;
9524         var p = this.options.params;
9525         if(p){
9526             if(typeof p == "object"){
9527                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9528             }else if(typeof p == 'string' && bp){
9529                 p += '&' + Roo.urlEncode(bp);
9530             }
9531         }else if(bp){
9532             p = Roo.urlEncode(bp);
9533         }
9534         return p;
9535     },
9536
9537     createCallback : function(){
9538         return {
9539             success: this.success,
9540             failure: this.failure,
9541             scope: this,
9542             timeout: (this.form.timeout*1000),
9543             upload: this.form.fileUpload ? this.success : undefined
9544         };
9545     }
9546 };
9547
9548 Roo.form.Action.Submit = function(form, options){
9549     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9550 };
9551
9552 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9553     type : 'submit',
9554
9555     haveProgress : false,
9556     uploadComplete : false,
9557     
9558     // uploadProgress indicator.
9559     uploadProgress : function()
9560     {
9561         if (!this.form.progressUrl) {
9562             return;
9563         }
9564         
9565         if (!this.haveProgress) {
9566             Roo.MessageBox.progress("Uploading", "Uploading");
9567         }
9568         if (this.uploadComplete) {
9569            Roo.MessageBox.hide();
9570            return;
9571         }
9572         
9573         this.haveProgress = true;
9574    
9575         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9576         
9577         var c = new Roo.data.Connection();
9578         c.request({
9579             url : this.form.progressUrl,
9580             params: {
9581                 id : uid
9582             },
9583             method: 'GET',
9584             success : function(req){
9585                //console.log(data);
9586                 var rdata = false;
9587                 var edata;
9588                 try  {
9589                    rdata = Roo.decode(req.responseText)
9590                 } catch (e) {
9591                     Roo.log("Invalid data from server..");
9592                     Roo.log(edata);
9593                     return;
9594                 }
9595                 if (!rdata || !rdata.success) {
9596                     Roo.log(rdata);
9597                     Roo.MessageBox.alert(Roo.encode(rdata));
9598                     return;
9599                 }
9600                 var data = rdata.data;
9601                 
9602                 if (this.uploadComplete) {
9603                    Roo.MessageBox.hide();
9604                    return;
9605                 }
9606                    
9607                 if (data){
9608                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9609                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9610                     );
9611                 }
9612                 this.uploadProgress.defer(2000,this);
9613             },
9614        
9615             failure: function(data) {
9616                 Roo.log('progress url failed ');
9617                 Roo.log(data);
9618             },
9619             scope : this
9620         });
9621            
9622     },
9623     
9624     
9625     run : function()
9626     {
9627         // run get Values on the form, so it syncs any secondary forms.
9628         this.form.getValues();
9629         
9630         var o = this.options;
9631         var method = this.getMethod();
9632         var isPost = method == 'POST';
9633         if(o.clientValidation === false || this.form.isValid()){
9634             
9635             if (this.form.progressUrl) {
9636                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9637                     (new Date() * 1) + '' + Math.random());
9638                     
9639             } 
9640             
9641             
9642             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9643                 form:this.form.el.dom,
9644                 url:this.getUrl(!isPost),
9645                 method: method,
9646                 params:isPost ? this.getParams() : null,
9647                 isUpload: this.form.fileUpload,
9648                 formData : this.form.formData
9649             }));
9650             
9651             this.uploadProgress();
9652
9653         }else if (o.clientValidation !== false){ // client validation failed
9654             this.failureType = Roo.form.Action.CLIENT_INVALID;
9655             this.form.afterAction(this, false);
9656         }
9657     },
9658
9659     success : function(response)
9660     {
9661         this.uploadComplete= true;
9662         if (this.haveProgress) {
9663             Roo.MessageBox.hide();
9664         }
9665         
9666         
9667         var result = this.processResponse(response);
9668         if(result === true || result.success){
9669             this.form.afterAction(this, true);
9670             return;
9671         }
9672         if(result.errors){
9673             this.form.markInvalid(result.errors);
9674             this.failureType = Roo.form.Action.SERVER_INVALID;
9675         }
9676         this.form.afterAction(this, false);
9677     },
9678     failure : function(response)
9679     {
9680         this.uploadComplete= true;
9681         if (this.haveProgress) {
9682             Roo.MessageBox.hide();
9683         }
9684         
9685         this.response = response;
9686         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9687         this.form.afterAction(this, false);
9688     },
9689     
9690     handleResponse : function(response){
9691         if(this.form.errorReader){
9692             var rs = this.form.errorReader.read(response);
9693             var errors = [];
9694             if(rs.records){
9695                 for(var i = 0, len = rs.records.length; i < len; i++) {
9696                     var r = rs.records[i];
9697                     errors[i] = r.data;
9698                 }
9699             }
9700             if(errors.length < 1){
9701                 errors = null;
9702             }
9703             return {
9704                 success : rs.success,
9705                 errors : errors
9706             };
9707         }
9708         var ret = false;
9709         try {
9710             ret = Roo.decode(response.responseText);
9711         } catch (e) {
9712             ret = {
9713                 success: false,
9714                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9715                 errors : []
9716             };
9717         }
9718         return ret;
9719         
9720     }
9721 });
9722
9723
9724 Roo.form.Action.Load = function(form, options){
9725     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9726     this.reader = this.form.reader;
9727 };
9728
9729 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9730     type : 'load',
9731
9732     run : function(){
9733         
9734         Roo.Ajax.request(Roo.apply(
9735                 this.createCallback(), {
9736                     method:this.getMethod(),
9737                     url:this.getUrl(false),
9738                     params:this.getParams()
9739         }));
9740     },
9741
9742     success : function(response){
9743         
9744         var result = this.processResponse(response);
9745         if(result === true || !result.success || !result.data){
9746             this.failureType = Roo.form.Action.LOAD_FAILURE;
9747             this.form.afterAction(this, false);
9748             return;
9749         }
9750         this.form.clearInvalid();
9751         this.form.setValues(result.data);
9752         this.form.afterAction(this, true);
9753     },
9754
9755     handleResponse : function(response){
9756         if(this.form.reader){
9757             var rs = this.form.reader.read(response);
9758             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9759             return {
9760                 success : rs.success,
9761                 data : data
9762             };
9763         }
9764         return Roo.decode(response.responseText);
9765     }
9766 });
9767
9768 Roo.form.Action.ACTION_TYPES = {
9769     'load' : Roo.form.Action.Load,
9770     'submit' : Roo.form.Action.Submit
9771 };/*
9772  * - LGPL
9773  *
9774  * form
9775  *
9776  */
9777
9778 /**
9779  * @class Roo.bootstrap.Form
9780  * @extends Roo.bootstrap.Component
9781  * Bootstrap Form class
9782  * @cfg {String} method  GET | POST (default POST)
9783  * @cfg {String} labelAlign top | left (default top)
9784  * @cfg {String} align left  | right - for navbars
9785  * @cfg {Boolean} loadMask load mask when submit (default true)
9786
9787  *
9788  * @constructor
9789  * Create a new Form
9790  * @param {Object} config The config object
9791  */
9792
9793
9794 Roo.bootstrap.Form = function(config){
9795     
9796     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9797     
9798     Roo.bootstrap.Form.popover.apply();
9799     
9800     this.addEvents({
9801         /**
9802          * @event clientvalidation
9803          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9804          * @param {Form} this
9805          * @param {Boolean} valid true if the form has passed client-side validation
9806          */
9807         clientvalidation: true,
9808         /**
9809          * @event beforeaction
9810          * Fires before any action is performed. Return false to cancel the action.
9811          * @param {Form} this
9812          * @param {Action} action The action to be performed
9813          */
9814         beforeaction: true,
9815         /**
9816          * @event actionfailed
9817          * Fires when an action fails.
9818          * @param {Form} this
9819          * @param {Action} action The action that failed
9820          */
9821         actionfailed : true,
9822         /**
9823          * @event actioncomplete
9824          * Fires when an action is completed.
9825          * @param {Form} this
9826          * @param {Action} action The action that completed
9827          */
9828         actioncomplete : true
9829     });
9830 };
9831
9832 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9833
9834      /**
9835      * @cfg {String} method
9836      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9837      */
9838     method : 'POST',
9839     /**
9840      * @cfg {String} url
9841      * The URL to use for form actions if one isn't supplied in the action options.
9842      */
9843     /**
9844      * @cfg {Boolean} fileUpload
9845      * Set to true if this form is a file upload.
9846      */
9847
9848     /**
9849      * @cfg {Object} baseParams
9850      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9851      */
9852
9853     /**
9854      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9855      */
9856     timeout: 30,
9857     /**
9858      * @cfg {Sting} align (left|right) for navbar forms
9859      */
9860     align : 'left',
9861
9862     // private
9863     activeAction : null,
9864
9865     /**
9866      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9867      * element by passing it or its id or mask the form itself by passing in true.
9868      * @type Mixed
9869      */
9870     waitMsgTarget : false,
9871
9872     loadMask : true,
9873     
9874     /**
9875      * @cfg {Boolean} errorMask (true|false) default false
9876      */
9877     errorMask : false,
9878     
9879     /**
9880      * @cfg {Number} maskOffset Default 100
9881      */
9882     maskOffset : 100,
9883     
9884     /**
9885      * @cfg {Boolean} maskBody
9886      */
9887     maskBody : false,
9888
9889     getAutoCreate : function(){
9890
9891         var cfg = {
9892             tag: 'form',
9893             method : this.method || 'POST',
9894             id : this.id || Roo.id(),
9895             cls : ''
9896         };
9897         if (this.parent().xtype.match(/^Nav/)) {
9898             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9899
9900         }
9901
9902         if (this.labelAlign == 'left' ) {
9903             cfg.cls += ' form-horizontal';
9904         }
9905
9906
9907         return cfg;
9908     },
9909     initEvents : function()
9910     {
9911         this.el.on('submit', this.onSubmit, this);
9912         // this was added as random key presses on the form where triggering form submit.
9913         this.el.on('keypress', function(e) {
9914             if (e.getCharCode() != 13) {
9915                 return true;
9916             }
9917             // we might need to allow it for textareas.. and some other items.
9918             // check e.getTarget().
9919
9920             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9921                 return true;
9922             }
9923
9924             Roo.log("keypress blocked");
9925
9926             e.preventDefault();
9927             return false;
9928         });
9929         
9930     },
9931     // private
9932     onSubmit : function(e){
9933         e.stopEvent();
9934     },
9935
9936      /**
9937      * Returns true if client-side validation on the form is successful.
9938      * @return Boolean
9939      */
9940     isValid : function(){
9941         var items = this.getItems();
9942         var valid = true;
9943         var target = false;
9944         
9945         items.each(function(f){
9946             
9947             if(f.validate()){
9948                 return;
9949             }
9950             
9951             Roo.log('invalid field: ' + f.name);
9952             
9953             valid = false;
9954
9955             if(!target && f.el.isVisible(true)){
9956                 target = f;
9957             }
9958            
9959         });
9960         
9961         if(this.errorMask && !valid){
9962             Roo.bootstrap.Form.popover.mask(this, target);
9963         }
9964         
9965         return valid;
9966     },
9967     
9968     /**
9969      * Returns true if any fields in this form have changed since their original load.
9970      * @return Boolean
9971      */
9972     isDirty : function(){
9973         var dirty = false;
9974         var items = this.getItems();
9975         items.each(function(f){
9976            if(f.isDirty()){
9977                dirty = true;
9978                return false;
9979            }
9980            return true;
9981         });
9982         return dirty;
9983     },
9984      /**
9985      * Performs a predefined action (submit or load) or custom actions you define on this form.
9986      * @param {String} actionName The name of the action type
9987      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9988      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9989      * accept other config options):
9990      * <pre>
9991 Property          Type             Description
9992 ----------------  ---------------  ----------------------------------------------------------------------------------
9993 url               String           The url for the action (defaults to the form's url)
9994 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9995 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9996 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9997                                    validate the form on the client (defaults to false)
9998      * </pre>
9999      * @return {BasicForm} this
10000      */
10001     doAction : function(action, options){
10002         if(typeof action == 'string'){
10003             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10004         }
10005         if(this.fireEvent('beforeaction', this, action) !== false){
10006             this.beforeAction(action);
10007             action.run.defer(100, action);
10008         }
10009         return this;
10010     },
10011
10012     // private
10013     beforeAction : function(action){
10014         var o = action.options;
10015         
10016         if(this.loadMask){
10017             
10018             if(this.maskBody){
10019                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10020             } else {
10021                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10022             }
10023         }
10024         // not really supported yet.. ??
10025
10026         //if(this.waitMsgTarget === true){
10027         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10028         //}else if(this.waitMsgTarget){
10029         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10030         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10031         //}else {
10032         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10033        // }
10034
10035     },
10036
10037     // private
10038     afterAction : function(action, success){
10039         this.activeAction = null;
10040         var o = action.options;
10041
10042         if(this.loadMask){
10043             
10044             if(this.maskBody){
10045                 Roo.get(document.body).unmask();
10046             } else {
10047                 this.el.unmask();
10048             }
10049         }
10050         
10051         //if(this.waitMsgTarget === true){
10052 //            this.el.unmask();
10053         //}else if(this.waitMsgTarget){
10054         //    this.waitMsgTarget.unmask();
10055         //}else{
10056         //    Roo.MessageBox.updateProgress(1);
10057         //    Roo.MessageBox.hide();
10058        // }
10059         //
10060         if(success){
10061             if(o.reset){
10062                 this.reset();
10063             }
10064             Roo.callback(o.success, o.scope, [this, action]);
10065             this.fireEvent('actioncomplete', this, action);
10066
10067         }else{
10068
10069             // failure condition..
10070             // we have a scenario where updates need confirming.
10071             // eg. if a locking scenario exists..
10072             // we look for { errors : { needs_confirm : true }} in the response.
10073             if (
10074                 (typeof(action.result) != 'undefined')  &&
10075                 (typeof(action.result.errors) != 'undefined')  &&
10076                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10077            ){
10078                 var _t = this;
10079                 Roo.log("not supported yet");
10080                  /*
10081
10082                 Roo.MessageBox.confirm(
10083                     "Change requires confirmation",
10084                     action.result.errorMsg,
10085                     function(r) {
10086                         if (r != 'yes') {
10087                             return;
10088                         }
10089                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10090                     }
10091
10092                 );
10093                 */
10094
10095
10096                 return;
10097             }
10098
10099             Roo.callback(o.failure, o.scope, [this, action]);
10100             // show an error message if no failed handler is set..
10101             if (!this.hasListener('actionfailed')) {
10102                 Roo.log("need to add dialog support");
10103                 /*
10104                 Roo.MessageBox.alert("Error",
10105                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10106                         action.result.errorMsg :
10107                         "Saving Failed, please check your entries or try again"
10108                 );
10109                 */
10110             }
10111
10112             this.fireEvent('actionfailed', this, action);
10113         }
10114
10115     },
10116     /**
10117      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10118      * @param {String} id The value to search for
10119      * @return Field
10120      */
10121     findField : function(id){
10122         var items = this.getItems();
10123         var field = items.get(id);
10124         if(!field){
10125              items.each(function(f){
10126                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10127                     field = f;
10128                     return false;
10129                 }
10130                 return true;
10131             });
10132         }
10133         return field || null;
10134     },
10135      /**
10136      * Mark fields in this form invalid in bulk.
10137      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10138      * @return {BasicForm} this
10139      */
10140     markInvalid : function(errors){
10141         if(errors instanceof Array){
10142             for(var i = 0, len = errors.length; i < len; i++){
10143                 var fieldError = errors[i];
10144                 var f = this.findField(fieldError.id);
10145                 if(f){
10146                     f.markInvalid(fieldError.msg);
10147                 }
10148             }
10149         }else{
10150             var field, id;
10151             for(id in errors){
10152                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10153                     field.markInvalid(errors[id]);
10154                 }
10155             }
10156         }
10157         //Roo.each(this.childForms || [], function (f) {
10158         //    f.markInvalid(errors);
10159         //});
10160
10161         return this;
10162     },
10163
10164     /**
10165      * Set values for fields in this form in bulk.
10166      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10167      * @return {BasicForm} this
10168      */
10169     setValues : function(values){
10170         if(values instanceof Array){ // array of objects
10171             for(var i = 0, len = values.length; i < len; i++){
10172                 var v = values[i];
10173                 var f = this.findField(v.id);
10174                 if(f){
10175                     f.setValue(v.value);
10176                     if(this.trackResetOnLoad){
10177                         f.originalValue = f.getValue();
10178                     }
10179                 }
10180             }
10181         }else{ // object hash
10182             var field, id;
10183             for(id in values){
10184                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10185
10186                     if (field.setFromData &&
10187                         field.valueField &&
10188                         field.displayField &&
10189                         // combos' with local stores can
10190                         // be queried via setValue()
10191                         // to set their value..
10192                         (field.store && !field.store.isLocal)
10193                         ) {
10194                         // it's a combo
10195                         var sd = { };
10196                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10197                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10198                         field.setFromData(sd);
10199
10200                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10201                         
10202                         field.setFromData(values);
10203                         
10204                     } else {
10205                         field.setValue(values[id]);
10206                     }
10207
10208
10209                     if(this.trackResetOnLoad){
10210                         field.originalValue = field.getValue();
10211                     }
10212                 }
10213             }
10214         }
10215
10216         //Roo.each(this.childForms || [], function (f) {
10217         //    f.setValues(values);
10218         //});
10219
10220         return this;
10221     },
10222
10223     /**
10224      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10225      * they are returned as an array.
10226      * @param {Boolean} asString
10227      * @return {Object}
10228      */
10229     getValues : function(asString){
10230         //if (this.childForms) {
10231             // copy values from the child forms
10232         //    Roo.each(this.childForms, function (f) {
10233         //        this.setValues(f.getValues());
10234         //    }, this);
10235         //}
10236
10237
10238
10239         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10240         if(asString === true){
10241             return fs;
10242         }
10243         return Roo.urlDecode(fs);
10244     },
10245
10246     /**
10247      * Returns the fields in this form as an object with key/value pairs.
10248      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10249      * @return {Object}
10250      */
10251     getFieldValues : function(with_hidden)
10252     {
10253         var items = this.getItems();
10254         var ret = {};
10255         items.each(function(f){
10256             
10257             if (!f.getName()) {
10258                 return;
10259             }
10260             
10261             var v = f.getValue();
10262             
10263             if (f.inputType =='radio') {
10264                 if (typeof(ret[f.getName()]) == 'undefined') {
10265                     ret[f.getName()] = ''; // empty..
10266                 }
10267
10268                 if (!f.el.dom.checked) {
10269                     return;
10270
10271                 }
10272                 v = f.el.dom.value;
10273
10274             }
10275             
10276             if(f.xtype == 'MoneyField'){
10277                 ret[f.currencyName] = f.getCurrency();
10278             }
10279
10280             // not sure if this supported any more..
10281             if ((typeof(v) == 'object') && f.getRawValue) {
10282                 v = f.getRawValue() ; // dates..
10283             }
10284             // combo boxes where name != hiddenName...
10285             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10286                 ret[f.name] = f.getRawValue();
10287             }
10288             ret[f.getName()] = v;
10289         });
10290
10291         return ret;
10292     },
10293
10294     /**
10295      * Clears all invalid messages in this form.
10296      * @return {BasicForm} this
10297      */
10298     clearInvalid : function(){
10299         var items = this.getItems();
10300
10301         items.each(function(f){
10302            f.clearInvalid();
10303         });
10304
10305         return this;
10306     },
10307
10308     /**
10309      * Resets this form.
10310      * @return {BasicForm} this
10311      */
10312     reset : function(){
10313         var items = this.getItems();
10314         items.each(function(f){
10315             f.reset();
10316         });
10317
10318         Roo.each(this.childForms || [], function (f) {
10319             f.reset();
10320         });
10321
10322
10323         return this;
10324     },
10325     
10326     getItems : function()
10327     {
10328         var r=new Roo.util.MixedCollection(false, function(o){
10329             return o.id || (o.id = Roo.id());
10330         });
10331         var iter = function(el) {
10332             if (el.inputEl) {
10333                 r.add(el);
10334             }
10335             if (!el.items) {
10336                 return;
10337             }
10338             Roo.each(el.items,function(e) {
10339                 iter(e);
10340             });
10341         };
10342
10343         iter(this);
10344         return r;
10345     },
10346     
10347     hideFields : function(items)
10348     {
10349         Roo.each(items, function(i){
10350             
10351             var f = this.findField(i);
10352             
10353             if(!f){
10354                 return;
10355             }
10356             
10357             f.hide();
10358             
10359         }, this);
10360     },
10361     
10362     showFields : function(items)
10363     {
10364         Roo.each(items, function(i){
10365             
10366             var f = this.findField(i);
10367             
10368             if(!f){
10369                 return;
10370             }
10371             
10372             f.show();
10373             
10374         }, this);
10375     }
10376
10377 });
10378
10379 Roo.apply(Roo.bootstrap.Form, {
10380     
10381     popover : {
10382         
10383         padding : 5,
10384         
10385         isApplied : false,
10386         
10387         isMasked : false,
10388         
10389         form : false,
10390         
10391         target : false,
10392         
10393         toolTip : false,
10394         
10395         intervalID : false,
10396         
10397         maskEl : false,
10398         
10399         apply : function()
10400         {
10401             if(this.isApplied){
10402                 return;
10403             }
10404             
10405             this.maskEl = {
10406                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10407                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10408                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10409                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10410             };
10411             
10412             this.maskEl.top.enableDisplayMode("block");
10413             this.maskEl.left.enableDisplayMode("block");
10414             this.maskEl.bottom.enableDisplayMode("block");
10415             this.maskEl.right.enableDisplayMode("block");
10416             
10417             this.toolTip = new Roo.bootstrap.Tooltip({
10418                 cls : 'roo-form-error-popover',
10419                 alignment : {
10420                     'left' : ['r-l', [-2,0], 'right'],
10421                     'right' : ['l-r', [2,0], 'left'],
10422                     'bottom' : ['tl-bl', [0,2], 'top'],
10423                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10424                 }
10425             });
10426             
10427             this.toolTip.render(Roo.get(document.body));
10428
10429             this.toolTip.el.enableDisplayMode("block");
10430             
10431             Roo.get(document.body).on('click', function(){
10432                 this.unmask();
10433             }, this);
10434             
10435             Roo.get(document.body).on('touchstart', function(){
10436                 this.unmask();
10437             }, this);
10438             
10439             this.isApplied = true
10440         },
10441         
10442         mask : function(form, target)
10443         {
10444             this.form = form;
10445             
10446             this.target = target;
10447             
10448             if(!this.form.errorMask || !target.el){
10449                 return;
10450             }
10451             
10452             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10453             
10454             Roo.log(scrollable);
10455             
10456             var ot = this.target.el.calcOffsetsTo(scrollable);
10457             
10458             var scrollTo = ot[1] - this.form.maskOffset;
10459             
10460             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10461             
10462             scrollable.scrollTo('top', scrollTo);
10463             
10464             var box = this.target.el.getBox();
10465             Roo.log(box);
10466             var zIndex = Roo.bootstrap.Modal.zIndex++;
10467
10468             
10469             this.maskEl.top.setStyle('position', 'absolute');
10470             this.maskEl.top.setStyle('z-index', zIndex);
10471             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10472             this.maskEl.top.setLeft(0);
10473             this.maskEl.top.setTop(0);
10474             this.maskEl.top.show();
10475             
10476             this.maskEl.left.setStyle('position', 'absolute');
10477             this.maskEl.left.setStyle('z-index', zIndex);
10478             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10479             this.maskEl.left.setLeft(0);
10480             this.maskEl.left.setTop(box.y - this.padding);
10481             this.maskEl.left.show();
10482
10483             this.maskEl.bottom.setStyle('position', 'absolute');
10484             this.maskEl.bottom.setStyle('z-index', zIndex);
10485             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10486             this.maskEl.bottom.setLeft(0);
10487             this.maskEl.bottom.setTop(box.bottom + this.padding);
10488             this.maskEl.bottom.show();
10489
10490             this.maskEl.right.setStyle('position', 'absolute');
10491             this.maskEl.right.setStyle('z-index', zIndex);
10492             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10493             this.maskEl.right.setLeft(box.right + this.padding);
10494             this.maskEl.right.setTop(box.y - this.padding);
10495             this.maskEl.right.show();
10496
10497             this.toolTip.bindEl = this.target.el;
10498
10499             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10500
10501             var tip = this.target.blankText;
10502
10503             if(this.target.getValue() !== '' ) {
10504                 
10505                 if (this.target.invalidText.length) {
10506                     tip = this.target.invalidText;
10507                 } else if (this.target.regexText.length){
10508                     tip = this.target.regexText;
10509                 }
10510             }
10511
10512             this.toolTip.show(tip);
10513
10514             this.intervalID = window.setInterval(function() {
10515                 Roo.bootstrap.Form.popover.unmask();
10516             }, 10000);
10517
10518             window.onwheel = function(){ return false;};
10519             
10520             (function(){ this.isMasked = true; }).defer(500, this);
10521             
10522         },
10523         
10524         unmask : function()
10525         {
10526             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10527                 return;
10528             }
10529             
10530             this.maskEl.top.setStyle('position', 'absolute');
10531             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10532             this.maskEl.top.hide();
10533
10534             this.maskEl.left.setStyle('position', 'absolute');
10535             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10536             this.maskEl.left.hide();
10537
10538             this.maskEl.bottom.setStyle('position', 'absolute');
10539             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10540             this.maskEl.bottom.hide();
10541
10542             this.maskEl.right.setStyle('position', 'absolute');
10543             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10544             this.maskEl.right.hide();
10545             
10546             this.toolTip.hide();
10547             
10548             this.toolTip.el.hide();
10549             
10550             window.onwheel = function(){ return true;};
10551             
10552             if(this.intervalID){
10553                 window.clearInterval(this.intervalID);
10554                 this.intervalID = false;
10555             }
10556             
10557             this.isMasked = false;
10558             
10559         }
10560         
10561     }
10562     
10563 });
10564
10565 /*
10566  * Based on:
10567  * Ext JS Library 1.1.1
10568  * Copyright(c) 2006-2007, Ext JS, LLC.
10569  *
10570  * Originally Released Under LGPL - original licence link has changed is not relivant.
10571  *
10572  * Fork - LGPL
10573  * <script type="text/javascript">
10574  */
10575 /**
10576  * @class Roo.form.VTypes
10577  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10578  * @singleton
10579  */
10580 Roo.form.VTypes = function(){
10581     // closure these in so they are only created once.
10582     var alpha = /^[a-zA-Z_]+$/;
10583     var alphanum = /^[a-zA-Z0-9_]+$/;
10584     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10585     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10586
10587     // All these messages and functions are configurable
10588     return {
10589         /**
10590          * The function used to validate email addresses
10591          * @param {String} value The email address
10592          */
10593         'email' : function(v){
10594             return email.test(v);
10595         },
10596         /**
10597          * The error text to display when the email validation function returns false
10598          * @type String
10599          */
10600         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10601         /**
10602          * The keystroke filter mask to be applied on email input
10603          * @type RegExp
10604          */
10605         'emailMask' : /[a-z0-9_\.\-@]/i,
10606
10607         /**
10608          * The function used to validate URLs
10609          * @param {String} value The URL
10610          */
10611         'url' : function(v){
10612             return url.test(v);
10613         },
10614         /**
10615          * The error text to display when the url validation function returns false
10616          * @type String
10617          */
10618         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10619         
10620         /**
10621          * The function used to validate alpha values
10622          * @param {String} value The value
10623          */
10624         'alpha' : function(v){
10625             return alpha.test(v);
10626         },
10627         /**
10628          * The error text to display when the alpha validation function returns false
10629          * @type String
10630          */
10631         'alphaText' : 'This field should only contain letters and _',
10632         /**
10633          * The keystroke filter mask to be applied on alpha input
10634          * @type RegExp
10635          */
10636         'alphaMask' : /[a-z_]/i,
10637
10638         /**
10639          * The function used to validate alphanumeric values
10640          * @param {String} value The value
10641          */
10642         'alphanum' : function(v){
10643             return alphanum.test(v);
10644         },
10645         /**
10646          * The error text to display when the alphanumeric validation function returns false
10647          * @type String
10648          */
10649         'alphanumText' : 'This field should only contain letters, numbers and _',
10650         /**
10651          * The keystroke filter mask to be applied on alphanumeric input
10652          * @type RegExp
10653          */
10654         'alphanumMask' : /[a-z0-9_]/i
10655     };
10656 }();/*
10657  * - LGPL
10658  *
10659  * Input
10660  * 
10661  */
10662
10663 /**
10664  * @class Roo.bootstrap.Input
10665  * @extends Roo.bootstrap.Component
10666  * Bootstrap Input class
10667  * @cfg {Boolean} disabled is it disabled
10668  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10669  * @cfg {String} name name of the input
10670  * @cfg {string} fieldLabel - the label associated
10671  * @cfg {string} placeholder - placeholder to put in text.
10672  * @cfg {string}  before - input group add on before
10673  * @cfg {string} after - input group add on after
10674  * @cfg {string} size - (lg|sm) or leave empty..
10675  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10676  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10677  * @cfg {Number} md colspan out of 12 for computer-sized screens
10678  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10679  * @cfg {string} value default value of the input
10680  * @cfg {Number} labelWidth set the width of label 
10681  * @cfg {Number} labellg set the width of label (1-12)
10682  * @cfg {Number} labelmd set the width of label (1-12)
10683  * @cfg {Number} labelsm set the width of label (1-12)
10684  * @cfg {Number} labelxs set the width of label (1-12)
10685  * @cfg {String} labelAlign (top|left)
10686  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10687  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10688  * @cfg {String} indicatorpos (left|right) default left
10689  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10690  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10691  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10692
10693  * @cfg {String} align (left|center|right) Default left
10694  * @cfg {Boolean} forceFeedback (true|false) Default false
10695  * 
10696  * @constructor
10697  * Create a new Input
10698  * @param {Object} config The config object
10699  */
10700
10701 Roo.bootstrap.Input = function(config){
10702     
10703     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10704     
10705     this.addEvents({
10706         /**
10707          * @event focus
10708          * Fires when this field receives input focus.
10709          * @param {Roo.form.Field} this
10710          */
10711         focus : true,
10712         /**
10713          * @event blur
10714          * Fires when this field loses input focus.
10715          * @param {Roo.form.Field} this
10716          */
10717         blur : true,
10718         /**
10719          * @event specialkey
10720          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10721          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10722          * @param {Roo.form.Field} this
10723          * @param {Roo.EventObject} e The event object
10724          */
10725         specialkey : true,
10726         /**
10727          * @event change
10728          * Fires just before the field blurs if the field value has changed.
10729          * @param {Roo.form.Field} this
10730          * @param {Mixed} newValue The new value
10731          * @param {Mixed} oldValue The original value
10732          */
10733         change : true,
10734         /**
10735          * @event invalid
10736          * Fires after the field has been marked as invalid.
10737          * @param {Roo.form.Field} this
10738          * @param {String} msg The validation message
10739          */
10740         invalid : true,
10741         /**
10742          * @event valid
10743          * Fires after the field has been validated with no errors.
10744          * @param {Roo.form.Field} this
10745          */
10746         valid : true,
10747          /**
10748          * @event keyup
10749          * Fires after the key up
10750          * @param {Roo.form.Field} this
10751          * @param {Roo.EventObject}  e The event Object
10752          */
10753         keyup : true,
10754         /**
10755          * @event paste
10756          * Fires after the user pastes into input
10757          * @param {Roo.form.Field} this
10758          * @param {Roo.EventObject}  e The event Object
10759          */
10760         paste : true
10761     });
10762 };
10763
10764 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10765      /**
10766      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10767       automatic validation (defaults to "keyup").
10768      */
10769     validationEvent : "keyup",
10770      /**
10771      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10772      */
10773     validateOnBlur : true,
10774     /**
10775      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10776      */
10777     validationDelay : 250,
10778      /**
10779      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10780      */
10781     focusClass : "x-form-focus",  // not needed???
10782     
10783        
10784     /**
10785      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10786      */
10787     invalidClass : "has-warning",
10788     
10789     /**
10790      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10791      */
10792     validClass : "has-success",
10793     
10794     /**
10795      * @cfg {Boolean} hasFeedback (true|false) default true
10796      */
10797     hasFeedback : true,
10798     
10799     /**
10800      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10801      */
10802     invalidFeedbackClass : "glyphicon-warning-sign",
10803     
10804     /**
10805      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10806      */
10807     validFeedbackClass : "glyphicon-ok",
10808     
10809     /**
10810      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10811      */
10812     selectOnFocus : false,
10813     
10814      /**
10815      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10816      */
10817     maskRe : null,
10818        /**
10819      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10820      */
10821     vtype : null,
10822     
10823       /**
10824      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10825      */
10826     disableKeyFilter : false,
10827     
10828        /**
10829      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10830      */
10831     disabled : false,
10832      /**
10833      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10834      */
10835     allowBlank : true,
10836     /**
10837      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10838      */
10839     blankText : "Please complete this mandatory field",
10840     
10841      /**
10842      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10843      */
10844     minLength : 0,
10845     /**
10846      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10847      */
10848     maxLength : Number.MAX_VALUE,
10849     /**
10850      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10851      */
10852     minLengthText : "The minimum length for this field is {0}",
10853     /**
10854      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10855      */
10856     maxLengthText : "The maximum length for this field is {0}",
10857   
10858     
10859     /**
10860      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10861      * If available, this function will be called only after the basic validators all return true, and will be passed the
10862      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10863      */
10864     validator : null,
10865     /**
10866      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10867      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10868      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10869      */
10870     regex : null,
10871     /**
10872      * @cfg {String} regexText -- Depricated - use Invalid Text
10873      */
10874     regexText : "",
10875     
10876     /**
10877      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10878      */
10879     invalidText : "",
10880     
10881     
10882     
10883     autocomplete: false,
10884     
10885     
10886     fieldLabel : '',
10887     inputType : 'text',
10888     
10889     name : false,
10890     placeholder: false,
10891     before : false,
10892     after : false,
10893     size : false,
10894     hasFocus : false,
10895     preventMark: false,
10896     isFormField : true,
10897     value : '',
10898     labelWidth : 2,
10899     labelAlign : false,
10900     readOnly : false,
10901     align : false,
10902     formatedValue : false,
10903     forceFeedback : false,
10904     
10905     indicatorpos : 'left',
10906     
10907     labellg : 0,
10908     labelmd : 0,
10909     labelsm : 0,
10910     labelxs : 0,
10911     
10912     capture : '',
10913     accept : '',
10914     
10915     parentLabelAlign : function()
10916     {
10917         var parent = this;
10918         while (parent.parent()) {
10919             parent = parent.parent();
10920             if (typeof(parent.labelAlign) !='undefined') {
10921                 return parent.labelAlign;
10922             }
10923         }
10924         return 'left';
10925         
10926     },
10927     
10928     getAutoCreate : function()
10929     {
10930         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10931         
10932         var id = Roo.id();
10933         
10934         var cfg = {};
10935         
10936         if(this.inputType != 'hidden'){
10937             cfg.cls = 'form-group' //input-group
10938         }
10939         
10940         var input =  {
10941             tag: 'input',
10942             id : id,
10943             type : this.inputType,
10944             value : this.value,
10945             cls : 'form-control',
10946             placeholder : this.placeholder || '',
10947             autocomplete : this.autocomplete || 'new-password'
10948         };
10949         if (this.inputType == 'file') {
10950             input.style = 'overflow:hidden'; // why not in CSS?
10951         }
10952         
10953         if(this.capture.length){
10954             input.capture = this.capture;
10955         }
10956         
10957         if(this.accept.length){
10958             input.accept = this.accept + "/*";
10959         }
10960         
10961         if(this.align){
10962             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10963         }
10964         
10965         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10966             input.maxLength = this.maxLength;
10967         }
10968         
10969         if (this.disabled) {
10970             input.disabled=true;
10971         }
10972         
10973         if (this.readOnly) {
10974             input.readonly=true;
10975         }
10976         
10977         if (this.name) {
10978             input.name = this.name;
10979         }
10980         
10981         if (this.size) {
10982             input.cls += ' input-' + this.size;
10983         }
10984         
10985         var settings=this;
10986         ['xs','sm','md','lg'].map(function(size){
10987             if (settings[size]) {
10988                 cfg.cls += ' col-' + size + '-' + settings[size];
10989             }
10990         });
10991         
10992         var inputblock = input;
10993         
10994         var feedback = {
10995             tag: 'span',
10996             cls: 'glyphicon form-control-feedback'
10997         };
10998             
10999         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11000             
11001             inputblock = {
11002                 cls : 'has-feedback',
11003                 cn :  [
11004                     input,
11005                     feedback
11006                 ] 
11007             };  
11008         }
11009         
11010         if (this.before || this.after) {
11011             
11012             inputblock = {
11013                 cls : 'input-group',
11014                 cn :  [] 
11015             };
11016             
11017             if (this.before && typeof(this.before) == 'string') {
11018                 
11019                 inputblock.cn.push({
11020                     tag :'span',
11021                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11022                     html : this.before
11023                 });
11024             }
11025             if (this.before && typeof(this.before) == 'object') {
11026                 this.before = Roo.factory(this.before);
11027                 
11028                 inputblock.cn.push({
11029                     tag :'span',
11030                     cls : 'roo-input-before input-group-prepend   input-group-' +
11031                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11032                 });
11033             }
11034             
11035             inputblock.cn.push(input);
11036             
11037             if (this.after && typeof(this.after) == 'string') {
11038                 inputblock.cn.push({
11039                     tag :'span',
11040                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11041                     html : this.after
11042                 });
11043             }
11044             if (this.after && typeof(this.after) == 'object') {
11045                 this.after = Roo.factory(this.after);
11046                 
11047                 inputblock.cn.push({
11048                     tag :'span',
11049                     cls : 'roo-input-after input-group-append  input-group-' +
11050                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11051                 });
11052             }
11053             
11054             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11055                 inputblock.cls += ' has-feedback';
11056                 inputblock.cn.push(feedback);
11057             }
11058         };
11059         var indicator = {
11060             tag : 'i',
11061             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11062             tooltip : 'This field is required'
11063         };
11064         if (this.allowBlank ) {
11065             indicator.style = this.allowBlank ? ' display:none' : '';
11066         }
11067         if (align ==='left' && this.fieldLabel.length) {
11068             
11069             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11070             
11071             cfg.cn = [
11072                 indicator,
11073                 {
11074                     tag: 'label',
11075                     'for' :  id,
11076                     cls : 'control-label col-form-label',
11077                     html : this.fieldLabel
11078
11079                 },
11080                 {
11081                     cls : "", 
11082                     cn: [
11083                         inputblock
11084                     ]
11085                 }
11086             ];
11087             
11088             var labelCfg = cfg.cn[1];
11089             var contentCfg = cfg.cn[2];
11090             
11091             if(this.indicatorpos == 'right'){
11092                 cfg.cn = [
11093                     {
11094                         tag: 'label',
11095                         'for' :  id,
11096                         cls : 'control-label col-form-label',
11097                         cn : [
11098                             {
11099                                 tag : 'span',
11100                                 html : this.fieldLabel
11101                             },
11102                             indicator
11103                         ]
11104                     },
11105                     {
11106                         cls : "",
11107                         cn: [
11108                             inputblock
11109                         ]
11110                     }
11111
11112                 ];
11113                 
11114                 labelCfg = cfg.cn[0];
11115                 contentCfg = cfg.cn[1];
11116             
11117             }
11118             
11119             if(this.labelWidth > 12){
11120                 labelCfg.style = "width: " + this.labelWidth + 'px';
11121             }
11122             
11123             if(this.labelWidth < 13 && this.labelmd == 0){
11124                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11125             }
11126             
11127             if(this.labellg > 0){
11128                 labelCfg.cls += ' col-lg-' + this.labellg;
11129                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11130             }
11131             
11132             if(this.labelmd > 0){
11133                 labelCfg.cls += ' col-md-' + this.labelmd;
11134                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11135             }
11136             
11137             if(this.labelsm > 0){
11138                 labelCfg.cls += ' col-sm-' + this.labelsm;
11139                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11140             }
11141             
11142             if(this.labelxs > 0){
11143                 labelCfg.cls += ' col-xs-' + this.labelxs;
11144                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11145             }
11146             
11147             
11148         } else if ( this.fieldLabel.length) {
11149                 
11150             
11151             
11152             cfg.cn = [
11153                 {
11154                     tag : 'i',
11155                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11156                     tooltip : 'This field is required',
11157                     style : this.allowBlank ? ' display:none' : '' 
11158                 },
11159                 {
11160                     tag: 'label',
11161                    //cls : 'input-group-addon',
11162                     html : this.fieldLabel
11163
11164                 },
11165
11166                inputblock
11167
11168            ];
11169            
11170            if(this.indicatorpos == 'right'){
11171        
11172                 cfg.cn = [
11173                     {
11174                         tag: 'label',
11175                        //cls : 'input-group-addon',
11176                         html : this.fieldLabel
11177
11178                     },
11179                     {
11180                         tag : 'i',
11181                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11182                         tooltip : 'This field is required',
11183                         style : this.allowBlank ? ' display:none' : '' 
11184                     },
11185
11186                    inputblock
11187
11188                ];
11189
11190             }
11191
11192         } else {
11193             
11194             cfg.cn = [
11195
11196                     inputblock
11197
11198             ];
11199                 
11200                 
11201         };
11202         
11203         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11204            cfg.cls += ' navbar-form';
11205         }
11206         
11207         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11208             // on BS4 we do this only if not form 
11209             cfg.cls += ' navbar-form';
11210             cfg.tag = 'li';
11211         }
11212         
11213         return cfg;
11214         
11215     },
11216     /**
11217      * return the real input element.
11218      */
11219     inputEl: function ()
11220     {
11221         return this.el.select('input.form-control',true).first();
11222     },
11223     
11224     tooltipEl : function()
11225     {
11226         return this.inputEl();
11227     },
11228     
11229     indicatorEl : function()
11230     {
11231         if (Roo.bootstrap.version == 4) {
11232             return false; // not enabled in v4 yet.
11233         }
11234         
11235         var indicator = this.el.select('i.roo-required-indicator',true).first();
11236         
11237         if(!indicator){
11238             return false;
11239         }
11240         
11241         return indicator;
11242         
11243     },
11244     
11245     setDisabled : function(v)
11246     {
11247         var i  = this.inputEl().dom;
11248         if (!v) {
11249             i.removeAttribute('disabled');
11250             return;
11251             
11252         }
11253         i.setAttribute('disabled','true');
11254     },
11255     initEvents : function()
11256     {
11257           
11258         this.inputEl().on("keydown" , this.fireKey,  this);
11259         this.inputEl().on("focus", this.onFocus,  this);
11260         this.inputEl().on("blur", this.onBlur,  this);
11261         
11262         this.inputEl().relayEvent('keyup', this);
11263         this.inputEl().relayEvent('paste', this);
11264         
11265         this.indicator = this.indicatorEl();
11266         
11267         if(this.indicator){
11268             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11269         }
11270  
11271         // reference to original value for reset
11272         this.originalValue = this.getValue();
11273         //Roo.form.TextField.superclass.initEvents.call(this);
11274         if(this.validationEvent == 'keyup'){
11275             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11276             this.inputEl().on('keyup', this.filterValidation, this);
11277         }
11278         else if(this.validationEvent !== false){
11279             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11280         }
11281         
11282         if(this.selectOnFocus){
11283             this.on("focus", this.preFocus, this);
11284             
11285         }
11286         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11287             this.inputEl().on("keypress", this.filterKeys, this);
11288         } else {
11289             this.inputEl().relayEvent('keypress', this);
11290         }
11291        /* if(this.grow){
11292             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11293             this.el.on("click", this.autoSize,  this);
11294         }
11295         */
11296         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11297             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11298         }
11299         
11300         if (typeof(this.before) == 'object') {
11301             this.before.render(this.el.select('.roo-input-before',true).first());
11302         }
11303         if (typeof(this.after) == 'object') {
11304             this.after.render(this.el.select('.roo-input-after',true).first());
11305         }
11306         
11307         this.inputEl().on('change', this.onChange, this);
11308         
11309     },
11310     filterValidation : function(e){
11311         if(!e.isNavKeyPress()){
11312             this.validationTask.delay(this.validationDelay);
11313         }
11314     },
11315      /**
11316      * Validates the field value
11317      * @return {Boolean} True if the value is valid, else false
11318      */
11319     validate : function(){
11320         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11321         if(this.disabled || this.validateValue(this.getRawValue())){
11322             this.markValid();
11323             return true;
11324         }
11325         
11326         this.markInvalid();
11327         return false;
11328     },
11329     
11330     
11331     /**
11332      * Validates a value according to the field's validation rules and marks the field as invalid
11333      * if the validation fails
11334      * @param {Mixed} value The value to validate
11335      * @return {Boolean} True if the value is valid, else false
11336      */
11337     validateValue : function(value)
11338     {
11339         if(this.getVisibilityEl().hasClass('hidden')){
11340             return true;
11341         }
11342         
11343         if(value.length < 1)  { // if it's blank
11344             if(this.allowBlank){
11345                 return true;
11346             }
11347             return false;
11348         }
11349         
11350         if(value.length < this.minLength){
11351             return false;
11352         }
11353         if(value.length > this.maxLength){
11354             return false;
11355         }
11356         if(this.vtype){
11357             var vt = Roo.form.VTypes;
11358             if(!vt[this.vtype](value, this)){
11359                 return false;
11360             }
11361         }
11362         if(typeof this.validator == "function"){
11363             var msg = this.validator(value);
11364             if(msg !== true){
11365                 return false;
11366             }
11367             if (typeof(msg) == 'string') {
11368                 this.invalidText = msg;
11369             }
11370         }
11371         
11372         if(this.regex && !this.regex.test(value)){
11373             return false;
11374         }
11375         
11376         return true;
11377     },
11378     
11379      // private
11380     fireKey : function(e){
11381         //Roo.log('field ' + e.getKey());
11382         if(e.isNavKeyPress()){
11383             this.fireEvent("specialkey", this, e);
11384         }
11385     },
11386     focus : function (selectText){
11387         if(this.rendered){
11388             this.inputEl().focus();
11389             if(selectText === true){
11390                 this.inputEl().dom.select();
11391             }
11392         }
11393         return this;
11394     } ,
11395     
11396     onFocus : function(){
11397         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11398            // this.el.addClass(this.focusClass);
11399         }
11400         if(!this.hasFocus){
11401             this.hasFocus = true;
11402             this.startValue = this.getValue();
11403             this.fireEvent("focus", this);
11404         }
11405     },
11406     
11407     beforeBlur : Roo.emptyFn,
11408
11409     
11410     // private
11411     onBlur : function(){
11412         this.beforeBlur();
11413         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11414             //this.el.removeClass(this.focusClass);
11415         }
11416         this.hasFocus = false;
11417         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11418             this.validate();
11419         }
11420         var v = this.getValue();
11421         if(String(v) !== String(this.startValue)){
11422             this.fireEvent('change', this, v, this.startValue);
11423         }
11424         this.fireEvent("blur", this);
11425     },
11426     
11427     onChange : function(e)
11428     {
11429         var v = this.getValue();
11430         if(String(v) !== String(this.startValue)){
11431             this.fireEvent('change', this, v, this.startValue);
11432         }
11433         
11434     },
11435     
11436     /**
11437      * Resets the current field value to the originally loaded value and clears any validation messages
11438      */
11439     reset : function(){
11440         this.setValue(this.originalValue);
11441         this.validate();
11442     },
11443      /**
11444      * Returns the name of the field
11445      * @return {Mixed} name The name field
11446      */
11447     getName: function(){
11448         return this.name;
11449     },
11450      /**
11451      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11452      * @return {Mixed} value The field value
11453      */
11454     getValue : function(){
11455         
11456         var v = this.inputEl().getValue();
11457         
11458         return v;
11459     },
11460     /**
11461      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11462      * @return {Mixed} value The field value
11463      */
11464     getRawValue : function(){
11465         var v = this.inputEl().getValue();
11466         
11467         return v;
11468     },
11469     
11470     /**
11471      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11472      * @param {Mixed} value The value to set
11473      */
11474     setRawValue : function(v){
11475         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11476     },
11477     
11478     selectText : function(start, end){
11479         var v = this.getRawValue();
11480         if(v.length > 0){
11481             start = start === undefined ? 0 : start;
11482             end = end === undefined ? v.length : end;
11483             var d = this.inputEl().dom;
11484             if(d.setSelectionRange){
11485                 d.setSelectionRange(start, end);
11486             }else if(d.createTextRange){
11487                 var range = d.createTextRange();
11488                 range.moveStart("character", start);
11489                 range.moveEnd("character", v.length-end);
11490                 range.select();
11491             }
11492         }
11493     },
11494     
11495     /**
11496      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11497      * @param {Mixed} value The value to set
11498      */
11499     setValue : function(v){
11500         this.value = v;
11501         if(this.rendered){
11502             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11503             this.validate();
11504         }
11505     },
11506     
11507     /*
11508     processValue : function(value){
11509         if(this.stripCharsRe){
11510             var newValue = value.replace(this.stripCharsRe, '');
11511             if(newValue !== value){
11512                 this.setRawValue(newValue);
11513                 return newValue;
11514             }
11515         }
11516         return value;
11517     },
11518   */
11519     preFocus : function(){
11520         
11521         if(this.selectOnFocus){
11522             this.inputEl().dom.select();
11523         }
11524     },
11525     filterKeys : function(e){
11526         var k = e.getKey();
11527         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11528             return;
11529         }
11530         var c = e.getCharCode(), cc = String.fromCharCode(c);
11531         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11532             return;
11533         }
11534         if(!this.maskRe.test(cc)){
11535             e.stopEvent();
11536         }
11537     },
11538      /**
11539      * Clear any invalid styles/messages for this field
11540      */
11541     clearInvalid : function(){
11542         
11543         if(!this.el || this.preventMark){ // not rendered
11544             return;
11545         }
11546         
11547         
11548         this.el.removeClass([this.invalidClass, 'is-invalid']);
11549         
11550         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11551             
11552             var feedback = this.el.select('.form-control-feedback', true).first();
11553             
11554             if(feedback){
11555                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11556             }
11557             
11558         }
11559         
11560         if(this.indicator){
11561             this.indicator.removeClass('visible');
11562             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11563         }
11564         
11565         this.fireEvent('valid', this);
11566     },
11567     
11568      /**
11569      * Mark this field as valid
11570      */
11571     markValid : function()
11572     {
11573         if(!this.el  || this.preventMark){ // not rendered...
11574             return;
11575         }
11576         
11577         this.el.removeClass([this.invalidClass, this.validClass]);
11578         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11579
11580         var feedback = this.el.select('.form-control-feedback', true).first();
11581             
11582         if(feedback){
11583             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11584         }
11585         
11586         if(this.indicator){
11587             this.indicator.removeClass('visible');
11588             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11589         }
11590         
11591         if(this.disabled){
11592             return;
11593         }
11594         
11595            
11596         if(this.allowBlank && !this.getRawValue().length){
11597             return;
11598         }
11599         if (Roo.bootstrap.version == 3) {
11600             this.el.addClass(this.validClass);
11601         } else {
11602             this.inputEl().addClass('is-valid');
11603         }
11604
11605         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11606             
11607             var feedback = this.el.select('.form-control-feedback', true).first();
11608             
11609             if(feedback){
11610                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11611                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11612             }
11613             
11614         }
11615         
11616         this.fireEvent('valid', this);
11617     },
11618     
11619      /**
11620      * Mark this field as invalid
11621      * @param {String} msg The validation message
11622      */
11623     markInvalid : function(msg)
11624     {
11625         if(!this.el  || this.preventMark){ // not rendered
11626             return;
11627         }
11628         
11629         this.el.removeClass([this.invalidClass, this.validClass]);
11630         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11631         
11632         var feedback = this.el.select('.form-control-feedback', true).first();
11633             
11634         if(feedback){
11635             this.el.select('.form-control-feedback', true).first().removeClass(
11636                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11637         }
11638
11639         if(this.disabled){
11640             return;
11641         }
11642         
11643         if(this.allowBlank && !this.getRawValue().length){
11644             return;
11645         }
11646         
11647         if(this.indicator){
11648             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11649             this.indicator.addClass('visible');
11650         }
11651         if (Roo.bootstrap.version == 3) {
11652             this.el.addClass(this.invalidClass);
11653         } else {
11654             this.inputEl().addClass('is-invalid');
11655         }
11656         
11657         
11658         
11659         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11660             
11661             var feedback = this.el.select('.form-control-feedback', true).first();
11662             
11663             if(feedback){
11664                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11665                 
11666                 if(this.getValue().length || this.forceFeedback){
11667                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11668                 }
11669                 
11670             }
11671             
11672         }
11673         
11674         this.fireEvent('invalid', this, msg);
11675     },
11676     // private
11677     SafariOnKeyDown : function(event)
11678     {
11679         // this is a workaround for a password hang bug on chrome/ webkit.
11680         if (this.inputEl().dom.type != 'password') {
11681             return;
11682         }
11683         
11684         var isSelectAll = false;
11685         
11686         if(this.inputEl().dom.selectionEnd > 0){
11687             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11688         }
11689         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11690             event.preventDefault();
11691             this.setValue('');
11692             return;
11693         }
11694         
11695         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11696             
11697             event.preventDefault();
11698             // this is very hacky as keydown always get's upper case.
11699             //
11700             var cc = String.fromCharCode(event.getCharCode());
11701             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11702             
11703         }
11704     },
11705     adjustWidth : function(tag, w){
11706         tag = tag.toLowerCase();
11707         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11708             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11709                 if(tag == 'input'){
11710                     return w + 2;
11711                 }
11712                 if(tag == 'textarea'){
11713                     return w-2;
11714                 }
11715             }else if(Roo.isOpera){
11716                 if(tag == 'input'){
11717                     return w + 2;
11718                 }
11719                 if(tag == 'textarea'){
11720                     return w-2;
11721                 }
11722             }
11723         }
11724         return w;
11725     },
11726     
11727     setFieldLabel : function(v)
11728     {
11729         if(!this.rendered){
11730             return;
11731         }
11732         
11733         if(this.indicatorEl()){
11734             var ar = this.el.select('label > span',true);
11735             
11736             if (ar.elements.length) {
11737                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11738                 this.fieldLabel = v;
11739                 return;
11740             }
11741             
11742             var br = this.el.select('label',true);
11743             
11744             if(br.elements.length) {
11745                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11746                 this.fieldLabel = v;
11747                 return;
11748             }
11749             
11750             Roo.log('Cannot Found any of label > span || label in input');
11751             return;
11752         }
11753         
11754         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11755         this.fieldLabel = v;
11756         
11757         
11758     }
11759 });
11760
11761  
11762 /*
11763  * - LGPL
11764  *
11765  * Input
11766  * 
11767  */
11768
11769 /**
11770  * @class Roo.bootstrap.TextArea
11771  * @extends Roo.bootstrap.Input
11772  * Bootstrap TextArea class
11773  * @cfg {Number} cols Specifies the visible width of a text area
11774  * @cfg {Number} rows Specifies the visible number of lines in a text area
11775  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11776  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11777  * @cfg {string} html text
11778  * 
11779  * @constructor
11780  * Create a new TextArea
11781  * @param {Object} config The config object
11782  */
11783
11784 Roo.bootstrap.TextArea = function(config){
11785     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11786    
11787 };
11788
11789 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11790      
11791     cols : false,
11792     rows : 5,
11793     readOnly : false,
11794     warp : 'soft',
11795     resize : false,
11796     value: false,
11797     html: false,
11798     
11799     getAutoCreate : function(){
11800         
11801         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11802         
11803         var id = Roo.id();
11804         
11805         var cfg = {};
11806         
11807         if(this.inputType != 'hidden'){
11808             cfg.cls = 'form-group' //input-group
11809         }
11810         
11811         var input =  {
11812             tag: 'textarea',
11813             id : id,
11814             warp : this.warp,
11815             rows : this.rows,
11816             value : this.value || '',
11817             html: this.html || '',
11818             cls : 'form-control',
11819             placeholder : this.placeholder || '' 
11820             
11821         };
11822         
11823         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11824             input.maxLength = this.maxLength;
11825         }
11826         
11827         if(this.resize){
11828             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11829         }
11830         
11831         if(this.cols){
11832             input.cols = this.cols;
11833         }
11834         
11835         if (this.readOnly) {
11836             input.readonly = true;
11837         }
11838         
11839         if (this.name) {
11840             input.name = this.name;
11841         }
11842         
11843         if (this.size) {
11844             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11845         }
11846         
11847         var settings=this;
11848         ['xs','sm','md','lg'].map(function(size){
11849             if (settings[size]) {
11850                 cfg.cls += ' col-' + size + '-' + settings[size];
11851             }
11852         });
11853         
11854         var inputblock = input;
11855         
11856         if(this.hasFeedback && !this.allowBlank){
11857             
11858             var feedback = {
11859                 tag: 'span',
11860                 cls: 'glyphicon form-control-feedback'
11861             };
11862
11863             inputblock = {
11864                 cls : 'has-feedback',
11865                 cn :  [
11866                     input,
11867                     feedback
11868                 ] 
11869             };  
11870         }
11871         
11872         
11873         if (this.before || this.after) {
11874             
11875             inputblock = {
11876                 cls : 'input-group',
11877                 cn :  [] 
11878             };
11879             if (this.before) {
11880                 inputblock.cn.push({
11881                     tag :'span',
11882                     cls : 'input-group-addon',
11883                     html : this.before
11884                 });
11885             }
11886             
11887             inputblock.cn.push(input);
11888             
11889             if(this.hasFeedback && !this.allowBlank){
11890                 inputblock.cls += ' has-feedback';
11891                 inputblock.cn.push(feedback);
11892             }
11893             
11894             if (this.after) {
11895                 inputblock.cn.push({
11896                     tag :'span',
11897                     cls : 'input-group-addon',
11898                     html : this.after
11899                 });
11900             }
11901             
11902         }
11903         
11904         if (align ==='left' && this.fieldLabel.length) {
11905             cfg.cn = [
11906                 {
11907                     tag: 'label',
11908                     'for' :  id,
11909                     cls : 'control-label',
11910                     html : this.fieldLabel
11911                 },
11912                 {
11913                     cls : "",
11914                     cn: [
11915                         inputblock
11916                     ]
11917                 }
11918
11919             ];
11920             
11921             if(this.labelWidth > 12){
11922                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11923             }
11924
11925             if(this.labelWidth < 13 && this.labelmd == 0){
11926                 this.labelmd = this.labelWidth;
11927             }
11928
11929             if(this.labellg > 0){
11930                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11931                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11932             }
11933
11934             if(this.labelmd > 0){
11935                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11936                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11937             }
11938
11939             if(this.labelsm > 0){
11940                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11941                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11942             }
11943
11944             if(this.labelxs > 0){
11945                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11946                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11947             }
11948             
11949         } else if ( this.fieldLabel.length) {
11950             cfg.cn = [
11951
11952                {
11953                    tag: 'label',
11954                    //cls : 'input-group-addon',
11955                    html : this.fieldLabel
11956
11957                },
11958
11959                inputblock
11960
11961            ];
11962
11963         } else {
11964
11965             cfg.cn = [
11966
11967                 inputblock
11968
11969             ];
11970                 
11971         }
11972         
11973         if (this.disabled) {
11974             input.disabled=true;
11975         }
11976         
11977         return cfg;
11978         
11979     },
11980     /**
11981      * return the real textarea element.
11982      */
11983     inputEl: function ()
11984     {
11985         return this.el.select('textarea.form-control',true).first();
11986     },
11987     
11988     /**
11989      * Clear any invalid styles/messages for this field
11990      */
11991     clearInvalid : function()
11992     {
11993         
11994         if(!this.el || this.preventMark){ // not rendered
11995             return;
11996         }
11997         
11998         var label = this.el.select('label', true).first();
11999         var icon = this.el.select('i.fa-star', true).first();
12000         
12001         if(label && icon){
12002             icon.remove();
12003         }
12004         this.el.removeClass( this.validClass);
12005         this.inputEl().removeClass('is-invalid');
12006          
12007         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12008             
12009             var feedback = this.el.select('.form-control-feedback', true).first();
12010             
12011             if(feedback){
12012                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12013             }
12014             
12015         }
12016         
12017         this.fireEvent('valid', this);
12018     },
12019     
12020      /**
12021      * Mark this field as valid
12022      */
12023     markValid : function()
12024     {
12025         if(!this.el  || this.preventMark){ // not rendered
12026             return;
12027         }
12028         
12029         this.el.removeClass([this.invalidClass, this.validClass]);
12030         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12031         
12032         var feedback = this.el.select('.form-control-feedback', true).first();
12033             
12034         if(feedback){
12035             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12036         }
12037
12038         if(this.disabled || this.allowBlank){
12039             return;
12040         }
12041         
12042         var label = this.el.select('label', true).first();
12043         var icon = this.el.select('i.fa-star', true).first();
12044         
12045         if(label && icon){
12046             icon.remove();
12047         }
12048         if (Roo.bootstrap.version == 3) {
12049             this.el.addClass(this.validClass);
12050         } else {
12051             this.inputEl().addClass('is-valid');
12052         }
12053         
12054         
12055         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12056             
12057             var feedback = this.el.select('.form-control-feedback', true).first();
12058             
12059             if(feedback){
12060                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12061                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12062             }
12063             
12064         }
12065         
12066         this.fireEvent('valid', this);
12067     },
12068     
12069      /**
12070      * Mark this field as invalid
12071      * @param {String} msg The validation message
12072      */
12073     markInvalid : function(msg)
12074     {
12075         if(!this.el  || this.preventMark){ // not rendered
12076             return;
12077         }
12078         
12079         this.el.removeClass([this.invalidClass, this.validClass]);
12080         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12081         
12082         var feedback = this.el.select('.form-control-feedback', true).first();
12083             
12084         if(feedback){
12085             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12086         }
12087
12088         if(this.disabled || this.allowBlank){
12089             return;
12090         }
12091         
12092         var label = this.el.select('label', true).first();
12093         var icon = this.el.select('i.fa-star', true).first();
12094         
12095         if(!this.getValue().length && label && !icon){
12096             this.el.createChild({
12097                 tag : 'i',
12098                 cls : 'text-danger fa fa-lg fa-star',
12099                 tooltip : 'This field is required',
12100                 style : 'margin-right:5px;'
12101             }, label, true);
12102         }
12103         
12104         if (Roo.bootstrap.version == 3) {
12105             this.el.addClass(this.invalidClass);
12106         } else {
12107             this.inputEl().addClass('is-invalid');
12108         }
12109         
12110         // fixme ... this may be depricated need to test..
12111         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12112             
12113             var feedback = this.el.select('.form-control-feedback', true).first();
12114             
12115             if(feedback){
12116                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12117                 
12118                 if(this.getValue().length || this.forceFeedback){
12119                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12120                 }
12121                 
12122             }
12123             
12124         }
12125         
12126         this.fireEvent('invalid', this, msg);
12127     }
12128 });
12129
12130  
12131 /*
12132  * - LGPL
12133  *
12134  * trigger field - base class for combo..
12135  * 
12136  */
12137  
12138 /**
12139  * @class Roo.bootstrap.TriggerField
12140  * @extends Roo.bootstrap.Input
12141  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12142  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12143  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12144  * for which you can provide a custom implementation.  For example:
12145  * <pre><code>
12146 var trigger = new Roo.bootstrap.TriggerField();
12147 trigger.onTriggerClick = myTriggerFn;
12148 trigger.applyTo('my-field');
12149 </code></pre>
12150  *
12151  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12152  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12153  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12154  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12155  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12156
12157  * @constructor
12158  * Create a new TriggerField.
12159  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12160  * to the base TextField)
12161  */
12162 Roo.bootstrap.TriggerField = function(config){
12163     this.mimicing = false;
12164     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12165 };
12166
12167 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12168     /**
12169      * @cfg {String} triggerClass A CSS class to apply to the trigger
12170      */
12171      /**
12172      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12173      */
12174     hideTrigger:false,
12175
12176     /**
12177      * @cfg {Boolean} removable (true|false) special filter default false
12178      */
12179     removable : false,
12180     
12181     /** @cfg {Boolean} grow @hide */
12182     /** @cfg {Number} growMin @hide */
12183     /** @cfg {Number} growMax @hide */
12184
12185     /**
12186      * @hide 
12187      * @method
12188      */
12189     autoSize: Roo.emptyFn,
12190     // private
12191     monitorTab : true,
12192     // private
12193     deferHeight : true,
12194
12195     
12196     actionMode : 'wrap',
12197     
12198     caret : false,
12199     
12200     
12201     getAutoCreate : function(){
12202        
12203         var align = this.labelAlign || this.parentLabelAlign();
12204         
12205         var id = Roo.id();
12206         
12207         var cfg = {
12208             cls: 'form-group' //input-group
12209         };
12210         
12211         
12212         var input =  {
12213             tag: 'input',
12214             id : id,
12215             type : this.inputType,
12216             cls : 'form-control',
12217             autocomplete: 'new-password',
12218             placeholder : this.placeholder || '' 
12219             
12220         };
12221         if (this.name) {
12222             input.name = this.name;
12223         }
12224         if (this.size) {
12225             input.cls += ' input-' + this.size;
12226         }
12227         
12228         if (this.disabled) {
12229             input.disabled=true;
12230         }
12231         
12232         var inputblock = input;
12233         
12234         if(this.hasFeedback && !this.allowBlank){
12235             
12236             var feedback = {
12237                 tag: 'span',
12238                 cls: 'glyphicon form-control-feedback'
12239             };
12240             
12241             if(this.removable && !this.editable  ){
12242                 inputblock = {
12243                     cls : 'has-feedback',
12244                     cn :  [
12245                         inputblock,
12246                         {
12247                             tag: 'button',
12248                             html : 'x',
12249                             cls : 'roo-combo-removable-btn close'
12250                         },
12251                         feedback
12252                     ] 
12253                 };
12254             } else {
12255                 inputblock = {
12256                     cls : 'has-feedback',
12257                     cn :  [
12258                         inputblock,
12259                         feedback
12260                     ] 
12261                 };
12262             }
12263
12264         } else {
12265             if(this.removable && !this.editable ){
12266                 inputblock = {
12267                     cls : 'roo-removable',
12268                     cn :  [
12269                         inputblock,
12270                         {
12271                             tag: 'button',
12272                             html : 'x',
12273                             cls : 'roo-combo-removable-btn close'
12274                         }
12275                     ] 
12276                 };
12277             }
12278         }
12279         
12280         if (this.before || this.after) {
12281             
12282             inputblock = {
12283                 cls : 'input-group',
12284                 cn :  [] 
12285             };
12286             if (this.before) {
12287                 inputblock.cn.push({
12288                     tag :'span',
12289                     cls : 'input-group-addon input-group-prepend input-group-text',
12290                     html : this.before
12291                 });
12292             }
12293             
12294             inputblock.cn.push(input);
12295             
12296             if(this.hasFeedback && !this.allowBlank){
12297                 inputblock.cls += ' has-feedback';
12298                 inputblock.cn.push(feedback);
12299             }
12300             
12301             if (this.after) {
12302                 inputblock.cn.push({
12303                     tag :'span',
12304                     cls : 'input-group-addon input-group-append input-group-text',
12305                     html : this.after
12306                 });
12307             }
12308             
12309         };
12310         
12311       
12312         
12313         var ibwrap = inputblock;
12314         
12315         if(this.multiple){
12316             ibwrap = {
12317                 tag: 'ul',
12318                 cls: 'roo-select2-choices',
12319                 cn:[
12320                     {
12321                         tag: 'li',
12322                         cls: 'roo-select2-search-field',
12323                         cn: [
12324
12325                             inputblock
12326                         ]
12327                     }
12328                 ]
12329             };
12330                 
12331         }
12332         
12333         var combobox = {
12334             cls: 'roo-select2-container input-group',
12335             cn: [
12336                  {
12337                     tag: 'input',
12338                     type : 'hidden',
12339                     cls: 'form-hidden-field'
12340                 },
12341                 ibwrap
12342             ]
12343         };
12344         
12345         if(!this.multiple && this.showToggleBtn){
12346             
12347             var caret = {
12348                         tag: 'span',
12349                         cls: 'caret'
12350              };
12351             if (this.caret != false) {
12352                 caret = {
12353                      tag: 'i',
12354                      cls: 'fa fa-' + this.caret
12355                 };
12356                 
12357             }
12358             
12359             combobox.cn.push({
12360                 tag :'span',
12361                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12362                 cn : [
12363                     Roo.bootstrap.version == 3 ? caret : '',
12364                     {
12365                         tag: 'span',
12366                         cls: 'combobox-clear',
12367                         cn  : [
12368                             {
12369                                 tag : 'i',
12370                                 cls: 'icon-remove'
12371                             }
12372                         ]
12373                     }
12374                 ]
12375
12376             })
12377         }
12378         
12379         if(this.multiple){
12380             combobox.cls += ' roo-select2-container-multi';
12381         }
12382          var indicator = {
12383             tag : 'i',
12384             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12385             tooltip : 'This field is required'
12386         };
12387         if (Roo.bootstrap.version == 4) {
12388             indicator = {
12389                 tag : 'i',
12390                 style : 'display:none'
12391             };
12392         }
12393         
12394         
12395         if (align ==='left' && this.fieldLabel.length) {
12396             
12397             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12398
12399             cfg.cn = [
12400                 indicator,
12401                 {
12402                     tag: 'label',
12403                     'for' :  id,
12404                     cls : 'control-label',
12405                     html : this.fieldLabel
12406
12407                 },
12408                 {
12409                     cls : "", 
12410                     cn: [
12411                         combobox
12412                     ]
12413                 }
12414
12415             ];
12416             
12417             var labelCfg = cfg.cn[1];
12418             var contentCfg = cfg.cn[2];
12419             
12420             if(this.indicatorpos == 'right'){
12421                 cfg.cn = [
12422                     {
12423                         tag: 'label',
12424                         'for' :  id,
12425                         cls : 'control-label',
12426                         cn : [
12427                             {
12428                                 tag : 'span',
12429                                 html : this.fieldLabel
12430                             },
12431                             indicator
12432                         ]
12433                     },
12434                     {
12435                         cls : "", 
12436                         cn: [
12437                             combobox
12438                         ]
12439                     }
12440
12441                 ];
12442                 
12443                 labelCfg = cfg.cn[0];
12444                 contentCfg = cfg.cn[1];
12445             }
12446             
12447             if(this.labelWidth > 12){
12448                 labelCfg.style = "width: " + this.labelWidth + 'px';
12449             }
12450             
12451             if(this.labelWidth < 13 && this.labelmd == 0){
12452                 this.labelmd = this.labelWidth;
12453             }
12454             
12455             if(this.labellg > 0){
12456                 labelCfg.cls += ' col-lg-' + this.labellg;
12457                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12458             }
12459             
12460             if(this.labelmd > 0){
12461                 labelCfg.cls += ' col-md-' + this.labelmd;
12462                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12463             }
12464             
12465             if(this.labelsm > 0){
12466                 labelCfg.cls += ' col-sm-' + this.labelsm;
12467                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12468             }
12469             
12470             if(this.labelxs > 0){
12471                 labelCfg.cls += ' col-xs-' + this.labelxs;
12472                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12473             }
12474             
12475         } else if ( this.fieldLabel.length) {
12476 //                Roo.log(" label");
12477             cfg.cn = [
12478                 indicator,
12479                {
12480                    tag: 'label',
12481                    //cls : 'input-group-addon',
12482                    html : this.fieldLabel
12483
12484                },
12485
12486                combobox
12487
12488             ];
12489             
12490             if(this.indicatorpos == 'right'){
12491                 
12492                 cfg.cn = [
12493                     {
12494                        tag: 'label',
12495                        cn : [
12496                            {
12497                                tag : 'span',
12498                                html : this.fieldLabel
12499                            },
12500                            indicator
12501                        ]
12502
12503                     },
12504                     combobox
12505
12506                 ];
12507
12508             }
12509
12510         } else {
12511             
12512 //                Roo.log(" no label && no align");
12513                 cfg = combobox
12514                      
12515                 
12516         }
12517         
12518         var settings=this;
12519         ['xs','sm','md','lg'].map(function(size){
12520             if (settings[size]) {
12521                 cfg.cls += ' col-' + size + '-' + settings[size];
12522             }
12523         });
12524         
12525         return cfg;
12526         
12527     },
12528     
12529     
12530     
12531     // private
12532     onResize : function(w, h){
12533 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12534 //        if(typeof w == 'number'){
12535 //            var x = w - this.trigger.getWidth();
12536 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12537 //            this.trigger.setStyle('left', x+'px');
12538 //        }
12539     },
12540
12541     // private
12542     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12543
12544     // private
12545     getResizeEl : function(){
12546         return this.inputEl();
12547     },
12548
12549     // private
12550     getPositionEl : function(){
12551         return this.inputEl();
12552     },
12553
12554     // private
12555     alignErrorIcon : function(){
12556         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12557     },
12558
12559     // private
12560     initEvents : function(){
12561         
12562         this.createList();
12563         
12564         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12565         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12566         if(!this.multiple && this.showToggleBtn){
12567             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12568             if(this.hideTrigger){
12569                 this.trigger.setDisplayed(false);
12570             }
12571             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12572         }
12573         
12574         if(this.multiple){
12575             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12576         }
12577         
12578         if(this.removable && !this.editable && !this.tickable){
12579             var close = this.closeTriggerEl();
12580             
12581             if(close){
12582                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12583                 close.on('click', this.removeBtnClick, this, close);
12584             }
12585         }
12586         
12587         //this.trigger.addClassOnOver('x-form-trigger-over');
12588         //this.trigger.addClassOnClick('x-form-trigger-click');
12589         
12590         //if(!this.width){
12591         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12592         //}
12593     },
12594     
12595     closeTriggerEl : function()
12596     {
12597         var close = this.el.select('.roo-combo-removable-btn', true).first();
12598         return close ? close : false;
12599     },
12600     
12601     removeBtnClick : function(e, h, el)
12602     {
12603         e.preventDefault();
12604         
12605         if(this.fireEvent("remove", this) !== false){
12606             this.reset();
12607             this.fireEvent("afterremove", this)
12608         }
12609     },
12610     
12611     createList : function()
12612     {
12613         this.list = Roo.get(document.body).createChild({
12614             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12615             cls: 'typeahead typeahead-long dropdown-menu shadow',
12616             style: 'display:none'
12617         });
12618         
12619         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12620         
12621     },
12622
12623     // private
12624     initTrigger : function(){
12625        
12626     },
12627
12628     // private
12629     onDestroy : function(){
12630         if(this.trigger){
12631             this.trigger.removeAllListeners();
12632           //  this.trigger.remove();
12633         }
12634         //if(this.wrap){
12635         //    this.wrap.remove();
12636         //}
12637         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12638     },
12639
12640     // private
12641     onFocus : function(){
12642         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12643         /*
12644         if(!this.mimicing){
12645             this.wrap.addClass('x-trigger-wrap-focus');
12646             this.mimicing = true;
12647             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12648             if(this.monitorTab){
12649                 this.el.on("keydown", this.checkTab, this);
12650             }
12651         }
12652         */
12653     },
12654
12655     // private
12656     checkTab : function(e){
12657         if(e.getKey() == e.TAB){
12658             this.triggerBlur();
12659         }
12660     },
12661
12662     // private
12663     onBlur : function(){
12664         // do nothing
12665     },
12666
12667     // private
12668     mimicBlur : function(e, t){
12669         /*
12670         if(!this.wrap.contains(t) && this.validateBlur()){
12671             this.triggerBlur();
12672         }
12673         */
12674     },
12675
12676     // private
12677     triggerBlur : function(){
12678         this.mimicing = false;
12679         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12680         if(this.monitorTab){
12681             this.el.un("keydown", this.checkTab, this);
12682         }
12683         //this.wrap.removeClass('x-trigger-wrap-focus');
12684         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12685     },
12686
12687     // private
12688     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12689     validateBlur : function(e, t){
12690         return true;
12691     },
12692
12693     // private
12694     onDisable : function(){
12695         this.inputEl().dom.disabled = true;
12696         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12697         //if(this.wrap){
12698         //    this.wrap.addClass('x-item-disabled');
12699         //}
12700     },
12701
12702     // private
12703     onEnable : function(){
12704         this.inputEl().dom.disabled = false;
12705         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12706         //if(this.wrap){
12707         //    this.el.removeClass('x-item-disabled');
12708         //}
12709     },
12710
12711     // private
12712     onShow : function(){
12713         var ae = this.getActionEl();
12714         
12715         if(ae){
12716             ae.dom.style.display = '';
12717             ae.dom.style.visibility = 'visible';
12718         }
12719     },
12720
12721     // private
12722     
12723     onHide : function(){
12724         var ae = this.getActionEl();
12725         ae.dom.style.display = 'none';
12726     },
12727
12728     /**
12729      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12730      * by an implementing function.
12731      * @method
12732      * @param {EventObject} e
12733      */
12734     onTriggerClick : Roo.emptyFn
12735 });
12736  
12737 /*
12738 * Licence: LGPL
12739 */
12740
12741 /**
12742  * @class Roo.bootstrap.CardUploader
12743  * @extends Roo.bootstrap.Button
12744  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12745  * @cfg {Number} errorTimeout default 3000
12746  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12747  * @cfg {Array}  html The button text.
12748
12749  *
12750  * @constructor
12751  * Create a new CardUploader
12752  * @param {Object} config The config object
12753  */
12754
12755 Roo.bootstrap.CardUploader = function(config){
12756     
12757  
12758     
12759     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12760     
12761     
12762     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12763         return r.data.id
12764      });
12765     
12766      this.addEvents({
12767          // raw events
12768         /**
12769          * @event preview
12770          * When a image is clicked on - and needs to display a slideshow or similar..
12771          * @param {Roo.bootstrap.Card} this
12772          * @param {Object} The image information data 
12773          *
12774          */
12775         'preview' : true,
12776          /**
12777          * @event download
12778          * When a the download link is clicked
12779          * @param {Roo.bootstrap.Card} this
12780          * @param {Object} The image information data  contains 
12781          */
12782         'download' : true
12783         
12784     });
12785 };
12786  
12787 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12788     
12789      
12790     errorTimeout : 3000,
12791      
12792     images : false,
12793    
12794     fileCollection : false,
12795     allowBlank : true,
12796     
12797     getAutoCreate : function()
12798     {
12799         
12800         var cfg =  {
12801             cls :'form-group' ,
12802             cn : [
12803                
12804                 {
12805                     tag: 'label',
12806                    //cls : 'input-group-addon',
12807                     html : this.fieldLabel
12808
12809                 },
12810
12811                 {
12812                     tag: 'input',
12813                     type : 'hidden',
12814                     name : this.name,
12815                     value : this.value,
12816                     cls : 'd-none  form-control'
12817                 },
12818                 
12819                 {
12820                     tag: 'input',
12821                     multiple : 'multiple',
12822                     type : 'file',
12823                     cls : 'd-none  roo-card-upload-selector'
12824                 },
12825                 
12826                 {
12827                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12828                 },
12829                 {
12830                     cls : 'card-columns roo-card-uploader-container'
12831                 }
12832
12833             ]
12834         };
12835            
12836          
12837         return cfg;
12838     },
12839     
12840     getChildContainer : function() /// what children are added to.
12841     {
12842         return this.containerEl;
12843     },
12844    
12845     getButtonContainer : function() /// what children are added to.
12846     {
12847         return this.el.select(".roo-card-uploader-button-container").first();
12848     },
12849    
12850     initEvents : function()
12851     {
12852         
12853         Roo.bootstrap.Input.prototype.initEvents.call(this);
12854         
12855         var t = this;
12856         this.addxtype({
12857             xns: Roo.bootstrap,
12858
12859             xtype : 'Button',
12860             container_method : 'getButtonContainer' ,            
12861             html :  this.html, // fix changable?
12862             cls : 'w-100 ',
12863             listeners : {
12864                 'click' : function(btn, e) {
12865                     t.onClick(e);
12866                 }
12867             }
12868         });
12869         
12870         
12871         
12872         
12873         this.urlAPI = (window.createObjectURL && window) || 
12874                                 (window.URL && URL.revokeObjectURL && URL) || 
12875                                 (window.webkitURL && webkitURL);
12876                         
12877          
12878          
12879          
12880         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12881         
12882         this.selectorEl.on('change', this.onFileSelected, this);
12883         if (this.images) {
12884             var t = this;
12885             this.images.forEach(function(img) {
12886                 t.addCard(img)
12887             });
12888             this.images = false;
12889         }
12890         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12891          
12892        
12893     },
12894     
12895    
12896     onClick : function(e)
12897     {
12898         e.preventDefault();
12899          
12900         this.selectorEl.dom.click();
12901          
12902     },
12903     
12904     onFileSelected : function(e)
12905     {
12906         e.preventDefault();
12907         
12908         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12909             return;
12910         }
12911         
12912         Roo.each(this.selectorEl.dom.files, function(file){    
12913             this.addFile(file);
12914         }, this);
12915          
12916     },
12917     
12918       
12919     
12920       
12921     
12922     addFile : function(file)
12923     {
12924            
12925         if(typeof(file) === 'string'){
12926             throw "Add file by name?"; // should not happen
12927             return;
12928         }
12929         
12930         if(!file || !this.urlAPI){
12931             return;
12932         }
12933         
12934         // file;
12935         // file.type;
12936         
12937         var _this = this;
12938         
12939         
12940         var url = _this.urlAPI.createObjectURL( file);
12941            
12942         this.addCard({
12943             id : Roo.bootstrap.CardUploader.ID--,
12944             is_uploaded : false,
12945             src : url,
12946             srcfile : file,
12947             title : file.name,
12948             mimetype : file.type,
12949             preview : false,
12950             is_deleted : 0
12951         });
12952         
12953     },
12954     
12955     /**
12956      * addCard - add an Attachment to the uploader
12957      * @param data - the data about the image to upload
12958      *
12959      * {
12960           id : 123
12961           title : "Title of file",
12962           is_uploaded : false,
12963           src : "http://.....",
12964           srcfile : { the File upload object },
12965           mimetype : file.type,
12966           preview : false,
12967           is_deleted : 0
12968           .. any other data...
12969         }
12970      *
12971      * 
12972     */
12973     
12974     addCard : function (data)
12975     {
12976         // hidden input element?
12977         // if the file is not an image...
12978         //then we need to use something other that and header_image
12979         var t = this;
12980         //   remove.....
12981         var footer = [
12982             {
12983                 xns : Roo.bootstrap,
12984                 xtype : 'CardFooter',
12985                  items: [
12986                     {
12987                         xns : Roo.bootstrap,
12988                         xtype : 'Element',
12989                         cls : 'd-flex',
12990                         items : [
12991                             
12992                             {
12993                                 xns : Roo.bootstrap,
12994                                 xtype : 'Button',
12995                                 html : String.format("<small>{0}</small>", data.title),
12996                                 cls : 'col-10 text-left',
12997                                 size: 'sm',
12998                                 weight: 'link',
12999                                 fa : 'download',
13000                                 listeners : {
13001                                     click : function() {
13002                                      
13003                                         t.fireEvent( "download", t, data );
13004                                     }
13005                                 }
13006                             },
13007                           
13008                             {
13009                                 xns : Roo.bootstrap,
13010                                 xtype : 'Button',
13011                                 style: 'max-height: 28px; ',
13012                                 size : 'sm',
13013                                 weight: 'danger',
13014                                 cls : 'col-2',
13015                                 fa : 'times',
13016                                 listeners : {
13017                                     click : function() {
13018                                         t.removeCard(data.id)
13019                                     }
13020                                 }
13021                             }
13022                         ]
13023                     }
13024                     
13025                 ] 
13026             }
13027             
13028         ];
13029         
13030         var cn = this.addxtype(
13031             {
13032                  
13033                 xns : Roo.bootstrap,
13034                 xtype : 'Card',
13035                 closeable : true,
13036                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13037                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13038                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13039                 data : data,
13040                 html : false,
13041                  
13042                 items : footer,
13043                 initEvents : function() {
13044                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13045                     var card = this;
13046                     this.imgEl = this.el.select('.card-img-top').first();
13047                     if (this.imgEl) {
13048                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13049                         this.imgEl.set({ 'pointer' : 'cursor' });
13050                                   
13051                     }
13052                     this.getCardFooter().addClass('p-1');
13053                     
13054                   
13055                 }
13056                 
13057             }
13058         );
13059         // dont' really need ot update items.
13060         // this.items.push(cn);
13061         this.fileCollection.add(cn);
13062         
13063         if (!data.srcfile) {
13064             this.updateInput();
13065             return;
13066         }
13067             
13068         var _t = this;
13069         var reader = new FileReader();
13070         reader.addEventListener("load", function() {  
13071             data.srcdata =  reader.result;
13072             _t.updateInput();
13073         });
13074         reader.readAsDataURL(data.srcfile);
13075         
13076         
13077         
13078     },
13079     removeCard : function(id)
13080     {
13081         
13082         var card  = this.fileCollection.get(id);
13083         card.data.is_deleted = 1;
13084         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13085         //this.fileCollection.remove(card);
13086         //this.items = this.items.filter(function(e) { return e != card });
13087         // dont' really need ot update items.
13088         card.el.dom.parentNode.removeChild(card.el.dom);
13089         this.updateInput();
13090
13091         
13092     },
13093     reset: function()
13094     {
13095         this.fileCollection.each(function(card) {
13096             if (card.el.dom && card.el.dom.parentNode) {
13097                 card.el.dom.parentNode.removeChild(card.el.dom);
13098             }
13099         });
13100         this.fileCollection.clear();
13101         this.updateInput();
13102     },
13103     
13104     updateInput : function()
13105     {
13106          var data = [];
13107         this.fileCollection.each(function(e) {
13108             data.push(e.data);
13109             
13110         });
13111         this.inputEl().dom.value = JSON.stringify(data);
13112         
13113         
13114         
13115     }
13116     
13117     
13118 });
13119
13120
13121 Roo.bootstrap.CardUploader.ID = -1;/*
13122  * Based on:
13123  * Ext JS Library 1.1.1
13124  * Copyright(c) 2006-2007, Ext JS, LLC.
13125  *
13126  * Originally Released Under LGPL - original licence link has changed is not relivant.
13127  *
13128  * Fork - LGPL
13129  * <script type="text/javascript">
13130  */
13131
13132
13133 /**
13134  * @class Roo.data.SortTypes
13135  * @singleton
13136  * Defines the default sorting (casting?) comparison functions used when sorting data.
13137  */
13138 Roo.data.SortTypes = {
13139     /**
13140      * Default sort that does nothing
13141      * @param {Mixed} s The value being converted
13142      * @return {Mixed} The comparison value
13143      */
13144     none : function(s){
13145         return s;
13146     },
13147     
13148     /**
13149      * The regular expression used to strip tags
13150      * @type {RegExp}
13151      * @property
13152      */
13153     stripTagsRE : /<\/?[^>]+>/gi,
13154     
13155     /**
13156      * Strips all HTML tags to sort on text only
13157      * @param {Mixed} s The value being converted
13158      * @return {String} The comparison value
13159      */
13160     asText : function(s){
13161         return String(s).replace(this.stripTagsRE, "");
13162     },
13163     
13164     /**
13165      * Strips all HTML tags to sort on text only - Case insensitive
13166      * @param {Mixed} s The value being converted
13167      * @return {String} The comparison value
13168      */
13169     asUCText : function(s){
13170         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13171     },
13172     
13173     /**
13174      * Case insensitive string
13175      * @param {Mixed} s The value being converted
13176      * @return {String} The comparison value
13177      */
13178     asUCString : function(s) {
13179         return String(s).toUpperCase();
13180     },
13181     
13182     /**
13183      * Date sorting
13184      * @param {Mixed} s The value being converted
13185      * @return {Number} The comparison value
13186      */
13187     asDate : function(s) {
13188         if(!s){
13189             return 0;
13190         }
13191         if(s instanceof Date){
13192             return s.getTime();
13193         }
13194         return Date.parse(String(s));
13195     },
13196     
13197     /**
13198      * Float sorting
13199      * @param {Mixed} s The value being converted
13200      * @return {Float} The comparison value
13201      */
13202     asFloat : function(s) {
13203         var val = parseFloat(String(s).replace(/,/g, ""));
13204         if(isNaN(val)) {
13205             val = 0;
13206         }
13207         return val;
13208     },
13209     
13210     /**
13211      * Integer sorting
13212      * @param {Mixed} s The value being converted
13213      * @return {Number} The comparison value
13214      */
13215     asInt : function(s) {
13216         var val = parseInt(String(s).replace(/,/g, ""));
13217         if(isNaN(val)) {
13218             val = 0;
13219         }
13220         return val;
13221     }
13222 };/*
13223  * Based on:
13224  * Ext JS Library 1.1.1
13225  * Copyright(c) 2006-2007, Ext JS, LLC.
13226  *
13227  * Originally Released Under LGPL - original licence link has changed is not relivant.
13228  *
13229  * Fork - LGPL
13230  * <script type="text/javascript">
13231  */
13232
13233 /**
13234 * @class Roo.data.Record
13235  * Instances of this class encapsulate both record <em>definition</em> information, and record
13236  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13237  * to access Records cached in an {@link Roo.data.Store} object.<br>
13238  * <p>
13239  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13240  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13241  * objects.<br>
13242  * <p>
13243  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13244  * @constructor
13245  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13246  * {@link #create}. The parameters are the same.
13247  * @param {Array} data An associative Array of data values keyed by the field name.
13248  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13249  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13250  * not specified an integer id is generated.
13251  */
13252 Roo.data.Record = function(data, id){
13253     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13254     this.data = data;
13255 };
13256
13257 /**
13258  * Generate a constructor for a specific record layout.
13259  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13260  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13261  * Each field definition object may contain the following properties: <ul>
13262  * <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,
13263  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13264  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13265  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13266  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13267  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13268  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13269  * this may be omitted.</p></li>
13270  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13271  * <ul><li>auto (Default, implies no conversion)</li>
13272  * <li>string</li>
13273  * <li>int</li>
13274  * <li>float</li>
13275  * <li>boolean</li>
13276  * <li>date</li></ul></p></li>
13277  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13278  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13279  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13280  * by the Reader into an object that will be stored in the Record. It is passed the
13281  * following parameters:<ul>
13282  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13283  * </ul></p></li>
13284  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13285  * </ul>
13286  * <br>usage:<br><pre><code>
13287 var TopicRecord = Roo.data.Record.create(
13288     {name: 'title', mapping: 'topic_title'},
13289     {name: 'author', mapping: 'username'},
13290     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13291     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13292     {name: 'lastPoster', mapping: 'user2'},
13293     {name: 'excerpt', mapping: 'post_text'}
13294 );
13295
13296 var myNewRecord = new TopicRecord({
13297     title: 'Do my job please',
13298     author: 'noobie',
13299     totalPosts: 1,
13300     lastPost: new Date(),
13301     lastPoster: 'Animal',
13302     excerpt: 'No way dude!'
13303 });
13304 myStore.add(myNewRecord);
13305 </code></pre>
13306  * @method create
13307  * @static
13308  */
13309 Roo.data.Record.create = function(o){
13310     var f = function(){
13311         f.superclass.constructor.apply(this, arguments);
13312     };
13313     Roo.extend(f, Roo.data.Record);
13314     var p = f.prototype;
13315     p.fields = new Roo.util.MixedCollection(false, function(field){
13316         return field.name;
13317     });
13318     for(var i = 0, len = o.length; i < len; i++){
13319         p.fields.add(new Roo.data.Field(o[i]));
13320     }
13321     f.getField = function(name){
13322         return p.fields.get(name);  
13323     };
13324     return f;
13325 };
13326
13327 Roo.data.Record.AUTO_ID = 1000;
13328 Roo.data.Record.EDIT = 'edit';
13329 Roo.data.Record.REJECT = 'reject';
13330 Roo.data.Record.COMMIT = 'commit';
13331
13332 Roo.data.Record.prototype = {
13333     /**
13334      * Readonly flag - true if this record has been modified.
13335      * @type Boolean
13336      */
13337     dirty : false,
13338     editing : false,
13339     error: null,
13340     modified: null,
13341
13342     // private
13343     join : function(store){
13344         this.store = store;
13345     },
13346
13347     /**
13348      * Set the named field to the specified value.
13349      * @param {String} name The name of the field to set.
13350      * @param {Object} value The value to set the field to.
13351      */
13352     set : function(name, value){
13353         if(this.data[name] == value){
13354             return;
13355         }
13356         this.dirty = true;
13357         if(!this.modified){
13358             this.modified = {};
13359         }
13360         if(typeof this.modified[name] == 'undefined'){
13361             this.modified[name] = this.data[name];
13362         }
13363         this.data[name] = value;
13364         if(!this.editing && this.store){
13365             this.store.afterEdit(this);
13366         }       
13367     },
13368
13369     /**
13370      * Get the value of the named field.
13371      * @param {String} name The name of the field to get the value of.
13372      * @return {Object} The value of the field.
13373      */
13374     get : function(name){
13375         return this.data[name]; 
13376     },
13377
13378     // private
13379     beginEdit : function(){
13380         this.editing = true;
13381         this.modified = {}; 
13382     },
13383
13384     // private
13385     cancelEdit : function(){
13386         this.editing = false;
13387         delete this.modified;
13388     },
13389
13390     // private
13391     endEdit : function(){
13392         this.editing = false;
13393         if(this.dirty && this.store){
13394             this.store.afterEdit(this);
13395         }
13396     },
13397
13398     /**
13399      * Usually called by the {@link Roo.data.Store} which owns the Record.
13400      * Rejects all changes made to the Record since either creation, or the last commit operation.
13401      * Modified fields are reverted to their original values.
13402      * <p>
13403      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13404      * of reject operations.
13405      */
13406     reject : function(){
13407         var m = this.modified;
13408         for(var n in m){
13409             if(typeof m[n] != "function"){
13410                 this.data[n] = m[n];
13411             }
13412         }
13413         this.dirty = false;
13414         delete this.modified;
13415         this.editing = false;
13416         if(this.store){
13417             this.store.afterReject(this);
13418         }
13419     },
13420
13421     /**
13422      * Usually called by the {@link Roo.data.Store} which owns the Record.
13423      * Commits all changes made to the Record since either creation, or the last commit operation.
13424      * <p>
13425      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13426      * of commit operations.
13427      */
13428     commit : function(){
13429         this.dirty = false;
13430         delete this.modified;
13431         this.editing = false;
13432         if(this.store){
13433             this.store.afterCommit(this);
13434         }
13435     },
13436
13437     // private
13438     hasError : function(){
13439         return this.error != null;
13440     },
13441
13442     // private
13443     clearError : function(){
13444         this.error = null;
13445     },
13446
13447     /**
13448      * Creates a copy of this record.
13449      * @param {String} id (optional) A new record id if you don't want to use this record's id
13450      * @return {Record}
13451      */
13452     copy : function(newId) {
13453         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13454     }
13455 };/*
13456  * Based on:
13457  * Ext JS Library 1.1.1
13458  * Copyright(c) 2006-2007, Ext JS, LLC.
13459  *
13460  * Originally Released Under LGPL - original licence link has changed is not relivant.
13461  *
13462  * Fork - LGPL
13463  * <script type="text/javascript">
13464  */
13465
13466
13467
13468 /**
13469  * @class Roo.data.Store
13470  * @extends Roo.util.Observable
13471  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13472  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13473  * <p>
13474  * 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
13475  * has no knowledge of the format of the data returned by the Proxy.<br>
13476  * <p>
13477  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13478  * instances from the data object. These records are cached and made available through accessor functions.
13479  * @constructor
13480  * Creates a new Store.
13481  * @param {Object} config A config object containing the objects needed for the Store to access data,
13482  * and read the data into Records.
13483  */
13484 Roo.data.Store = function(config){
13485     this.data = new Roo.util.MixedCollection(false);
13486     this.data.getKey = function(o){
13487         return o.id;
13488     };
13489     this.baseParams = {};
13490     // private
13491     this.paramNames = {
13492         "start" : "start",
13493         "limit" : "limit",
13494         "sort" : "sort",
13495         "dir" : "dir",
13496         "multisort" : "_multisort"
13497     };
13498
13499     if(config && config.data){
13500         this.inlineData = config.data;
13501         delete config.data;
13502     }
13503
13504     Roo.apply(this, config);
13505     
13506     if(this.reader){ // reader passed
13507         this.reader = Roo.factory(this.reader, Roo.data);
13508         this.reader.xmodule = this.xmodule || false;
13509         if(!this.recordType){
13510             this.recordType = this.reader.recordType;
13511         }
13512         if(this.reader.onMetaChange){
13513             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13514         }
13515     }
13516
13517     if(this.recordType){
13518         this.fields = this.recordType.prototype.fields;
13519     }
13520     this.modified = [];
13521
13522     this.addEvents({
13523         /**
13524          * @event datachanged
13525          * Fires when the data cache has changed, and a widget which is using this Store
13526          * as a Record cache should refresh its view.
13527          * @param {Store} this
13528          */
13529         datachanged : true,
13530         /**
13531          * @event metachange
13532          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13533          * @param {Store} this
13534          * @param {Object} meta The JSON metadata
13535          */
13536         metachange : true,
13537         /**
13538          * @event add
13539          * Fires when Records have been added to the Store
13540          * @param {Store} this
13541          * @param {Roo.data.Record[]} records The array of Records added
13542          * @param {Number} index The index at which the record(s) were added
13543          */
13544         add : true,
13545         /**
13546          * @event remove
13547          * Fires when a Record has been removed from the Store
13548          * @param {Store} this
13549          * @param {Roo.data.Record} record The Record that was removed
13550          * @param {Number} index The index at which the record was removed
13551          */
13552         remove : true,
13553         /**
13554          * @event update
13555          * Fires when a Record has been updated
13556          * @param {Store} this
13557          * @param {Roo.data.Record} record The Record that was updated
13558          * @param {String} operation The update operation being performed.  Value may be one of:
13559          * <pre><code>
13560  Roo.data.Record.EDIT
13561  Roo.data.Record.REJECT
13562  Roo.data.Record.COMMIT
13563          * </code></pre>
13564          */
13565         update : true,
13566         /**
13567          * @event clear
13568          * Fires when the data cache has been cleared.
13569          * @param {Store} this
13570          */
13571         clear : true,
13572         /**
13573          * @event beforeload
13574          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13575          * the load action will be canceled.
13576          * @param {Store} this
13577          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13578          */
13579         beforeload : true,
13580         /**
13581          * @event beforeloadadd
13582          * Fires after a new set of Records has been loaded.
13583          * @param {Store} this
13584          * @param {Roo.data.Record[]} records The Records that were loaded
13585          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13586          */
13587         beforeloadadd : true,
13588         /**
13589          * @event load
13590          * Fires after a new set of Records has been loaded, before they are added to the store.
13591          * @param {Store} this
13592          * @param {Roo.data.Record[]} records The Records that were loaded
13593          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13594          * @params {Object} return from reader
13595          */
13596         load : true,
13597         /**
13598          * @event loadexception
13599          * Fires if an exception occurs in the Proxy during loading.
13600          * Called with the signature of the Proxy's "loadexception" event.
13601          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13602          * 
13603          * @param {Proxy} 
13604          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13605          * @param {Object} load options 
13606          * @param {Object} jsonData from your request (normally this contains the Exception)
13607          */
13608         loadexception : true
13609     });
13610     
13611     if(this.proxy){
13612         this.proxy = Roo.factory(this.proxy, Roo.data);
13613         this.proxy.xmodule = this.xmodule || false;
13614         this.relayEvents(this.proxy,  ["loadexception"]);
13615     }
13616     this.sortToggle = {};
13617     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13618
13619     Roo.data.Store.superclass.constructor.call(this);
13620
13621     if(this.inlineData){
13622         this.loadData(this.inlineData);
13623         delete this.inlineData;
13624     }
13625 };
13626
13627 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13628      /**
13629     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13630     * without a remote query - used by combo/forms at present.
13631     */
13632     
13633     /**
13634     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13635     */
13636     /**
13637     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13638     */
13639     /**
13640     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13641     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13642     */
13643     /**
13644     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13645     * on any HTTP request
13646     */
13647     /**
13648     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13649     */
13650     /**
13651     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13652     */
13653     multiSort: false,
13654     /**
13655     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13656     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13657     */
13658     remoteSort : false,
13659
13660     /**
13661     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13662      * loaded or when a record is removed. (defaults to false).
13663     */
13664     pruneModifiedRecords : false,
13665
13666     // private
13667     lastOptions : null,
13668
13669     /**
13670      * Add Records to the Store and fires the add event.
13671      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13672      */
13673     add : function(records){
13674         records = [].concat(records);
13675         for(var i = 0, len = records.length; i < len; i++){
13676             records[i].join(this);
13677         }
13678         var index = this.data.length;
13679         this.data.addAll(records);
13680         this.fireEvent("add", this, records, index);
13681     },
13682
13683     /**
13684      * Remove a Record from the Store and fires the remove event.
13685      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13686      */
13687     remove : function(record){
13688         var index = this.data.indexOf(record);
13689         this.data.removeAt(index);
13690  
13691         if(this.pruneModifiedRecords){
13692             this.modified.remove(record);
13693         }
13694         this.fireEvent("remove", this, record, index);
13695     },
13696
13697     /**
13698      * Remove all Records from the Store and fires the clear event.
13699      */
13700     removeAll : function(){
13701         this.data.clear();
13702         if(this.pruneModifiedRecords){
13703             this.modified = [];
13704         }
13705         this.fireEvent("clear", this);
13706     },
13707
13708     /**
13709      * Inserts Records to the Store at the given index and fires the add event.
13710      * @param {Number} index The start index at which to insert the passed Records.
13711      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13712      */
13713     insert : function(index, records){
13714         records = [].concat(records);
13715         for(var i = 0, len = records.length; i < len; i++){
13716             this.data.insert(index, records[i]);
13717             records[i].join(this);
13718         }
13719         this.fireEvent("add", this, records, index);
13720     },
13721
13722     /**
13723      * Get the index within the cache of the passed Record.
13724      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13725      * @return {Number} The index of the passed Record. Returns -1 if not found.
13726      */
13727     indexOf : function(record){
13728         return this.data.indexOf(record);
13729     },
13730
13731     /**
13732      * Get the index within the cache of the Record with the passed id.
13733      * @param {String} id The id of the Record to find.
13734      * @return {Number} The index of the Record. Returns -1 if not found.
13735      */
13736     indexOfId : function(id){
13737         return this.data.indexOfKey(id);
13738     },
13739
13740     /**
13741      * Get the Record with the specified id.
13742      * @param {String} id The id of the Record to find.
13743      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13744      */
13745     getById : function(id){
13746         return this.data.key(id);
13747     },
13748
13749     /**
13750      * Get the Record at the specified index.
13751      * @param {Number} index The index of the Record to find.
13752      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13753      */
13754     getAt : function(index){
13755         return this.data.itemAt(index);
13756     },
13757
13758     /**
13759      * Returns a range of Records between specified indices.
13760      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13761      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13762      * @return {Roo.data.Record[]} An array of Records
13763      */
13764     getRange : function(start, end){
13765         return this.data.getRange(start, end);
13766     },
13767
13768     // private
13769     storeOptions : function(o){
13770         o = Roo.apply({}, o);
13771         delete o.callback;
13772         delete o.scope;
13773         this.lastOptions = o;
13774     },
13775
13776     /**
13777      * Loads the Record cache from the configured Proxy using the configured Reader.
13778      * <p>
13779      * If using remote paging, then the first load call must specify the <em>start</em>
13780      * and <em>limit</em> properties in the options.params property to establish the initial
13781      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13782      * <p>
13783      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13784      * and this call will return before the new data has been loaded. Perform any post-processing
13785      * in a callback function, or in a "load" event handler.</strong>
13786      * <p>
13787      * @param {Object} options An object containing properties which control loading options:<ul>
13788      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13789      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13790      * passed the following arguments:<ul>
13791      * <li>r : Roo.data.Record[]</li>
13792      * <li>options: Options object from the load call</li>
13793      * <li>success: Boolean success indicator</li></ul></li>
13794      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13795      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13796      * </ul>
13797      */
13798     load : function(options){
13799         options = options || {};
13800         if(this.fireEvent("beforeload", this, options) !== false){
13801             this.storeOptions(options);
13802             var p = Roo.apply(options.params || {}, this.baseParams);
13803             // if meta was not loaded from remote source.. try requesting it.
13804             if (!this.reader.metaFromRemote) {
13805                 p._requestMeta = 1;
13806             }
13807             if(this.sortInfo && this.remoteSort){
13808                 var pn = this.paramNames;
13809                 p[pn["sort"]] = this.sortInfo.field;
13810                 p[pn["dir"]] = this.sortInfo.direction;
13811             }
13812             if (this.multiSort) {
13813                 var pn = this.paramNames;
13814                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13815             }
13816             
13817             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13818         }
13819     },
13820
13821     /**
13822      * Reloads the Record cache from the configured Proxy using the configured Reader and
13823      * the options from the last load operation performed.
13824      * @param {Object} options (optional) An object containing properties which may override the options
13825      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13826      * the most recently used options are reused).
13827      */
13828     reload : function(options){
13829         this.load(Roo.applyIf(options||{}, this.lastOptions));
13830     },
13831
13832     // private
13833     // Called as a callback by the Reader during a load operation.
13834     loadRecords : function(o, options, success){
13835         if(!o || success === false){
13836             if(success !== false){
13837                 this.fireEvent("load", this, [], options, o);
13838             }
13839             if(options.callback){
13840                 options.callback.call(options.scope || this, [], options, false);
13841             }
13842             return;
13843         }
13844         // if data returned failure - throw an exception.
13845         if (o.success === false) {
13846             // show a message if no listener is registered.
13847             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13848                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13849             }
13850             // loadmask wil be hooked into this..
13851             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13852             return;
13853         }
13854         var r = o.records, t = o.totalRecords || r.length;
13855         
13856         this.fireEvent("beforeloadadd", this, r, options, o);
13857         
13858         if(!options || options.add !== true){
13859             if(this.pruneModifiedRecords){
13860                 this.modified = [];
13861             }
13862             for(var i = 0, len = r.length; i < len; i++){
13863                 r[i].join(this);
13864             }
13865             if(this.snapshot){
13866                 this.data = this.snapshot;
13867                 delete this.snapshot;
13868             }
13869             this.data.clear();
13870             this.data.addAll(r);
13871             this.totalLength = t;
13872             this.applySort();
13873             this.fireEvent("datachanged", this);
13874         }else{
13875             this.totalLength = Math.max(t, this.data.length+r.length);
13876             this.add(r);
13877         }
13878         
13879         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13880                 
13881             var e = new Roo.data.Record({});
13882
13883             e.set(this.parent.displayField, this.parent.emptyTitle);
13884             e.set(this.parent.valueField, '');
13885
13886             this.insert(0, e);
13887         }
13888             
13889         this.fireEvent("load", this, r, options, o);
13890         if(options.callback){
13891             options.callback.call(options.scope || this, r, options, true);
13892         }
13893     },
13894
13895
13896     /**
13897      * Loads data from a passed data block. A Reader which understands the format of the data
13898      * must have been configured in the constructor.
13899      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13900      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13901      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13902      */
13903     loadData : function(o, append){
13904         var r = this.reader.readRecords(o);
13905         this.loadRecords(r, {add: append}, true);
13906     },
13907     
13908      /**
13909      * using 'cn' the nested child reader read the child array into it's child stores.
13910      * @param {Object} rec The record with a 'children array
13911      */
13912     loadDataFromChildren : function(rec)
13913     {
13914         this.loadData(this.reader.toLoadData(rec));
13915     },
13916     
13917
13918     /**
13919      * Gets the number of cached records.
13920      * <p>
13921      * <em>If using paging, this may not be the total size of the dataset. If the data object
13922      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13923      * the data set size</em>
13924      */
13925     getCount : function(){
13926         return this.data.length || 0;
13927     },
13928
13929     /**
13930      * Gets the total number of records in the dataset as returned by the server.
13931      * <p>
13932      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13933      * the dataset size</em>
13934      */
13935     getTotalCount : function(){
13936         return this.totalLength || 0;
13937     },
13938
13939     /**
13940      * Returns the sort state of the Store as an object with two properties:
13941      * <pre><code>
13942  field {String} The name of the field by which the Records are sorted
13943  direction {String} The sort order, "ASC" or "DESC"
13944      * </code></pre>
13945      */
13946     getSortState : function(){
13947         return this.sortInfo;
13948     },
13949
13950     // private
13951     applySort : function(){
13952         if(this.sortInfo && !this.remoteSort){
13953             var s = this.sortInfo, f = s.field;
13954             var st = this.fields.get(f).sortType;
13955             var fn = function(r1, r2){
13956                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13957                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13958             };
13959             this.data.sort(s.direction, fn);
13960             if(this.snapshot && this.snapshot != this.data){
13961                 this.snapshot.sort(s.direction, fn);
13962             }
13963         }
13964     },
13965
13966     /**
13967      * Sets the default sort column and order to be used by the next load operation.
13968      * @param {String} fieldName The name of the field to sort by.
13969      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13970      */
13971     setDefaultSort : function(field, dir){
13972         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13973     },
13974
13975     /**
13976      * Sort the Records.
13977      * If remote sorting is used, the sort is performed on the server, and the cache is
13978      * reloaded. If local sorting is used, the cache is sorted internally.
13979      * @param {String} fieldName The name of the field to sort by.
13980      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13981      */
13982     sort : function(fieldName, dir){
13983         var f = this.fields.get(fieldName);
13984         if(!dir){
13985             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13986             
13987             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13988                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13989             }else{
13990                 dir = f.sortDir;
13991             }
13992         }
13993         this.sortToggle[f.name] = dir;
13994         this.sortInfo = {field: f.name, direction: dir};
13995         if(!this.remoteSort){
13996             this.applySort();
13997             this.fireEvent("datachanged", this);
13998         }else{
13999             this.load(this.lastOptions);
14000         }
14001     },
14002
14003     /**
14004      * Calls the specified function for each of the Records in the cache.
14005      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14006      * Returning <em>false</em> aborts and exits the iteration.
14007      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14008      */
14009     each : function(fn, scope){
14010         this.data.each(fn, scope);
14011     },
14012
14013     /**
14014      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14015      * (e.g., during paging).
14016      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14017      */
14018     getModifiedRecords : function(){
14019         return this.modified;
14020     },
14021
14022     // private
14023     createFilterFn : function(property, value, anyMatch){
14024         if(!value.exec){ // not a regex
14025             value = String(value);
14026             if(value.length == 0){
14027                 return false;
14028             }
14029             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14030         }
14031         return function(r){
14032             return value.test(r.data[property]);
14033         };
14034     },
14035
14036     /**
14037      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14038      * @param {String} property A field on your records
14039      * @param {Number} start The record index to start at (defaults to 0)
14040      * @param {Number} end The last record index to include (defaults to length - 1)
14041      * @return {Number} The sum
14042      */
14043     sum : function(property, start, end){
14044         var rs = this.data.items, v = 0;
14045         start = start || 0;
14046         end = (end || end === 0) ? end : rs.length-1;
14047
14048         for(var i = start; i <= end; i++){
14049             v += (rs[i].data[property] || 0);
14050         }
14051         return v;
14052     },
14053
14054     /**
14055      * Filter the records by a specified property.
14056      * @param {String} field A field on your records
14057      * @param {String/RegExp} value Either a string that the field
14058      * should start with or a RegExp to test against the field
14059      * @param {Boolean} anyMatch True to match any part not just the beginning
14060      */
14061     filter : function(property, value, anyMatch){
14062         var fn = this.createFilterFn(property, value, anyMatch);
14063         return fn ? this.filterBy(fn) : this.clearFilter();
14064     },
14065
14066     /**
14067      * Filter by a function. The specified function will be called with each
14068      * record in this data source. If the function returns true the record is included,
14069      * otherwise it is filtered.
14070      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14071      * @param {Object} scope (optional) The scope of the function (defaults to this)
14072      */
14073     filterBy : function(fn, scope){
14074         this.snapshot = this.snapshot || this.data;
14075         this.data = this.queryBy(fn, scope||this);
14076         this.fireEvent("datachanged", this);
14077     },
14078
14079     /**
14080      * Query the records by a specified property.
14081      * @param {String} field A field on your records
14082      * @param {String/RegExp} value Either a string that the field
14083      * should start with or a RegExp to test against the field
14084      * @param {Boolean} anyMatch True to match any part not just the beginning
14085      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14086      */
14087     query : function(property, value, anyMatch){
14088         var fn = this.createFilterFn(property, value, anyMatch);
14089         return fn ? this.queryBy(fn) : this.data.clone();
14090     },
14091
14092     /**
14093      * Query by a function. The specified function will be called with each
14094      * record in this data source. If the function returns true the record is included
14095      * in the results.
14096      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14097      * @param {Object} scope (optional) The scope of the function (defaults to this)
14098       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14099      **/
14100     queryBy : function(fn, scope){
14101         var data = this.snapshot || this.data;
14102         return data.filterBy(fn, scope||this);
14103     },
14104
14105     /**
14106      * Collects unique values for a particular dataIndex from this store.
14107      * @param {String} dataIndex The property to collect
14108      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14109      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14110      * @return {Array} An array of the unique values
14111      **/
14112     collect : function(dataIndex, allowNull, bypassFilter){
14113         var d = (bypassFilter === true && this.snapshot) ?
14114                 this.snapshot.items : this.data.items;
14115         var v, sv, r = [], l = {};
14116         for(var i = 0, len = d.length; i < len; i++){
14117             v = d[i].data[dataIndex];
14118             sv = String(v);
14119             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14120                 l[sv] = true;
14121                 r[r.length] = v;
14122             }
14123         }
14124         return r;
14125     },
14126
14127     /**
14128      * Revert to a view of the Record cache with no filtering applied.
14129      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14130      */
14131     clearFilter : function(suppressEvent){
14132         if(this.snapshot && this.snapshot != this.data){
14133             this.data = this.snapshot;
14134             delete this.snapshot;
14135             if(suppressEvent !== true){
14136                 this.fireEvent("datachanged", this);
14137             }
14138         }
14139     },
14140
14141     // private
14142     afterEdit : function(record){
14143         if(this.modified.indexOf(record) == -1){
14144             this.modified.push(record);
14145         }
14146         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14147     },
14148     
14149     // private
14150     afterReject : function(record){
14151         this.modified.remove(record);
14152         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14153     },
14154
14155     // private
14156     afterCommit : function(record){
14157         this.modified.remove(record);
14158         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14159     },
14160
14161     /**
14162      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14163      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14164      */
14165     commitChanges : function(){
14166         var m = this.modified.slice(0);
14167         this.modified = [];
14168         for(var i = 0, len = m.length; i < len; i++){
14169             m[i].commit();
14170         }
14171     },
14172
14173     /**
14174      * Cancel outstanding changes on all changed records.
14175      */
14176     rejectChanges : function(){
14177         var m = this.modified.slice(0);
14178         this.modified = [];
14179         for(var i = 0, len = m.length; i < len; i++){
14180             m[i].reject();
14181         }
14182     },
14183
14184     onMetaChange : function(meta, rtype, o){
14185         this.recordType = rtype;
14186         this.fields = rtype.prototype.fields;
14187         delete this.snapshot;
14188         this.sortInfo = meta.sortInfo || this.sortInfo;
14189         this.modified = [];
14190         this.fireEvent('metachange', this, this.reader.meta);
14191     },
14192     
14193     moveIndex : function(data, type)
14194     {
14195         var index = this.indexOf(data);
14196         
14197         var newIndex = index + type;
14198         
14199         this.remove(data);
14200         
14201         this.insert(newIndex, data);
14202         
14203     }
14204 });/*
14205  * Based on:
14206  * Ext JS Library 1.1.1
14207  * Copyright(c) 2006-2007, Ext JS, LLC.
14208  *
14209  * Originally Released Under LGPL - original licence link has changed is not relivant.
14210  *
14211  * Fork - LGPL
14212  * <script type="text/javascript">
14213  */
14214
14215 /**
14216  * @class Roo.data.SimpleStore
14217  * @extends Roo.data.Store
14218  * Small helper class to make creating Stores from Array data easier.
14219  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14220  * @cfg {Array} fields An array of field definition objects, or field name strings.
14221  * @cfg {Object} an existing reader (eg. copied from another store)
14222  * @cfg {Array} data The multi-dimensional array of data
14223  * @constructor
14224  * @param {Object} config
14225  */
14226 Roo.data.SimpleStore = function(config)
14227 {
14228     Roo.data.SimpleStore.superclass.constructor.call(this, {
14229         isLocal : true,
14230         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14231                 id: config.id
14232             },
14233             Roo.data.Record.create(config.fields)
14234         ),
14235         proxy : new Roo.data.MemoryProxy(config.data)
14236     });
14237     this.load();
14238 };
14239 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14240  * Based on:
14241  * Ext JS Library 1.1.1
14242  * Copyright(c) 2006-2007, Ext JS, LLC.
14243  *
14244  * Originally Released Under LGPL - original licence link has changed is not relivant.
14245  *
14246  * Fork - LGPL
14247  * <script type="text/javascript">
14248  */
14249
14250 /**
14251 /**
14252  * @extends Roo.data.Store
14253  * @class Roo.data.JsonStore
14254  * Small helper class to make creating Stores for JSON data easier. <br/>
14255 <pre><code>
14256 var store = new Roo.data.JsonStore({
14257     url: 'get-images.php',
14258     root: 'images',
14259     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14260 });
14261 </code></pre>
14262  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14263  * JsonReader and HttpProxy (unless inline data is provided).</b>
14264  * @cfg {Array} fields An array of field definition objects, or field name strings.
14265  * @constructor
14266  * @param {Object} config
14267  */
14268 Roo.data.JsonStore = function(c){
14269     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14270         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14271         reader: new Roo.data.JsonReader(c, c.fields)
14272     }));
14273 };
14274 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14275  * Based on:
14276  * Ext JS Library 1.1.1
14277  * Copyright(c) 2006-2007, Ext JS, LLC.
14278  *
14279  * Originally Released Under LGPL - original licence link has changed is not relivant.
14280  *
14281  * Fork - LGPL
14282  * <script type="text/javascript">
14283  */
14284
14285  
14286 Roo.data.Field = function(config){
14287     if(typeof config == "string"){
14288         config = {name: config};
14289     }
14290     Roo.apply(this, config);
14291     
14292     if(!this.type){
14293         this.type = "auto";
14294     }
14295     
14296     var st = Roo.data.SortTypes;
14297     // named sortTypes are supported, here we look them up
14298     if(typeof this.sortType == "string"){
14299         this.sortType = st[this.sortType];
14300     }
14301     
14302     // set default sortType for strings and dates
14303     if(!this.sortType){
14304         switch(this.type){
14305             case "string":
14306                 this.sortType = st.asUCString;
14307                 break;
14308             case "date":
14309                 this.sortType = st.asDate;
14310                 break;
14311             default:
14312                 this.sortType = st.none;
14313         }
14314     }
14315
14316     // define once
14317     var stripRe = /[\$,%]/g;
14318
14319     // prebuilt conversion function for this field, instead of
14320     // switching every time we're reading a value
14321     if(!this.convert){
14322         var cv, dateFormat = this.dateFormat;
14323         switch(this.type){
14324             case "":
14325             case "auto":
14326             case undefined:
14327                 cv = function(v){ return v; };
14328                 break;
14329             case "string":
14330                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14331                 break;
14332             case "int":
14333                 cv = function(v){
14334                     return v !== undefined && v !== null && v !== '' ?
14335                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14336                     };
14337                 break;
14338             case "float":
14339                 cv = function(v){
14340                     return v !== undefined && v !== null && v !== '' ?
14341                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14342                     };
14343                 break;
14344             case "bool":
14345             case "boolean":
14346                 cv = function(v){ return v === true || v === "true" || v == 1; };
14347                 break;
14348             case "date":
14349                 cv = function(v){
14350                     if(!v){
14351                         return '';
14352                     }
14353                     if(v instanceof Date){
14354                         return v;
14355                     }
14356                     if(dateFormat){
14357                         if(dateFormat == "timestamp"){
14358                             return new Date(v*1000);
14359                         }
14360                         return Date.parseDate(v, dateFormat);
14361                     }
14362                     var parsed = Date.parse(v);
14363                     return parsed ? new Date(parsed) : null;
14364                 };
14365              break;
14366             
14367         }
14368         this.convert = cv;
14369     }
14370 };
14371
14372 Roo.data.Field.prototype = {
14373     dateFormat: null,
14374     defaultValue: "",
14375     mapping: null,
14376     sortType : null,
14377     sortDir : "ASC"
14378 };/*
14379  * Based on:
14380  * Ext JS Library 1.1.1
14381  * Copyright(c) 2006-2007, Ext JS, LLC.
14382  *
14383  * Originally Released Under LGPL - original licence link has changed is not relivant.
14384  *
14385  * Fork - LGPL
14386  * <script type="text/javascript">
14387  */
14388  
14389 // Base class for reading structured data from a data source.  This class is intended to be
14390 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14391
14392 /**
14393  * @class Roo.data.DataReader
14394  * Base class for reading structured data from a data source.  This class is intended to be
14395  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14396  */
14397
14398 Roo.data.DataReader = function(meta, recordType){
14399     
14400     this.meta = meta;
14401     
14402     this.recordType = recordType instanceof Array ? 
14403         Roo.data.Record.create(recordType) : recordType;
14404 };
14405
14406 Roo.data.DataReader.prototype = {
14407     
14408     
14409     readerType : 'Data',
14410      /**
14411      * Create an empty record
14412      * @param {Object} data (optional) - overlay some values
14413      * @return {Roo.data.Record} record created.
14414      */
14415     newRow :  function(d) {
14416         var da =  {};
14417         this.recordType.prototype.fields.each(function(c) {
14418             switch( c.type) {
14419                 case 'int' : da[c.name] = 0; break;
14420                 case 'date' : da[c.name] = new Date(); break;
14421                 case 'float' : da[c.name] = 0.0; break;
14422                 case 'boolean' : da[c.name] = false; break;
14423                 default : da[c.name] = ""; break;
14424             }
14425             
14426         });
14427         return new this.recordType(Roo.apply(da, d));
14428     }
14429     
14430     
14431 };/*
14432  * Based on:
14433  * Ext JS Library 1.1.1
14434  * Copyright(c) 2006-2007, Ext JS, LLC.
14435  *
14436  * Originally Released Under LGPL - original licence link has changed is not relivant.
14437  *
14438  * Fork - LGPL
14439  * <script type="text/javascript">
14440  */
14441
14442 /**
14443  * @class Roo.data.DataProxy
14444  * @extends Roo.data.Observable
14445  * This class is an abstract base class for implementations which provide retrieval of
14446  * unformatted data objects.<br>
14447  * <p>
14448  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14449  * (of the appropriate type which knows how to parse the data object) to provide a block of
14450  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14451  * <p>
14452  * Custom implementations must implement the load method as described in
14453  * {@link Roo.data.HttpProxy#load}.
14454  */
14455 Roo.data.DataProxy = function(){
14456     this.addEvents({
14457         /**
14458          * @event beforeload
14459          * Fires before a network request is made to retrieve a data object.
14460          * @param {Object} This DataProxy object.
14461          * @param {Object} params The params parameter to the load function.
14462          */
14463         beforeload : true,
14464         /**
14465          * @event load
14466          * Fires before the load method's callback is called.
14467          * @param {Object} This DataProxy object.
14468          * @param {Object} o The data object.
14469          * @param {Object} arg The callback argument object passed to the load function.
14470          */
14471         load : true,
14472         /**
14473          * @event loadexception
14474          * Fires if an Exception occurs during data retrieval.
14475          * @param {Object} This DataProxy object.
14476          * @param {Object} o The data object.
14477          * @param {Object} arg The callback argument object passed to the load function.
14478          * @param {Object} e The Exception.
14479          */
14480         loadexception : true
14481     });
14482     Roo.data.DataProxy.superclass.constructor.call(this);
14483 };
14484
14485 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14486
14487     /**
14488      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14489      */
14490 /*
14491  * Based on:
14492  * Ext JS Library 1.1.1
14493  * Copyright(c) 2006-2007, Ext JS, LLC.
14494  *
14495  * Originally Released Under LGPL - original licence link has changed is not relivant.
14496  *
14497  * Fork - LGPL
14498  * <script type="text/javascript">
14499  */
14500 /**
14501  * @class Roo.data.MemoryProxy
14502  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14503  * to the Reader when its load method is called.
14504  * @constructor
14505  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14506  */
14507 Roo.data.MemoryProxy = function(data){
14508     if (data.data) {
14509         data = data.data;
14510     }
14511     Roo.data.MemoryProxy.superclass.constructor.call(this);
14512     this.data = data;
14513 };
14514
14515 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14516     
14517     /**
14518      * Load data from the requested source (in this case an in-memory
14519      * data object passed to the constructor), read the data object into
14520      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14521      * process that block using the passed callback.
14522      * @param {Object} params This parameter is not used by the MemoryProxy class.
14523      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14524      * object into a block of Roo.data.Records.
14525      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14526      * The function must be passed <ul>
14527      * <li>The Record block object</li>
14528      * <li>The "arg" argument from the load function</li>
14529      * <li>A boolean success indicator</li>
14530      * </ul>
14531      * @param {Object} scope The scope in which to call the callback
14532      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14533      */
14534     load : function(params, reader, callback, scope, arg){
14535         params = params || {};
14536         var result;
14537         try {
14538             result = reader.readRecords(params.data ? params.data :this.data);
14539         }catch(e){
14540             this.fireEvent("loadexception", this, arg, null, e);
14541             callback.call(scope, null, arg, false);
14542             return;
14543         }
14544         callback.call(scope, result, arg, true);
14545     },
14546     
14547     // private
14548     update : function(params, records){
14549         
14550     }
14551 });/*
14552  * Based on:
14553  * Ext JS Library 1.1.1
14554  * Copyright(c) 2006-2007, Ext JS, LLC.
14555  *
14556  * Originally Released Under LGPL - original licence link has changed is not relivant.
14557  *
14558  * Fork - LGPL
14559  * <script type="text/javascript">
14560  */
14561 /**
14562  * @class Roo.data.HttpProxy
14563  * @extends Roo.data.DataProxy
14564  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14565  * configured to reference a certain URL.<br><br>
14566  * <p>
14567  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14568  * from which the running page was served.<br><br>
14569  * <p>
14570  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14571  * <p>
14572  * Be aware that to enable the browser to parse an XML document, the server must set
14573  * the Content-Type header in the HTTP response to "text/xml".
14574  * @constructor
14575  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14576  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14577  * will be used to make the request.
14578  */
14579 Roo.data.HttpProxy = function(conn){
14580     Roo.data.HttpProxy.superclass.constructor.call(this);
14581     // is conn a conn config or a real conn?
14582     this.conn = conn;
14583     this.useAjax = !conn || !conn.events;
14584   
14585 };
14586
14587 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14588     // thse are take from connection...
14589     
14590     /**
14591      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14592      */
14593     /**
14594      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14595      * extra parameters to each request made by this object. (defaults to undefined)
14596      */
14597     /**
14598      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14599      *  to each request made by this object. (defaults to undefined)
14600      */
14601     /**
14602      * @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)
14603      */
14604     /**
14605      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14606      */
14607      /**
14608      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14609      * @type Boolean
14610      */
14611   
14612
14613     /**
14614      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14615      * @type Boolean
14616      */
14617     /**
14618      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14619      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14620      * a finer-grained basis than the DataProxy events.
14621      */
14622     getConnection : function(){
14623         return this.useAjax ? Roo.Ajax : this.conn;
14624     },
14625
14626     /**
14627      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14628      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14629      * process that block using the passed callback.
14630      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14631      * for the request to the remote server.
14632      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14633      * object into a block of Roo.data.Records.
14634      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14635      * The function must be passed <ul>
14636      * <li>The Record block object</li>
14637      * <li>The "arg" argument from the load function</li>
14638      * <li>A boolean success indicator</li>
14639      * </ul>
14640      * @param {Object} scope The scope in which to call the callback
14641      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14642      */
14643     load : function(params, reader, callback, scope, arg){
14644         if(this.fireEvent("beforeload", this, params) !== false){
14645             var  o = {
14646                 params : params || {},
14647                 request: {
14648                     callback : callback,
14649                     scope : scope,
14650                     arg : arg
14651                 },
14652                 reader: reader,
14653                 callback : this.loadResponse,
14654                 scope: this
14655             };
14656             if(this.useAjax){
14657                 Roo.applyIf(o, this.conn);
14658                 if(this.activeRequest){
14659                     Roo.Ajax.abort(this.activeRequest);
14660                 }
14661                 this.activeRequest = Roo.Ajax.request(o);
14662             }else{
14663                 this.conn.request(o);
14664             }
14665         }else{
14666             callback.call(scope||this, null, arg, false);
14667         }
14668     },
14669
14670     // private
14671     loadResponse : function(o, success, response){
14672         delete this.activeRequest;
14673         if(!success){
14674             this.fireEvent("loadexception", this, o, response);
14675             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14676             return;
14677         }
14678         var result;
14679         try {
14680             result = o.reader.read(response);
14681         }catch(e){
14682             this.fireEvent("loadexception", this, o, response, e);
14683             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14684             return;
14685         }
14686         
14687         this.fireEvent("load", this, o, o.request.arg);
14688         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14689     },
14690
14691     // private
14692     update : function(dataSet){
14693
14694     },
14695
14696     // private
14697     updateResponse : function(dataSet){
14698
14699     }
14700 });/*
14701  * Based on:
14702  * Ext JS Library 1.1.1
14703  * Copyright(c) 2006-2007, Ext JS, LLC.
14704  *
14705  * Originally Released Under LGPL - original licence link has changed is not relivant.
14706  *
14707  * Fork - LGPL
14708  * <script type="text/javascript">
14709  */
14710
14711 /**
14712  * @class Roo.data.ScriptTagProxy
14713  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14714  * other than the originating domain of the running page.<br><br>
14715  * <p>
14716  * <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
14717  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14718  * <p>
14719  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14720  * source code that is used as the source inside a &lt;script> tag.<br><br>
14721  * <p>
14722  * In order for the browser to process the returned data, the server must wrap the data object
14723  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14724  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14725  * depending on whether the callback name was passed:
14726  * <p>
14727  * <pre><code>
14728 boolean scriptTag = false;
14729 String cb = request.getParameter("callback");
14730 if (cb != null) {
14731     scriptTag = true;
14732     response.setContentType("text/javascript");
14733 } else {
14734     response.setContentType("application/x-json");
14735 }
14736 Writer out = response.getWriter();
14737 if (scriptTag) {
14738     out.write(cb + "(");
14739 }
14740 out.print(dataBlock.toJsonString());
14741 if (scriptTag) {
14742     out.write(");");
14743 }
14744 </pre></code>
14745  *
14746  * @constructor
14747  * @param {Object} config A configuration object.
14748  */
14749 Roo.data.ScriptTagProxy = function(config){
14750     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14751     Roo.apply(this, config);
14752     this.head = document.getElementsByTagName("head")[0];
14753 };
14754
14755 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14756
14757 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14758     /**
14759      * @cfg {String} url The URL from which to request the data object.
14760      */
14761     /**
14762      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14763      */
14764     timeout : 30000,
14765     /**
14766      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14767      * the server the name of the callback function set up by the load call to process the returned data object.
14768      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14769      * javascript output which calls this named function passing the data object as its only parameter.
14770      */
14771     callbackParam : "callback",
14772     /**
14773      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14774      * name to the request.
14775      */
14776     nocache : true,
14777
14778     /**
14779      * Load data from the configured URL, read the data object into
14780      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14781      * process that block using the passed callback.
14782      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14783      * for the request to the remote server.
14784      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14785      * object into a block of Roo.data.Records.
14786      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14787      * The function must be passed <ul>
14788      * <li>The Record block object</li>
14789      * <li>The "arg" argument from the load function</li>
14790      * <li>A boolean success indicator</li>
14791      * </ul>
14792      * @param {Object} scope The scope in which to call the callback
14793      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14794      */
14795     load : function(params, reader, callback, scope, arg){
14796         if(this.fireEvent("beforeload", this, params) !== false){
14797
14798             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14799
14800             var url = this.url;
14801             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14802             if(this.nocache){
14803                 url += "&_dc=" + (new Date().getTime());
14804             }
14805             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14806             var trans = {
14807                 id : transId,
14808                 cb : "stcCallback"+transId,
14809                 scriptId : "stcScript"+transId,
14810                 params : params,
14811                 arg : arg,
14812                 url : url,
14813                 callback : callback,
14814                 scope : scope,
14815                 reader : reader
14816             };
14817             var conn = this;
14818
14819             window[trans.cb] = function(o){
14820                 conn.handleResponse(o, trans);
14821             };
14822
14823             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14824
14825             if(this.autoAbort !== false){
14826                 this.abort();
14827             }
14828
14829             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14830
14831             var script = document.createElement("script");
14832             script.setAttribute("src", url);
14833             script.setAttribute("type", "text/javascript");
14834             script.setAttribute("id", trans.scriptId);
14835             this.head.appendChild(script);
14836
14837             this.trans = trans;
14838         }else{
14839             callback.call(scope||this, null, arg, false);
14840         }
14841     },
14842
14843     // private
14844     isLoading : function(){
14845         return this.trans ? true : false;
14846     },
14847
14848     /**
14849      * Abort the current server request.
14850      */
14851     abort : function(){
14852         if(this.isLoading()){
14853             this.destroyTrans(this.trans);
14854         }
14855     },
14856
14857     // private
14858     destroyTrans : function(trans, isLoaded){
14859         this.head.removeChild(document.getElementById(trans.scriptId));
14860         clearTimeout(trans.timeoutId);
14861         if(isLoaded){
14862             window[trans.cb] = undefined;
14863             try{
14864                 delete window[trans.cb];
14865             }catch(e){}
14866         }else{
14867             // if hasn't been loaded, wait for load to remove it to prevent script error
14868             window[trans.cb] = function(){
14869                 window[trans.cb] = undefined;
14870                 try{
14871                     delete window[trans.cb];
14872                 }catch(e){}
14873             };
14874         }
14875     },
14876
14877     // private
14878     handleResponse : function(o, trans){
14879         this.trans = false;
14880         this.destroyTrans(trans, true);
14881         var result;
14882         try {
14883             result = trans.reader.readRecords(o);
14884         }catch(e){
14885             this.fireEvent("loadexception", this, o, trans.arg, e);
14886             trans.callback.call(trans.scope||window, null, trans.arg, false);
14887             return;
14888         }
14889         this.fireEvent("load", this, o, trans.arg);
14890         trans.callback.call(trans.scope||window, result, trans.arg, true);
14891     },
14892
14893     // private
14894     handleFailure : function(trans){
14895         this.trans = false;
14896         this.destroyTrans(trans, false);
14897         this.fireEvent("loadexception", this, null, trans.arg);
14898         trans.callback.call(trans.scope||window, null, trans.arg, false);
14899     }
14900 });/*
14901  * Based on:
14902  * Ext JS Library 1.1.1
14903  * Copyright(c) 2006-2007, Ext JS, LLC.
14904  *
14905  * Originally Released Under LGPL - original licence link has changed is not relivant.
14906  *
14907  * Fork - LGPL
14908  * <script type="text/javascript">
14909  */
14910
14911 /**
14912  * @class Roo.data.JsonReader
14913  * @extends Roo.data.DataReader
14914  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14915  * based on mappings in a provided Roo.data.Record constructor.
14916  * 
14917  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14918  * in the reply previously. 
14919  * 
14920  * <p>
14921  * Example code:
14922  * <pre><code>
14923 var RecordDef = Roo.data.Record.create([
14924     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14925     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14926 ]);
14927 var myReader = new Roo.data.JsonReader({
14928     totalProperty: "results",    // The property which contains the total dataset size (optional)
14929     root: "rows",                // The property which contains an Array of row objects
14930     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14931 }, RecordDef);
14932 </code></pre>
14933  * <p>
14934  * This would consume a JSON file like this:
14935  * <pre><code>
14936 { 'results': 2, 'rows': [
14937     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14938     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14939 }
14940 </code></pre>
14941  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14942  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14943  * paged from the remote server.
14944  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14945  * @cfg {String} root name of the property which contains the Array of row objects.
14946  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14947  * @cfg {Array} fields Array of field definition objects
14948  * @constructor
14949  * Create a new JsonReader
14950  * @param {Object} meta Metadata configuration options
14951  * @param {Object} recordType Either an Array of field definition objects,
14952  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14953  */
14954 Roo.data.JsonReader = function(meta, recordType){
14955     
14956     meta = meta || {};
14957     // set some defaults:
14958     Roo.applyIf(meta, {
14959         totalProperty: 'total',
14960         successProperty : 'success',
14961         root : 'data',
14962         id : 'id'
14963     });
14964     
14965     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14966 };
14967 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14968     
14969     readerType : 'Json',
14970     
14971     /**
14972      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14973      * Used by Store query builder to append _requestMeta to params.
14974      * 
14975      */
14976     metaFromRemote : false,
14977     /**
14978      * This method is only used by a DataProxy which has retrieved data from a remote server.
14979      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14980      * @return {Object} data A data block which is used by an Roo.data.Store object as
14981      * a cache of Roo.data.Records.
14982      */
14983     read : function(response){
14984         var json = response.responseText;
14985        
14986         var o = /* eval:var:o */ eval("("+json+")");
14987         if(!o) {
14988             throw {message: "JsonReader.read: Json object not found"};
14989         }
14990         
14991         if(o.metaData){
14992             
14993             delete this.ef;
14994             this.metaFromRemote = true;
14995             this.meta = o.metaData;
14996             this.recordType = Roo.data.Record.create(o.metaData.fields);
14997             this.onMetaChange(this.meta, this.recordType, o);
14998         }
14999         return this.readRecords(o);
15000     },
15001
15002     // private function a store will implement
15003     onMetaChange : function(meta, recordType, o){
15004
15005     },
15006
15007     /**
15008          * @ignore
15009          */
15010     simpleAccess: function(obj, subsc) {
15011         return obj[subsc];
15012     },
15013
15014         /**
15015          * @ignore
15016          */
15017     getJsonAccessor: function(){
15018         var re = /[\[\.]/;
15019         return function(expr) {
15020             try {
15021                 return(re.test(expr))
15022                     ? new Function("obj", "return obj." + expr)
15023                     : function(obj){
15024                         return obj[expr];
15025                     };
15026             } catch(e){}
15027             return Roo.emptyFn;
15028         };
15029     }(),
15030
15031     /**
15032      * Create a data block containing Roo.data.Records from an XML document.
15033      * @param {Object} o An object which contains an Array of row objects in the property specified
15034      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15035      * which contains the total size of the dataset.
15036      * @return {Object} data A data block which is used by an Roo.data.Store object as
15037      * a cache of Roo.data.Records.
15038      */
15039     readRecords : function(o){
15040         /**
15041          * After any data loads, the raw JSON data is available for further custom processing.
15042          * @type Object
15043          */
15044         this.o = o;
15045         var s = this.meta, Record = this.recordType,
15046             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15047
15048 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15049         if (!this.ef) {
15050             if(s.totalProperty) {
15051                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15052                 }
15053                 if(s.successProperty) {
15054                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15055                 }
15056                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15057                 if (s.id) {
15058                         var g = this.getJsonAccessor(s.id);
15059                         this.getId = function(rec) {
15060                                 var r = g(rec);  
15061                                 return (r === undefined || r === "") ? null : r;
15062                         };
15063                 } else {
15064                         this.getId = function(){return null;};
15065                 }
15066             this.ef = [];
15067             for(var jj = 0; jj < fl; jj++){
15068                 f = fi[jj];
15069                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15070                 this.ef[jj] = this.getJsonAccessor(map);
15071             }
15072         }
15073
15074         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15075         if(s.totalProperty){
15076             var vt = parseInt(this.getTotal(o), 10);
15077             if(!isNaN(vt)){
15078                 totalRecords = vt;
15079             }
15080         }
15081         if(s.successProperty){
15082             var vs = this.getSuccess(o);
15083             if(vs === false || vs === 'false'){
15084                 success = false;
15085             }
15086         }
15087         var records = [];
15088         for(var i = 0; i < c; i++){
15089                 var n = root[i];
15090             var values = {};
15091             var id = this.getId(n);
15092             for(var j = 0; j < fl; j++){
15093                 f = fi[j];
15094             var v = this.ef[j](n);
15095             if (!f.convert) {
15096                 Roo.log('missing convert for ' + f.name);
15097                 Roo.log(f);
15098                 continue;
15099             }
15100             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15101             }
15102             var record = new Record(values, id);
15103             record.json = n;
15104             records[i] = record;
15105         }
15106         return {
15107             raw : o,
15108             success : success,
15109             records : records,
15110             totalRecords : totalRecords
15111         };
15112     },
15113     // used when loading children.. @see loadDataFromChildren
15114     toLoadData: function(rec)
15115     {
15116         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15117         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15118         return { data : data, total : data.length };
15119         
15120     }
15121 });/*
15122  * Based on:
15123  * Ext JS Library 1.1.1
15124  * Copyright(c) 2006-2007, Ext JS, LLC.
15125  *
15126  * Originally Released Under LGPL - original licence link has changed is not relivant.
15127  *
15128  * Fork - LGPL
15129  * <script type="text/javascript">
15130  */
15131
15132 /**
15133  * @class Roo.data.ArrayReader
15134  * @extends Roo.data.DataReader
15135  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15136  * Each element of that Array represents a row of data fields. The
15137  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15138  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15139  * <p>
15140  * Example code:.
15141  * <pre><code>
15142 var RecordDef = Roo.data.Record.create([
15143     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15144     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15145 ]);
15146 var myReader = new Roo.data.ArrayReader({
15147     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15148 }, RecordDef);
15149 </code></pre>
15150  * <p>
15151  * This would consume an Array like this:
15152  * <pre><code>
15153 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15154   </code></pre>
15155  
15156  * @constructor
15157  * Create a new JsonReader
15158  * @param {Object} meta Metadata configuration options.
15159  * @param {Object|Array} recordType Either an Array of field definition objects
15160  * 
15161  * @cfg {Array} fields Array of field definition objects
15162  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15163  * as specified to {@link Roo.data.Record#create},
15164  * or an {@link Roo.data.Record} object
15165  *
15166  * 
15167  * created using {@link Roo.data.Record#create}.
15168  */
15169 Roo.data.ArrayReader = function(meta, recordType)
15170 {    
15171     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15172 };
15173
15174 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15175     
15176       /**
15177      * Create a data block containing Roo.data.Records from an XML document.
15178      * @param {Object} o An Array of row objects which represents the dataset.
15179      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15180      * a cache of Roo.data.Records.
15181      */
15182     readRecords : function(o)
15183     {
15184         var sid = this.meta ? this.meta.id : null;
15185         var recordType = this.recordType, fields = recordType.prototype.fields;
15186         var records = [];
15187         var root = o;
15188         for(var i = 0; i < root.length; i++){
15189             var n = root[i];
15190             var values = {};
15191             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15192             for(var j = 0, jlen = fields.length; j < jlen; j++){
15193                 var f = fields.items[j];
15194                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15195                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15196                 v = f.convert(v);
15197                 values[f.name] = v;
15198             }
15199             var record = new recordType(values, id);
15200             record.json = n;
15201             records[records.length] = record;
15202         }
15203         return {
15204             records : records,
15205             totalRecords : records.length
15206         };
15207     },
15208     // used when loading children.. @see loadDataFromChildren
15209     toLoadData: function(rec)
15210     {
15211         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15212         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15213         
15214     }
15215     
15216     
15217 });/*
15218  * - LGPL
15219  * * 
15220  */
15221
15222 /**
15223  * @class Roo.bootstrap.ComboBox
15224  * @extends Roo.bootstrap.TriggerField
15225  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15226  * @cfg {Boolean} append (true|false) default false
15227  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15228  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15229  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15230  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15231  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15232  * @cfg {Boolean} animate default true
15233  * @cfg {Boolean} emptyResultText only for touch device
15234  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15235  * @cfg {String} emptyTitle default ''
15236  * @cfg {Number} width fixed with? experimental
15237  * @constructor
15238  * Create a new ComboBox.
15239  * @param {Object} config Configuration options
15240  */
15241 Roo.bootstrap.ComboBox = function(config){
15242     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15243     this.addEvents({
15244         /**
15245          * @event expand
15246          * Fires when the dropdown list is expanded
15247         * @param {Roo.bootstrap.ComboBox} combo This combo box
15248         */
15249         'expand' : true,
15250         /**
15251          * @event collapse
15252          * Fires when the dropdown list is collapsed
15253         * @param {Roo.bootstrap.ComboBox} combo This combo box
15254         */
15255         'collapse' : true,
15256         /**
15257          * @event beforeselect
15258          * Fires before a list item is selected. Return false to cancel the selection.
15259         * @param {Roo.bootstrap.ComboBox} combo This combo box
15260         * @param {Roo.data.Record} record The data record returned from the underlying store
15261         * @param {Number} index The index of the selected item in the dropdown list
15262         */
15263         'beforeselect' : true,
15264         /**
15265          * @event select
15266          * Fires when a list item is selected
15267         * @param {Roo.bootstrap.ComboBox} combo This combo box
15268         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15269         * @param {Number} index The index of the selected item in the dropdown list
15270         */
15271         'select' : true,
15272         /**
15273          * @event beforequery
15274          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15275          * The event object passed has these properties:
15276         * @param {Roo.bootstrap.ComboBox} combo This combo box
15277         * @param {String} query The query
15278         * @param {Boolean} forceAll true to force "all" query
15279         * @param {Boolean} cancel true to cancel the query
15280         * @param {Object} e The query event object
15281         */
15282         'beforequery': true,
15283          /**
15284          * @event add
15285          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15286         * @param {Roo.bootstrap.ComboBox} combo This combo box
15287         */
15288         'add' : true,
15289         /**
15290          * @event edit
15291          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15292         * @param {Roo.bootstrap.ComboBox} combo This combo box
15293         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15294         */
15295         'edit' : true,
15296         /**
15297          * @event remove
15298          * Fires when the remove value from the combobox array
15299         * @param {Roo.bootstrap.ComboBox} combo This combo box
15300         */
15301         'remove' : true,
15302         /**
15303          * @event afterremove
15304          * Fires when the remove value from the combobox array
15305         * @param {Roo.bootstrap.ComboBox} combo This combo box
15306         */
15307         'afterremove' : true,
15308         /**
15309          * @event specialfilter
15310          * Fires when specialfilter
15311             * @param {Roo.bootstrap.ComboBox} combo This combo box
15312             */
15313         'specialfilter' : true,
15314         /**
15315          * @event tick
15316          * Fires when tick the element
15317             * @param {Roo.bootstrap.ComboBox} combo This combo box
15318             */
15319         'tick' : true,
15320         /**
15321          * @event touchviewdisplay
15322          * Fires when touch view require special display (default is using displayField)
15323             * @param {Roo.bootstrap.ComboBox} combo This combo box
15324             * @param {Object} cfg set html .
15325             */
15326         'touchviewdisplay' : true
15327         
15328     });
15329     
15330     this.item = [];
15331     this.tickItems = [];
15332     
15333     this.selectedIndex = -1;
15334     if(this.mode == 'local'){
15335         if(config.queryDelay === undefined){
15336             this.queryDelay = 10;
15337         }
15338         if(config.minChars === undefined){
15339             this.minChars = 0;
15340         }
15341     }
15342 };
15343
15344 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15345      
15346     /**
15347      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15348      * rendering into an Roo.Editor, defaults to false)
15349      */
15350     /**
15351      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15352      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15353      */
15354     /**
15355      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15356      */
15357     /**
15358      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15359      * the dropdown list (defaults to undefined, with no header element)
15360      */
15361
15362      /**
15363      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15364      */
15365      
15366      /**
15367      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15368      */
15369     listWidth: undefined,
15370     /**
15371      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15372      * mode = 'remote' or 'text' if mode = 'local')
15373      */
15374     displayField: undefined,
15375     
15376     /**
15377      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15378      * mode = 'remote' or 'value' if mode = 'local'). 
15379      * Note: use of a valueField requires the user make a selection
15380      * in order for a value to be mapped.
15381      */
15382     valueField: undefined,
15383     /**
15384      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15385      */
15386     modalTitle : '',
15387     
15388     /**
15389      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15390      * field's data value (defaults to the underlying DOM element's name)
15391      */
15392     hiddenName: undefined,
15393     /**
15394      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15395      */
15396     listClass: '',
15397     /**
15398      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15399      */
15400     selectedClass: 'active',
15401     
15402     /**
15403      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15404      */
15405     shadow:'sides',
15406     /**
15407      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15408      * anchor positions (defaults to 'tl-bl')
15409      */
15410     listAlign: 'tl-bl?',
15411     /**
15412      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15413      */
15414     maxHeight: 300,
15415     /**
15416      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15417      * query specified by the allQuery config option (defaults to 'query')
15418      */
15419     triggerAction: 'query',
15420     /**
15421      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15422      * (defaults to 4, does not apply if editable = false)
15423      */
15424     minChars : 4,
15425     /**
15426      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15427      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15428      */
15429     typeAhead: false,
15430     /**
15431      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15432      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15433      */
15434     queryDelay: 500,
15435     /**
15436      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15437      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15438      */
15439     pageSize: 0,
15440     /**
15441      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15442      * when editable = true (defaults to false)
15443      */
15444     selectOnFocus:false,
15445     /**
15446      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15447      */
15448     queryParam: 'query',
15449     /**
15450      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15451      * when mode = 'remote' (defaults to 'Loading...')
15452      */
15453     loadingText: 'Loading...',
15454     /**
15455      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15456      */
15457     resizable: false,
15458     /**
15459      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15460      */
15461     handleHeight : 8,
15462     /**
15463      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15464      * traditional select (defaults to true)
15465      */
15466     editable: true,
15467     /**
15468      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15469      */
15470     allQuery: '',
15471     /**
15472      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15473      */
15474     mode: 'remote',
15475     /**
15476      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15477      * listWidth has a higher value)
15478      */
15479     minListWidth : 70,
15480     /**
15481      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15482      * allow the user to set arbitrary text into the field (defaults to false)
15483      */
15484     forceSelection:false,
15485     /**
15486      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15487      * if typeAhead = true (defaults to 250)
15488      */
15489     typeAheadDelay : 250,
15490     /**
15491      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15492      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15493      */
15494     valueNotFoundText : undefined,
15495     /**
15496      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15497      */
15498     blockFocus : false,
15499     
15500     /**
15501      * @cfg {Boolean} disableClear Disable showing of clear button.
15502      */
15503     disableClear : false,
15504     /**
15505      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15506      */
15507     alwaysQuery : false,
15508     
15509     /**
15510      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15511      */
15512     multiple : false,
15513     
15514     /**
15515      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15516      */
15517     invalidClass : "has-warning",
15518     
15519     /**
15520      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15521      */
15522     validClass : "has-success",
15523     
15524     /**
15525      * @cfg {Boolean} specialFilter (true|false) special filter default false
15526      */
15527     specialFilter : false,
15528     
15529     /**
15530      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15531      */
15532     mobileTouchView : true,
15533     
15534     /**
15535      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15536      */
15537     useNativeIOS : false,
15538     
15539     /**
15540      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15541      */
15542     mobile_restrict_height : false,
15543     
15544     ios_options : false,
15545     
15546     //private
15547     addicon : false,
15548     editicon: false,
15549     
15550     page: 0,
15551     hasQuery: false,
15552     append: false,
15553     loadNext: false,
15554     autoFocus : true,
15555     tickable : false,
15556     btnPosition : 'right',
15557     triggerList : true,
15558     showToggleBtn : true,
15559     animate : true,
15560     emptyResultText: 'Empty',
15561     triggerText : 'Select',
15562     emptyTitle : '',
15563     width : false,
15564     
15565     // element that contains real text value.. (when hidden is used..)
15566     
15567     getAutoCreate : function()
15568     {   
15569         var cfg = false;
15570         //render
15571         /*
15572          * Render classic select for iso
15573          */
15574         
15575         if(Roo.isIOS && this.useNativeIOS){
15576             cfg = this.getAutoCreateNativeIOS();
15577             return cfg;
15578         }
15579         
15580         /*
15581          * Touch Devices
15582          */
15583         
15584         if(Roo.isTouch && this.mobileTouchView){
15585             cfg = this.getAutoCreateTouchView();
15586             return cfg;;
15587         }
15588         
15589         /*
15590          *  Normal ComboBox
15591          */
15592         if(!this.tickable){
15593             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15594             return cfg;
15595         }
15596         
15597         /*
15598          *  ComboBox with tickable selections
15599          */
15600              
15601         var align = this.labelAlign || this.parentLabelAlign();
15602         
15603         cfg = {
15604             cls : 'form-group roo-combobox-tickable' //input-group
15605         };
15606         
15607         var btn_text_select = '';
15608         var btn_text_done = '';
15609         var btn_text_cancel = '';
15610         
15611         if (this.btn_text_show) {
15612             btn_text_select = 'Select';
15613             btn_text_done = 'Done';
15614             btn_text_cancel = 'Cancel'; 
15615         }
15616         
15617         var buttons = {
15618             tag : 'div',
15619             cls : 'tickable-buttons',
15620             cn : [
15621                 {
15622                     tag : 'button',
15623                     type : 'button',
15624                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15625                     //html : this.triggerText
15626                     html: btn_text_select
15627                 },
15628                 {
15629                     tag : 'button',
15630                     type : 'button',
15631                     name : 'ok',
15632                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15633                     //html : 'Done'
15634                     html: btn_text_done
15635                 },
15636                 {
15637                     tag : 'button',
15638                     type : 'button',
15639                     name : 'cancel',
15640                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15641                     //html : 'Cancel'
15642                     html: btn_text_cancel
15643                 }
15644             ]
15645         };
15646         
15647         if(this.editable){
15648             buttons.cn.unshift({
15649                 tag: 'input',
15650                 cls: 'roo-select2-search-field-input'
15651             });
15652         }
15653         
15654         var _this = this;
15655         
15656         Roo.each(buttons.cn, function(c){
15657             if (_this.size) {
15658                 c.cls += ' btn-' + _this.size;
15659             }
15660
15661             if (_this.disabled) {
15662                 c.disabled = true;
15663             }
15664         });
15665         
15666         var box = {
15667             tag: 'div',
15668             style : 'display: contents',
15669             cn: [
15670                 {
15671                     tag: 'input',
15672                     type : 'hidden',
15673                     cls: 'form-hidden-field'
15674                 },
15675                 {
15676                     tag: 'ul',
15677                     cls: 'roo-select2-choices',
15678                     cn:[
15679                         {
15680                             tag: 'li',
15681                             cls: 'roo-select2-search-field',
15682                             cn: [
15683                                 buttons
15684                             ]
15685                         }
15686                     ]
15687                 }
15688             ]
15689         };
15690         
15691         var combobox = {
15692             cls: 'roo-select2-container input-group roo-select2-container-multi',
15693             cn: [
15694                 
15695                 box
15696 //                {
15697 //                    tag: 'ul',
15698 //                    cls: 'typeahead typeahead-long dropdown-menu',
15699 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15700 //                }
15701             ]
15702         };
15703         
15704         if(this.hasFeedback && !this.allowBlank){
15705             
15706             var feedback = {
15707                 tag: 'span',
15708                 cls: 'glyphicon form-control-feedback'
15709             };
15710
15711             combobox.cn.push(feedback);
15712         }
15713         
15714         
15715         
15716         var indicator = {
15717             tag : 'i',
15718             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15719             tooltip : 'This field is required'
15720         };
15721         if (Roo.bootstrap.version == 4) {
15722             indicator = {
15723                 tag : 'i',
15724                 style : 'display:none'
15725             };
15726         }
15727         if (align ==='left' && this.fieldLabel.length) {
15728             
15729             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15730             
15731             cfg.cn = [
15732                 indicator,
15733                 {
15734                     tag: 'label',
15735                     'for' :  id,
15736                     cls : 'control-label col-form-label',
15737                     html : this.fieldLabel
15738
15739                 },
15740                 {
15741                     cls : "", 
15742                     cn: [
15743                         combobox
15744                     ]
15745                 }
15746
15747             ];
15748             
15749             var labelCfg = cfg.cn[1];
15750             var contentCfg = cfg.cn[2];
15751             
15752
15753             if(this.indicatorpos == 'right'){
15754                 
15755                 cfg.cn = [
15756                     {
15757                         tag: 'label',
15758                         'for' :  id,
15759                         cls : 'control-label col-form-label',
15760                         cn : [
15761                             {
15762                                 tag : 'span',
15763                                 html : this.fieldLabel
15764                             },
15765                             indicator
15766                         ]
15767                     },
15768                     {
15769                         cls : "",
15770                         cn: [
15771                             combobox
15772                         ]
15773                     }
15774
15775                 ];
15776                 
15777                 
15778                 
15779                 labelCfg = cfg.cn[0];
15780                 contentCfg = cfg.cn[1];
15781             
15782             }
15783             
15784             if(this.labelWidth > 12){
15785                 labelCfg.style = "width: " + this.labelWidth + 'px';
15786             }
15787             if(this.width * 1 > 0){
15788                 contentCfg.style = "width: " + this.width + 'px';
15789             }
15790             if(this.labelWidth < 13 && this.labelmd == 0){
15791                 this.labelmd = this.labelWidth;
15792             }
15793             
15794             if(this.labellg > 0){
15795                 labelCfg.cls += ' col-lg-' + this.labellg;
15796                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15797             }
15798             
15799             if(this.labelmd > 0){
15800                 labelCfg.cls += ' col-md-' + this.labelmd;
15801                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15802             }
15803             
15804             if(this.labelsm > 0){
15805                 labelCfg.cls += ' col-sm-' + this.labelsm;
15806                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15807             }
15808             
15809             if(this.labelxs > 0){
15810                 labelCfg.cls += ' col-xs-' + this.labelxs;
15811                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15812             }
15813                 
15814                 
15815         } else if ( this.fieldLabel.length) {
15816 //                Roo.log(" label");
15817                  cfg.cn = [
15818                    indicator,
15819                     {
15820                         tag: 'label',
15821                         //cls : 'input-group-addon',
15822                         html : this.fieldLabel
15823                     },
15824                     combobox
15825                 ];
15826                 
15827                 if(this.indicatorpos == 'right'){
15828                     cfg.cn = [
15829                         {
15830                             tag: 'label',
15831                             //cls : 'input-group-addon',
15832                             html : this.fieldLabel
15833                         },
15834                         indicator,
15835                         combobox
15836                     ];
15837                     
15838                 }
15839
15840         } else {
15841             
15842 //                Roo.log(" no label && no align");
15843                 cfg = combobox
15844                      
15845                 
15846         }
15847          
15848         var settings=this;
15849         ['xs','sm','md','lg'].map(function(size){
15850             if (settings[size]) {
15851                 cfg.cls += ' col-' + size + '-' + settings[size];
15852             }
15853         });
15854         
15855         return cfg;
15856         
15857     },
15858     
15859     _initEventsCalled : false,
15860     
15861     // private
15862     initEvents: function()
15863     {   
15864         if (this._initEventsCalled) { // as we call render... prevent looping...
15865             return;
15866         }
15867         this._initEventsCalled = true;
15868         
15869         if (!this.store) {
15870             throw "can not find store for combo";
15871         }
15872         
15873         this.indicator = this.indicatorEl();
15874         
15875         this.store = Roo.factory(this.store, Roo.data);
15876         this.store.parent = this;
15877         
15878         // if we are building from html. then this element is so complex, that we can not really
15879         // use the rendered HTML.
15880         // so we have to trash and replace the previous code.
15881         if (Roo.XComponent.build_from_html) {
15882             // remove this element....
15883             var e = this.el.dom, k=0;
15884             while (e ) { e = e.previousSibling;  ++k;}
15885
15886             this.el.remove();
15887             
15888             this.el=false;
15889             this.rendered = false;
15890             
15891             this.render(this.parent().getChildContainer(true), k);
15892         }
15893         
15894         if(Roo.isIOS && this.useNativeIOS){
15895             this.initIOSView();
15896             return;
15897         }
15898         
15899         /*
15900          * Touch Devices
15901          */
15902         
15903         if(Roo.isTouch && this.mobileTouchView){
15904             this.initTouchView();
15905             return;
15906         }
15907         
15908         if(this.tickable){
15909             this.initTickableEvents();
15910             return;
15911         }
15912         
15913         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15914         
15915         if(this.hiddenName){
15916             
15917             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15918             
15919             this.hiddenField.dom.value =
15920                 this.hiddenValue !== undefined ? this.hiddenValue :
15921                 this.value !== undefined ? this.value : '';
15922
15923             // prevent input submission
15924             this.el.dom.removeAttribute('name');
15925             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15926              
15927              
15928         }
15929         //if(Roo.isGecko){
15930         //    this.el.dom.setAttribute('autocomplete', 'off');
15931         //}
15932         
15933         var cls = 'x-combo-list';
15934         
15935         //this.list = new Roo.Layer({
15936         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15937         //});
15938         
15939         var _this = this;
15940         
15941         (function(){
15942             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15943             _this.list.setWidth(lw);
15944         }).defer(100);
15945         
15946         this.list.on('mouseover', this.onViewOver, this);
15947         this.list.on('mousemove', this.onViewMove, this);
15948         this.list.on('scroll', this.onViewScroll, this);
15949         
15950         /*
15951         this.list.swallowEvent('mousewheel');
15952         this.assetHeight = 0;
15953
15954         if(this.title){
15955             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15956             this.assetHeight += this.header.getHeight();
15957         }
15958
15959         this.innerList = this.list.createChild({cls:cls+'-inner'});
15960         this.innerList.on('mouseover', this.onViewOver, this);
15961         this.innerList.on('mousemove', this.onViewMove, this);
15962         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15963         
15964         if(this.allowBlank && !this.pageSize && !this.disableClear){
15965             this.footer = this.list.createChild({cls:cls+'-ft'});
15966             this.pageTb = new Roo.Toolbar(this.footer);
15967            
15968         }
15969         if(this.pageSize){
15970             this.footer = this.list.createChild({cls:cls+'-ft'});
15971             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15972                     {pageSize: this.pageSize});
15973             
15974         }
15975         
15976         if (this.pageTb && this.allowBlank && !this.disableClear) {
15977             var _this = this;
15978             this.pageTb.add(new Roo.Toolbar.Fill(), {
15979                 cls: 'x-btn-icon x-btn-clear',
15980                 text: '&#160;',
15981                 handler: function()
15982                 {
15983                     _this.collapse();
15984                     _this.clearValue();
15985                     _this.onSelect(false, -1);
15986                 }
15987             });
15988         }
15989         if (this.footer) {
15990             this.assetHeight += this.footer.getHeight();
15991         }
15992         */
15993             
15994         if(!this.tpl){
15995             this.tpl = Roo.bootstrap.version == 4 ?
15996                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15997                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15998         }
15999
16000         this.view = new Roo.View(this.list, this.tpl, {
16001             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16002         });
16003         //this.view.wrapEl.setDisplayed(false);
16004         this.view.on('click', this.onViewClick, this);
16005         
16006         
16007         this.store.on('beforeload', this.onBeforeLoad, this);
16008         this.store.on('load', this.onLoad, this);
16009         this.store.on('loadexception', this.onLoadException, this);
16010         /*
16011         if(this.resizable){
16012             this.resizer = new Roo.Resizable(this.list,  {
16013                pinned:true, handles:'se'
16014             });
16015             this.resizer.on('resize', function(r, w, h){
16016                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16017                 this.listWidth = w;
16018                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16019                 this.restrictHeight();
16020             }, this);
16021             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16022         }
16023         */
16024         if(!this.editable){
16025             this.editable = true;
16026             this.setEditable(false);
16027         }
16028         
16029         /*
16030         
16031         if (typeof(this.events.add.listeners) != 'undefined') {
16032             
16033             this.addicon = this.wrap.createChild(
16034                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16035        
16036             this.addicon.on('click', function(e) {
16037                 this.fireEvent('add', this);
16038             }, this);
16039         }
16040         if (typeof(this.events.edit.listeners) != 'undefined') {
16041             
16042             this.editicon = this.wrap.createChild(
16043                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16044             if (this.addicon) {
16045                 this.editicon.setStyle('margin-left', '40px');
16046             }
16047             this.editicon.on('click', function(e) {
16048                 
16049                 // we fire even  if inothing is selected..
16050                 this.fireEvent('edit', this, this.lastData );
16051                 
16052             }, this);
16053         }
16054         */
16055         
16056         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16057             "up" : function(e){
16058                 this.inKeyMode = true;
16059                 this.selectPrev();
16060             },
16061
16062             "down" : function(e){
16063                 if(!this.isExpanded()){
16064                     this.onTriggerClick();
16065                 }else{
16066                     this.inKeyMode = true;
16067                     this.selectNext();
16068                 }
16069             },
16070
16071             "enter" : function(e){
16072 //                this.onViewClick();
16073                 //return true;
16074                 this.collapse();
16075                 
16076                 if(this.fireEvent("specialkey", this, e)){
16077                     this.onViewClick(false);
16078                 }
16079                 
16080                 return true;
16081             },
16082
16083             "esc" : function(e){
16084                 this.collapse();
16085             },
16086
16087             "tab" : function(e){
16088                 this.collapse();
16089                 
16090                 if(this.fireEvent("specialkey", this, e)){
16091                     this.onViewClick(false);
16092                 }
16093                 
16094                 return true;
16095             },
16096
16097             scope : this,
16098
16099             doRelay : function(foo, bar, hname){
16100                 if(hname == 'down' || this.scope.isExpanded()){
16101                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16102                 }
16103                 return true;
16104             },
16105
16106             forceKeyDown: true
16107         });
16108         
16109         
16110         this.queryDelay = Math.max(this.queryDelay || 10,
16111                 this.mode == 'local' ? 10 : 250);
16112         
16113         
16114         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16115         
16116         if(this.typeAhead){
16117             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16118         }
16119         if(this.editable !== false){
16120             this.inputEl().on("keyup", this.onKeyUp, this);
16121         }
16122         if(this.forceSelection){
16123             this.inputEl().on('blur', this.doForce, this);
16124         }
16125         
16126         if(this.multiple){
16127             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16128             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16129         }
16130     },
16131     
16132     initTickableEvents: function()
16133     {   
16134         this.createList();
16135         
16136         if(this.hiddenName){
16137             
16138             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16139             
16140             this.hiddenField.dom.value =
16141                 this.hiddenValue !== undefined ? this.hiddenValue :
16142                 this.value !== undefined ? this.value : '';
16143
16144             // prevent input submission
16145             this.el.dom.removeAttribute('name');
16146             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16147              
16148              
16149         }
16150         
16151 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16152         
16153         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16154         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16155         if(this.triggerList){
16156             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16157         }
16158          
16159         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16160         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16161         
16162         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16163         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16164         
16165         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16166         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16167         
16168         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16169         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16170         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16171         
16172         this.okBtn.hide();
16173         this.cancelBtn.hide();
16174         
16175         var _this = this;
16176         
16177         (function(){
16178             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16179             _this.list.setWidth(lw);
16180         }).defer(100);
16181         
16182         this.list.on('mouseover', this.onViewOver, this);
16183         this.list.on('mousemove', this.onViewMove, this);
16184         
16185         this.list.on('scroll', this.onViewScroll, this);
16186         
16187         if(!this.tpl){
16188             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16189                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16190         }
16191
16192         this.view = new Roo.View(this.list, this.tpl, {
16193             singleSelect:true,
16194             tickable:true,
16195             parent:this,
16196             store: this.store,
16197             selectedClass: this.selectedClass
16198         });
16199         
16200         //this.view.wrapEl.setDisplayed(false);
16201         this.view.on('click', this.onViewClick, this);
16202         
16203         
16204         
16205         this.store.on('beforeload', this.onBeforeLoad, this);
16206         this.store.on('load', this.onLoad, this);
16207         this.store.on('loadexception', this.onLoadException, this);
16208         
16209         if(this.editable){
16210             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16211                 "up" : function(e){
16212                     this.inKeyMode = true;
16213                     this.selectPrev();
16214                 },
16215
16216                 "down" : function(e){
16217                     this.inKeyMode = true;
16218                     this.selectNext();
16219                 },
16220
16221                 "enter" : function(e){
16222                     if(this.fireEvent("specialkey", this, e)){
16223                         this.onViewClick(false);
16224                     }
16225                     
16226                     return true;
16227                 },
16228
16229                 "esc" : function(e){
16230                     this.onTickableFooterButtonClick(e, false, false);
16231                 },
16232
16233                 "tab" : function(e){
16234                     this.fireEvent("specialkey", this, e);
16235                     
16236                     this.onTickableFooterButtonClick(e, false, false);
16237                     
16238                     return true;
16239                 },
16240
16241                 scope : this,
16242
16243                 doRelay : function(e, fn, key){
16244                     if(this.scope.isExpanded()){
16245                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16246                     }
16247                     return true;
16248                 },
16249
16250                 forceKeyDown: true
16251             });
16252         }
16253         
16254         this.queryDelay = Math.max(this.queryDelay || 10,
16255                 this.mode == 'local' ? 10 : 250);
16256         
16257         
16258         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16259         
16260         if(this.typeAhead){
16261             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16262         }
16263         
16264         if(this.editable !== false){
16265             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16266         }
16267         
16268         this.indicator = this.indicatorEl();
16269         
16270         if(this.indicator){
16271             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16272             this.indicator.hide();
16273         }
16274         
16275     },
16276
16277     onDestroy : function(){
16278         if(this.view){
16279             this.view.setStore(null);
16280             this.view.el.removeAllListeners();
16281             this.view.el.remove();
16282             this.view.purgeListeners();
16283         }
16284         if(this.list){
16285             this.list.dom.innerHTML  = '';
16286         }
16287         
16288         if(this.store){
16289             this.store.un('beforeload', this.onBeforeLoad, this);
16290             this.store.un('load', this.onLoad, this);
16291             this.store.un('loadexception', this.onLoadException, this);
16292         }
16293         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16294     },
16295
16296     // private
16297     fireKey : function(e){
16298         if(e.isNavKeyPress() && !this.list.isVisible()){
16299             this.fireEvent("specialkey", this, e);
16300         }
16301     },
16302
16303     // private
16304     onResize: function(w, h)
16305     {
16306         
16307         
16308 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16309 //        
16310 //        if(typeof w != 'number'){
16311 //            // we do not handle it!?!?
16312 //            return;
16313 //        }
16314 //        var tw = this.trigger.getWidth();
16315 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16316 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16317 //        var x = w - tw;
16318 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16319 //            
16320 //        //this.trigger.setStyle('left', x+'px');
16321 //        
16322 //        if(this.list && this.listWidth === undefined){
16323 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16324 //            this.list.setWidth(lw);
16325 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16326 //        }
16327         
16328     
16329         
16330     },
16331
16332     /**
16333      * Allow or prevent the user from directly editing the field text.  If false is passed,
16334      * the user will only be able to select from the items defined in the dropdown list.  This method
16335      * is the runtime equivalent of setting the 'editable' config option at config time.
16336      * @param {Boolean} value True to allow the user to directly edit the field text
16337      */
16338     setEditable : function(value){
16339         if(value == this.editable){
16340             return;
16341         }
16342         this.editable = value;
16343         if(!value){
16344             this.inputEl().dom.setAttribute('readOnly', true);
16345             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16346             this.inputEl().addClass('x-combo-noedit');
16347         }else{
16348             this.inputEl().dom.setAttribute('readOnly', false);
16349             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16350             this.inputEl().removeClass('x-combo-noedit');
16351         }
16352     },
16353
16354     // private
16355     
16356     onBeforeLoad : function(combo,opts){
16357         if(!this.hasFocus){
16358             return;
16359         }
16360          if (!opts.add) {
16361             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16362          }
16363         this.restrictHeight();
16364         this.selectedIndex = -1;
16365     },
16366
16367     // private
16368     onLoad : function(){
16369         
16370         this.hasQuery = false;
16371         
16372         if(!this.hasFocus){
16373             return;
16374         }
16375         
16376         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16377             this.loading.hide();
16378         }
16379         
16380         if(this.store.getCount() > 0){
16381             
16382             this.expand();
16383             this.restrictHeight();
16384             if(this.lastQuery == this.allQuery){
16385                 if(this.editable && !this.tickable){
16386                     this.inputEl().dom.select();
16387                 }
16388                 
16389                 if(
16390                     !this.selectByValue(this.value, true) &&
16391                     this.autoFocus && 
16392                     (
16393                         !this.store.lastOptions ||
16394                         typeof(this.store.lastOptions.add) == 'undefined' || 
16395                         this.store.lastOptions.add != true
16396                     )
16397                 ){
16398                     this.select(0, true);
16399                 }
16400             }else{
16401                 if(this.autoFocus){
16402                     this.selectNext();
16403                 }
16404                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16405                     this.taTask.delay(this.typeAheadDelay);
16406                 }
16407             }
16408         }else{
16409             this.onEmptyResults();
16410         }
16411         
16412         //this.el.focus();
16413     },
16414     // private
16415     onLoadException : function()
16416     {
16417         this.hasQuery = false;
16418         
16419         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16420             this.loading.hide();
16421         }
16422         
16423         if(this.tickable && this.editable){
16424             return;
16425         }
16426         
16427         this.collapse();
16428         // only causes errors at present
16429         //Roo.log(this.store.reader.jsonData);
16430         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16431             // fixme
16432             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16433         //}
16434         
16435         
16436     },
16437     // private
16438     onTypeAhead : function(){
16439         if(this.store.getCount() > 0){
16440             var r = this.store.getAt(0);
16441             var newValue = r.data[this.displayField];
16442             var len = newValue.length;
16443             var selStart = this.getRawValue().length;
16444             
16445             if(selStart != len){
16446                 this.setRawValue(newValue);
16447                 this.selectText(selStart, newValue.length);
16448             }
16449         }
16450     },
16451
16452     // private
16453     onSelect : function(record, index){
16454         
16455         if(this.fireEvent('beforeselect', this, record, index) !== false){
16456         
16457             this.setFromData(index > -1 ? record.data : false);
16458             
16459             this.collapse();
16460             this.fireEvent('select', this, record, index);
16461         }
16462     },
16463
16464     /**
16465      * Returns the currently selected field value or empty string if no value is set.
16466      * @return {String} value The selected value
16467      */
16468     getValue : function()
16469     {
16470         if(Roo.isIOS && this.useNativeIOS){
16471             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16472         }
16473         
16474         if(this.multiple){
16475             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16476         }
16477         
16478         if(this.valueField){
16479             return typeof this.value != 'undefined' ? this.value : '';
16480         }else{
16481             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16482         }
16483     },
16484     
16485     getRawValue : function()
16486     {
16487         if(Roo.isIOS && this.useNativeIOS){
16488             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16489         }
16490         
16491         var v = this.inputEl().getValue();
16492         
16493         return v;
16494     },
16495
16496     /**
16497      * Clears any text/value currently set in the field
16498      */
16499     clearValue : function(){
16500         
16501         if(this.hiddenField){
16502             this.hiddenField.dom.value = '';
16503         }
16504         this.value = '';
16505         this.setRawValue('');
16506         this.lastSelectionText = '';
16507         this.lastData = false;
16508         
16509         var close = this.closeTriggerEl();
16510         
16511         if(close){
16512             close.hide();
16513         }
16514         
16515         this.validate();
16516         
16517     },
16518
16519     /**
16520      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16521      * will be displayed in the field.  If the value does not match the data value of an existing item,
16522      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16523      * Otherwise the field will be blank (although the value will still be set).
16524      * @param {String} value The value to match
16525      */
16526     setValue : function(v)
16527     {
16528         if(Roo.isIOS && this.useNativeIOS){
16529             this.setIOSValue(v);
16530             return;
16531         }
16532         
16533         if(this.multiple){
16534             this.syncValue();
16535             return;
16536         }
16537         
16538         var text = v;
16539         if(this.valueField){
16540             var r = this.findRecord(this.valueField, v);
16541             if(r){
16542                 text = r.data[this.displayField];
16543             }else if(this.valueNotFoundText !== undefined){
16544                 text = this.valueNotFoundText;
16545             }
16546         }
16547         this.lastSelectionText = text;
16548         if(this.hiddenField){
16549             this.hiddenField.dom.value = v;
16550         }
16551         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16552         this.value = v;
16553         
16554         var close = this.closeTriggerEl();
16555         
16556         if(close){
16557             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16558         }
16559         
16560         this.validate();
16561     },
16562     /**
16563      * @property {Object} the last set data for the element
16564      */
16565     
16566     lastData : false,
16567     /**
16568      * Sets the value of the field based on a object which is related to the record format for the store.
16569      * @param {Object} value the value to set as. or false on reset?
16570      */
16571     setFromData : function(o){
16572         
16573         if(this.multiple){
16574             this.addItem(o);
16575             return;
16576         }
16577             
16578         var dv = ''; // display value
16579         var vv = ''; // value value..
16580         this.lastData = o;
16581         if (this.displayField) {
16582             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16583         } else {
16584             // this is an error condition!!!
16585             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16586         }
16587         
16588         if(this.valueField){
16589             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16590         }
16591         
16592         var close = this.closeTriggerEl();
16593         
16594         if(close){
16595             if(dv.length || vv * 1 > 0){
16596                 close.show() ;
16597                 this.blockFocus=true;
16598             } else {
16599                 close.hide();
16600             }             
16601         }
16602         
16603         if(this.hiddenField){
16604             this.hiddenField.dom.value = vv;
16605             
16606             this.lastSelectionText = dv;
16607             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16608             this.value = vv;
16609             return;
16610         }
16611         // no hidden field.. - we store the value in 'value', but still display
16612         // display field!!!!
16613         this.lastSelectionText = dv;
16614         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16615         this.value = vv;
16616         
16617         
16618         
16619     },
16620     // private
16621     reset : function(){
16622         // overridden so that last data is reset..
16623         
16624         if(this.multiple){
16625             this.clearItem();
16626             return;
16627         }
16628         
16629         this.setValue(this.originalValue);
16630         //this.clearInvalid();
16631         this.lastData = false;
16632         if (this.view) {
16633             this.view.clearSelections();
16634         }
16635         
16636         this.validate();
16637     },
16638     // private
16639     findRecord : function(prop, value){
16640         var record;
16641         if(this.store.getCount() > 0){
16642             this.store.each(function(r){
16643                 if(r.data[prop] == value){
16644                     record = r;
16645                     return false;
16646                 }
16647                 return true;
16648             });
16649         }
16650         return record;
16651     },
16652     
16653     getName: function()
16654     {
16655         // returns hidden if it's set..
16656         if (!this.rendered) {return ''};
16657         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16658         
16659     },
16660     // private
16661     onViewMove : function(e, t){
16662         this.inKeyMode = false;
16663     },
16664
16665     // private
16666     onViewOver : function(e, t){
16667         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16668             return;
16669         }
16670         var item = this.view.findItemFromChild(t);
16671         
16672         if(item){
16673             var index = this.view.indexOf(item);
16674             this.select(index, false);
16675         }
16676     },
16677
16678     // private
16679     onViewClick : function(view, doFocus, el, e)
16680     {
16681         var index = this.view.getSelectedIndexes()[0];
16682         
16683         var r = this.store.getAt(index);
16684         
16685         if(this.tickable){
16686             
16687             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16688                 return;
16689             }
16690             
16691             var rm = false;
16692             var _this = this;
16693             
16694             Roo.each(this.tickItems, function(v,k){
16695                 
16696                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16697                     Roo.log(v);
16698                     _this.tickItems.splice(k, 1);
16699                     
16700                     if(typeof(e) == 'undefined' && view == false){
16701                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16702                     }
16703                     
16704                     rm = true;
16705                     return;
16706                 }
16707             });
16708             
16709             if(rm){
16710                 return;
16711             }
16712             
16713             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16714                 this.tickItems.push(r.data);
16715             }
16716             
16717             if(typeof(e) == 'undefined' && view == false){
16718                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16719             }
16720                     
16721             return;
16722         }
16723         
16724         if(r){
16725             this.onSelect(r, index);
16726         }
16727         if(doFocus !== false && !this.blockFocus){
16728             this.inputEl().focus();
16729         }
16730     },
16731
16732     // private
16733     restrictHeight : function(){
16734         //this.innerList.dom.style.height = '';
16735         //var inner = this.innerList.dom;
16736         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16737         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16738         //this.list.beginUpdate();
16739         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16740         this.list.alignTo(this.inputEl(), this.listAlign);
16741         this.list.alignTo(this.inputEl(), this.listAlign);
16742         //this.list.endUpdate();
16743     },
16744
16745     // private
16746     onEmptyResults : function(){
16747         
16748         if(this.tickable && this.editable){
16749             this.hasFocus = false;
16750             this.restrictHeight();
16751             return;
16752         }
16753         
16754         this.collapse();
16755     },
16756
16757     /**
16758      * Returns true if the dropdown list is expanded, else false.
16759      */
16760     isExpanded : function(){
16761         return this.list.isVisible();
16762     },
16763
16764     /**
16765      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16766      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16767      * @param {String} value The data value of the item to select
16768      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16769      * selected item if it is not currently in view (defaults to true)
16770      * @return {Boolean} True if the value matched an item in the list, else false
16771      */
16772     selectByValue : function(v, scrollIntoView){
16773         if(v !== undefined && v !== null){
16774             var r = this.findRecord(this.valueField || this.displayField, v);
16775             if(r){
16776                 this.select(this.store.indexOf(r), scrollIntoView);
16777                 return true;
16778             }
16779         }
16780         return false;
16781     },
16782
16783     /**
16784      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16785      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16786      * @param {Number} index The zero-based index of the list item to select
16787      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16788      * selected item if it is not currently in view (defaults to true)
16789      */
16790     select : function(index, scrollIntoView){
16791         this.selectedIndex = index;
16792         this.view.select(index);
16793         if(scrollIntoView !== false){
16794             var el = this.view.getNode(index);
16795             /*
16796              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16797              */
16798             if(el){
16799                 this.list.scrollChildIntoView(el, false);
16800             }
16801         }
16802     },
16803
16804     // private
16805     selectNext : function(){
16806         var ct = this.store.getCount();
16807         if(ct > 0){
16808             if(this.selectedIndex == -1){
16809                 this.select(0);
16810             }else if(this.selectedIndex < ct-1){
16811                 this.select(this.selectedIndex+1);
16812             }
16813         }
16814     },
16815
16816     // private
16817     selectPrev : function(){
16818         var ct = this.store.getCount();
16819         if(ct > 0){
16820             if(this.selectedIndex == -1){
16821                 this.select(0);
16822             }else if(this.selectedIndex != 0){
16823                 this.select(this.selectedIndex-1);
16824             }
16825         }
16826     },
16827
16828     // private
16829     onKeyUp : function(e){
16830         if(this.editable !== false && !e.isSpecialKey()){
16831             this.lastKey = e.getKey();
16832             this.dqTask.delay(this.queryDelay);
16833         }
16834     },
16835
16836     // private
16837     validateBlur : function(){
16838         return !this.list || !this.list.isVisible();   
16839     },
16840
16841     // private
16842     initQuery : function(){
16843         
16844         var v = this.getRawValue();
16845         
16846         if(this.tickable && this.editable){
16847             v = this.tickableInputEl().getValue();
16848         }
16849         
16850         this.doQuery(v);
16851     },
16852
16853     // private
16854     doForce : function(){
16855         if(this.inputEl().dom.value.length > 0){
16856             this.inputEl().dom.value =
16857                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16858              
16859         }
16860     },
16861
16862     /**
16863      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16864      * query allowing the query action to be canceled if needed.
16865      * @param {String} query The SQL query to execute
16866      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16867      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16868      * saved in the current store (defaults to false)
16869      */
16870     doQuery : function(q, forceAll){
16871         
16872         if(q === undefined || q === null){
16873             q = '';
16874         }
16875         var qe = {
16876             query: q,
16877             forceAll: forceAll,
16878             combo: this,
16879             cancel:false
16880         };
16881         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16882             return false;
16883         }
16884         q = qe.query;
16885         
16886         forceAll = qe.forceAll;
16887         if(forceAll === true || (q.length >= this.minChars)){
16888             
16889             this.hasQuery = true;
16890             
16891             if(this.lastQuery != q || this.alwaysQuery){
16892                 this.lastQuery = q;
16893                 if(this.mode == 'local'){
16894                     this.selectedIndex = -1;
16895                     if(forceAll){
16896                         this.store.clearFilter();
16897                     }else{
16898                         
16899                         if(this.specialFilter){
16900                             this.fireEvent('specialfilter', this);
16901                             this.onLoad();
16902                             return;
16903                         }
16904                         
16905                         this.store.filter(this.displayField, q);
16906                     }
16907                     
16908                     this.store.fireEvent("datachanged", this.store);
16909                     
16910                     this.onLoad();
16911                     
16912                     
16913                 }else{
16914                     
16915                     this.store.baseParams[this.queryParam] = q;
16916                     
16917                     var options = {params : this.getParams(q)};
16918                     
16919                     if(this.loadNext){
16920                         options.add = true;
16921                         options.params.start = this.page * this.pageSize;
16922                     }
16923                     
16924                     this.store.load(options);
16925                     
16926                     /*
16927                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16928                      *  we should expand the list on onLoad
16929                      *  so command out it
16930                      */
16931 //                    this.expand();
16932                 }
16933             }else{
16934                 this.selectedIndex = -1;
16935                 this.onLoad();   
16936             }
16937         }
16938         
16939         this.loadNext = false;
16940     },
16941     
16942     // private
16943     getParams : function(q){
16944         var p = {};
16945         //p[this.queryParam] = q;
16946         
16947         if(this.pageSize){
16948             p.start = 0;
16949             p.limit = this.pageSize;
16950         }
16951         return p;
16952     },
16953
16954     /**
16955      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16956      */
16957     collapse : function(){
16958         if(!this.isExpanded()){
16959             return;
16960         }
16961         
16962         this.list.hide();
16963         
16964         this.hasFocus = false;
16965         
16966         if(this.tickable){
16967             this.okBtn.hide();
16968             this.cancelBtn.hide();
16969             this.trigger.show();
16970             
16971             if(this.editable){
16972                 this.tickableInputEl().dom.value = '';
16973                 this.tickableInputEl().blur();
16974             }
16975             
16976         }
16977         
16978         Roo.get(document).un('mousedown', this.collapseIf, this);
16979         Roo.get(document).un('mousewheel', this.collapseIf, this);
16980         if (!this.editable) {
16981             Roo.get(document).un('keydown', this.listKeyPress, this);
16982         }
16983         this.fireEvent('collapse', this);
16984         
16985         this.validate();
16986     },
16987
16988     // private
16989     collapseIf : function(e){
16990         var in_combo  = e.within(this.el);
16991         var in_list =  e.within(this.list);
16992         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16993         
16994         if (in_combo || in_list || is_list) {
16995             //e.stopPropagation();
16996             return;
16997         }
16998         
16999         if(this.tickable){
17000             this.onTickableFooterButtonClick(e, false, false);
17001         }
17002
17003         this.collapse();
17004         
17005     },
17006
17007     /**
17008      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17009      */
17010     expand : function(){
17011        
17012         if(this.isExpanded() || !this.hasFocus){
17013             return;
17014         }
17015         
17016         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17017         this.list.setWidth(lw);
17018         
17019         Roo.log('expand');
17020         
17021         this.list.show();
17022         
17023         this.restrictHeight();
17024         
17025         if(this.tickable){
17026             
17027             this.tickItems = Roo.apply([], this.item);
17028             
17029             this.okBtn.show();
17030             this.cancelBtn.show();
17031             this.trigger.hide();
17032             
17033             if(this.editable){
17034                 this.tickableInputEl().focus();
17035             }
17036             
17037         }
17038         
17039         Roo.get(document).on('mousedown', this.collapseIf, this);
17040         Roo.get(document).on('mousewheel', this.collapseIf, this);
17041         if (!this.editable) {
17042             Roo.get(document).on('keydown', this.listKeyPress, this);
17043         }
17044         
17045         this.fireEvent('expand', this);
17046     },
17047
17048     // private
17049     // Implements the default empty TriggerField.onTriggerClick function
17050     onTriggerClick : function(e)
17051     {
17052         Roo.log('trigger click');
17053         
17054         if(this.disabled || !this.triggerList){
17055             return;
17056         }
17057         
17058         this.page = 0;
17059         this.loadNext = false;
17060         
17061         if(this.isExpanded()){
17062             this.collapse();
17063             if (!this.blockFocus) {
17064                 this.inputEl().focus();
17065             }
17066             
17067         }else {
17068             this.hasFocus = true;
17069             if(this.triggerAction == 'all') {
17070                 this.doQuery(this.allQuery, true);
17071             } else {
17072                 this.doQuery(this.getRawValue());
17073             }
17074             if (!this.blockFocus) {
17075                 this.inputEl().focus();
17076             }
17077         }
17078     },
17079     
17080     onTickableTriggerClick : function(e)
17081     {
17082         if(this.disabled){
17083             return;
17084         }
17085         
17086         this.page = 0;
17087         this.loadNext = false;
17088         this.hasFocus = true;
17089         
17090         if(this.triggerAction == 'all') {
17091             this.doQuery(this.allQuery, true);
17092         } else {
17093             this.doQuery(this.getRawValue());
17094         }
17095     },
17096     
17097     onSearchFieldClick : function(e)
17098     {
17099         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17100             this.onTickableFooterButtonClick(e, false, false);
17101             return;
17102         }
17103         
17104         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17105             return;
17106         }
17107         
17108         this.page = 0;
17109         this.loadNext = false;
17110         this.hasFocus = true;
17111         
17112         if(this.triggerAction == 'all') {
17113             this.doQuery(this.allQuery, true);
17114         } else {
17115             this.doQuery(this.getRawValue());
17116         }
17117     },
17118     
17119     listKeyPress : function(e)
17120     {
17121         //Roo.log('listkeypress');
17122         // scroll to first matching element based on key pres..
17123         if (e.isSpecialKey()) {
17124             return false;
17125         }
17126         var k = String.fromCharCode(e.getKey()).toUpperCase();
17127         //Roo.log(k);
17128         var match  = false;
17129         var csel = this.view.getSelectedNodes();
17130         var cselitem = false;
17131         if (csel.length) {
17132             var ix = this.view.indexOf(csel[0]);
17133             cselitem  = this.store.getAt(ix);
17134             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17135                 cselitem = false;
17136             }
17137             
17138         }
17139         
17140         this.store.each(function(v) { 
17141             if (cselitem) {
17142                 // start at existing selection.
17143                 if (cselitem.id == v.id) {
17144                     cselitem = false;
17145                 }
17146                 return true;
17147             }
17148                 
17149             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17150                 match = this.store.indexOf(v);
17151                 return false;
17152             }
17153             return true;
17154         }, this);
17155         
17156         if (match === false) {
17157             return true; // no more action?
17158         }
17159         // scroll to?
17160         this.view.select(match);
17161         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17162         sn.scrollIntoView(sn.dom.parentNode, false);
17163     },
17164     
17165     onViewScroll : function(e, t){
17166         
17167         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){
17168             return;
17169         }
17170         
17171         this.hasQuery = true;
17172         
17173         this.loading = this.list.select('.loading', true).first();
17174         
17175         if(this.loading === null){
17176             this.list.createChild({
17177                 tag: 'div',
17178                 cls: 'loading roo-select2-more-results roo-select2-active',
17179                 html: 'Loading more results...'
17180             });
17181             
17182             this.loading = this.list.select('.loading', true).first();
17183             
17184             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17185             
17186             this.loading.hide();
17187         }
17188         
17189         this.loading.show();
17190         
17191         var _combo = this;
17192         
17193         this.page++;
17194         this.loadNext = true;
17195         
17196         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17197         
17198         return;
17199     },
17200     
17201     addItem : function(o)
17202     {   
17203         var dv = ''; // display value
17204         
17205         if (this.displayField) {
17206             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17207         } else {
17208             // this is an error condition!!!
17209             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17210         }
17211         
17212         if(!dv.length){
17213             return;
17214         }
17215         
17216         var choice = this.choices.createChild({
17217             tag: 'li',
17218             cls: 'roo-select2-search-choice',
17219             cn: [
17220                 {
17221                     tag: 'div',
17222                     html: dv
17223                 },
17224                 {
17225                     tag: 'a',
17226                     href: '#',
17227                     cls: 'roo-select2-search-choice-close fa fa-times',
17228                     tabindex: '-1'
17229                 }
17230             ]
17231             
17232         }, this.searchField);
17233         
17234         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17235         
17236         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17237         
17238         this.item.push(o);
17239         
17240         this.lastData = o;
17241         
17242         this.syncValue();
17243         
17244         this.inputEl().dom.value = '';
17245         
17246         this.validate();
17247     },
17248     
17249     onRemoveItem : function(e, _self, o)
17250     {
17251         e.preventDefault();
17252         
17253         this.lastItem = Roo.apply([], this.item);
17254         
17255         var index = this.item.indexOf(o.data) * 1;
17256         
17257         if( index < 0){
17258             Roo.log('not this item?!');
17259             return;
17260         }
17261         
17262         this.item.splice(index, 1);
17263         o.item.remove();
17264         
17265         this.syncValue();
17266         
17267         this.fireEvent('remove', this, e);
17268         
17269         this.validate();
17270         
17271     },
17272     
17273     syncValue : function()
17274     {
17275         if(!this.item.length){
17276             this.clearValue();
17277             return;
17278         }
17279             
17280         var value = [];
17281         var _this = this;
17282         Roo.each(this.item, function(i){
17283             if(_this.valueField){
17284                 value.push(i[_this.valueField]);
17285                 return;
17286             }
17287
17288             value.push(i);
17289         });
17290
17291         this.value = value.join(',');
17292
17293         if(this.hiddenField){
17294             this.hiddenField.dom.value = this.value;
17295         }
17296         
17297         this.store.fireEvent("datachanged", this.store);
17298         
17299         this.validate();
17300     },
17301     
17302     clearItem : function()
17303     {
17304         if(!this.multiple){
17305             return;
17306         }
17307         
17308         this.item = [];
17309         
17310         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17311            c.remove();
17312         });
17313         
17314         this.syncValue();
17315         
17316         this.validate();
17317         
17318         if(this.tickable && !Roo.isTouch){
17319             this.view.refresh();
17320         }
17321     },
17322     
17323     inputEl: function ()
17324     {
17325         if(Roo.isIOS && this.useNativeIOS){
17326             return this.el.select('select.roo-ios-select', true).first();
17327         }
17328         
17329         if(Roo.isTouch && this.mobileTouchView){
17330             return this.el.select('input.form-control',true).first();
17331         }
17332         
17333         if(this.tickable){
17334             return this.searchField;
17335         }
17336         
17337         return this.el.select('input.form-control',true).first();
17338     },
17339     
17340     onTickableFooterButtonClick : function(e, btn, el)
17341     {
17342         e.preventDefault();
17343         
17344         this.lastItem = Roo.apply([], this.item);
17345         
17346         if(btn && btn.name == 'cancel'){
17347             this.tickItems = Roo.apply([], this.item);
17348             this.collapse();
17349             return;
17350         }
17351         
17352         this.clearItem();
17353         
17354         var _this = this;
17355         
17356         Roo.each(this.tickItems, function(o){
17357             _this.addItem(o);
17358         });
17359         
17360         this.collapse();
17361         
17362     },
17363     
17364     validate : function()
17365     {
17366         if(this.getVisibilityEl().hasClass('hidden')){
17367             return true;
17368         }
17369         
17370         var v = this.getRawValue();
17371         
17372         if(this.multiple){
17373             v = this.getValue();
17374         }
17375         
17376         if(this.disabled || this.allowBlank || v.length){
17377             this.markValid();
17378             return true;
17379         }
17380         
17381         this.markInvalid();
17382         return false;
17383     },
17384     
17385     tickableInputEl : function()
17386     {
17387         if(!this.tickable || !this.editable){
17388             return this.inputEl();
17389         }
17390         
17391         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17392     },
17393     
17394     
17395     getAutoCreateTouchView : function()
17396     {
17397         var id = Roo.id();
17398         
17399         var cfg = {
17400             cls: 'form-group' //input-group
17401         };
17402         
17403         var input =  {
17404             tag: 'input',
17405             id : id,
17406             type : this.inputType,
17407             cls : 'form-control x-combo-noedit',
17408             autocomplete: 'new-password',
17409             placeholder : this.placeholder || '',
17410             readonly : true
17411         };
17412         
17413         if (this.name) {
17414             input.name = this.name;
17415         }
17416         
17417         if (this.size) {
17418             input.cls += ' input-' + this.size;
17419         }
17420         
17421         if (this.disabled) {
17422             input.disabled = true;
17423         }
17424         
17425         var inputblock = {
17426             cls : 'roo-combobox-wrap',
17427             cn : [
17428                 input
17429             ]
17430         };
17431         
17432         if(this.before){
17433             inputblock.cls += ' input-group';
17434             
17435             inputblock.cn.unshift({
17436                 tag :'span',
17437                 cls : 'input-group-addon input-group-prepend input-group-text',
17438                 html : this.before
17439             });
17440         }
17441         
17442         if(this.removable && !this.multiple){
17443             inputblock.cls += ' roo-removable';
17444             
17445             inputblock.cn.push({
17446                 tag: 'button',
17447                 html : 'x',
17448                 cls : 'roo-combo-removable-btn close'
17449             });
17450         }
17451
17452         if(this.hasFeedback && !this.allowBlank){
17453             
17454             inputblock.cls += ' has-feedback';
17455             
17456             inputblock.cn.push({
17457                 tag: 'span',
17458                 cls: 'glyphicon form-control-feedback'
17459             });
17460             
17461         }
17462         
17463         if (this.after) {
17464             
17465             inputblock.cls += (this.before) ? '' : ' input-group';
17466             
17467             inputblock.cn.push({
17468                 tag :'span',
17469                 cls : 'input-group-addon input-group-append input-group-text',
17470                 html : this.after
17471             });
17472         }
17473
17474         
17475         var ibwrap = inputblock;
17476         
17477         if(this.multiple){
17478             ibwrap = {
17479                 tag: 'ul',
17480                 cls: 'roo-select2-choices',
17481                 cn:[
17482                     {
17483                         tag: 'li',
17484                         cls: 'roo-select2-search-field',
17485                         cn: [
17486
17487                             inputblock
17488                         ]
17489                     }
17490                 ]
17491             };
17492         
17493             
17494         }
17495         
17496         var combobox = {
17497             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17498             cn: [
17499                 {
17500                     tag: 'input',
17501                     type : 'hidden',
17502                     cls: 'form-hidden-field'
17503                 },
17504                 ibwrap
17505             ]
17506         };
17507         
17508         if(!this.multiple && this.showToggleBtn){
17509             
17510             var caret = {
17511                 cls: 'caret'
17512             };
17513             
17514             if (this.caret != false) {
17515                 caret = {
17516                      tag: 'i',
17517                      cls: 'fa fa-' + this.caret
17518                 };
17519                 
17520             }
17521             
17522             combobox.cn.push({
17523                 tag :'span',
17524                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17525                 cn : [
17526                     Roo.bootstrap.version == 3 ? caret : '',
17527                     {
17528                         tag: 'span',
17529                         cls: 'combobox-clear',
17530                         cn  : [
17531                             {
17532                                 tag : 'i',
17533                                 cls: 'icon-remove'
17534                             }
17535                         ]
17536                     }
17537                 ]
17538
17539             })
17540         }
17541         
17542         if(this.multiple){
17543             combobox.cls += ' roo-select2-container-multi';
17544         }
17545         
17546         var align = this.labelAlign || this.parentLabelAlign();
17547         
17548         if (align ==='left' && this.fieldLabel.length) {
17549
17550             cfg.cn = [
17551                 {
17552                    tag : 'i',
17553                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17554                    tooltip : 'This field is required'
17555                 },
17556                 {
17557                     tag: 'label',
17558                     cls : 'control-label col-form-label',
17559                     html : this.fieldLabel
17560
17561                 },
17562                 {
17563                     cls : 'roo-combobox-wrap ', 
17564                     cn: [
17565                         combobox
17566                     ]
17567                 }
17568             ];
17569             
17570             var labelCfg = cfg.cn[1];
17571             var contentCfg = cfg.cn[2];
17572             
17573
17574             if(this.indicatorpos == 'right'){
17575                 cfg.cn = [
17576                     {
17577                         tag: 'label',
17578                         'for' :  id,
17579                         cls : 'control-label col-form-label',
17580                         cn : [
17581                             {
17582                                 tag : 'span',
17583                                 html : this.fieldLabel
17584                             },
17585                             {
17586                                 tag : 'i',
17587                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17588                                 tooltip : 'This field is required'
17589                             }
17590                         ]
17591                     },
17592                     {
17593                         cls : "roo-combobox-wrap ",
17594                         cn: [
17595                             combobox
17596                         ]
17597                     }
17598
17599                 ];
17600                 
17601                 labelCfg = cfg.cn[0];
17602                 contentCfg = cfg.cn[1];
17603             }
17604             
17605            
17606             
17607             if(this.labelWidth > 12){
17608                 labelCfg.style = "width: " + this.labelWidth + 'px';
17609             }
17610            
17611             if(this.labelWidth < 13 && this.labelmd == 0){
17612                 this.labelmd = this.labelWidth;
17613             }
17614             
17615             if(this.labellg > 0){
17616                 labelCfg.cls += ' col-lg-' + this.labellg;
17617                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17618             }
17619             
17620             if(this.labelmd > 0){
17621                 labelCfg.cls += ' col-md-' + this.labelmd;
17622                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17623             }
17624             
17625             if(this.labelsm > 0){
17626                 labelCfg.cls += ' col-sm-' + this.labelsm;
17627                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17628             }
17629             
17630             if(this.labelxs > 0){
17631                 labelCfg.cls += ' col-xs-' + this.labelxs;
17632                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17633             }
17634                 
17635                 
17636         } else if ( this.fieldLabel.length) {
17637             cfg.cn = [
17638                 {
17639                    tag : 'i',
17640                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17641                    tooltip : 'This field is required'
17642                 },
17643                 {
17644                     tag: 'label',
17645                     cls : 'control-label',
17646                     html : this.fieldLabel
17647
17648                 },
17649                 {
17650                     cls : '', 
17651                     cn: [
17652                         combobox
17653                     ]
17654                 }
17655             ];
17656             
17657             if(this.indicatorpos == 'right'){
17658                 cfg.cn = [
17659                     {
17660                         tag: 'label',
17661                         cls : 'control-label',
17662                         html : this.fieldLabel,
17663                         cn : [
17664                             {
17665                                tag : 'i',
17666                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17667                                tooltip : 'This field is required'
17668                             }
17669                         ]
17670                     },
17671                     {
17672                         cls : '', 
17673                         cn: [
17674                             combobox
17675                         ]
17676                     }
17677                 ];
17678             }
17679         } else {
17680             cfg.cn = combobox;    
17681         }
17682         
17683         
17684         var settings = this;
17685         
17686         ['xs','sm','md','lg'].map(function(size){
17687             if (settings[size]) {
17688                 cfg.cls += ' col-' + size + '-' + settings[size];
17689             }
17690         });
17691         
17692         return cfg;
17693     },
17694     
17695     initTouchView : function()
17696     {
17697         this.renderTouchView();
17698         
17699         this.touchViewEl.on('scroll', function(){
17700             this.el.dom.scrollTop = 0;
17701         }, this);
17702         
17703         this.originalValue = this.getValue();
17704         
17705         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17706         
17707         this.inputEl().on("click", this.showTouchView, this);
17708         if (this.triggerEl) {
17709             this.triggerEl.on("click", this.showTouchView, this);
17710         }
17711         
17712         
17713         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17714         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17715         
17716         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17717         
17718         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17719         this.store.on('load', this.onTouchViewLoad, this);
17720         this.store.on('loadexception', this.onTouchViewLoadException, this);
17721         
17722         if(this.hiddenName){
17723             
17724             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17725             
17726             this.hiddenField.dom.value =
17727                 this.hiddenValue !== undefined ? this.hiddenValue :
17728                 this.value !== undefined ? this.value : '';
17729         
17730             this.el.dom.removeAttribute('name');
17731             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17732         }
17733         
17734         if(this.multiple){
17735             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17736             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17737         }
17738         
17739         if(this.removable && !this.multiple){
17740             var close = this.closeTriggerEl();
17741             if(close){
17742                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17743                 close.on('click', this.removeBtnClick, this, close);
17744             }
17745         }
17746         /*
17747          * fix the bug in Safari iOS8
17748          */
17749         this.inputEl().on("focus", function(e){
17750             document.activeElement.blur();
17751         }, this);
17752         
17753         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17754         
17755         return;
17756         
17757         
17758     },
17759     
17760     renderTouchView : function()
17761     {
17762         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17763         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17764         
17765         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17766         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17767         
17768         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17769         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17770         this.touchViewBodyEl.setStyle('overflow', 'auto');
17771         
17772         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17773         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17774         
17775         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17776         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17777         
17778     },
17779     
17780     showTouchView : function()
17781     {
17782         if(this.disabled){
17783             return;
17784         }
17785         
17786         this.touchViewHeaderEl.hide();
17787
17788         if(this.modalTitle.length){
17789             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17790             this.touchViewHeaderEl.show();
17791         }
17792
17793         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17794         this.touchViewEl.show();
17795
17796         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17797         
17798         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17799         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17800
17801         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17802
17803         if(this.modalTitle.length){
17804             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17805         }
17806         
17807         this.touchViewBodyEl.setHeight(bodyHeight);
17808
17809         if(this.animate){
17810             var _this = this;
17811             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17812         }else{
17813             this.touchViewEl.addClass(['in','show']);
17814         }
17815         
17816         if(this._touchViewMask){
17817             Roo.get(document.body).addClass("x-body-masked");
17818             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17819             this._touchViewMask.setStyle('z-index', 10000);
17820             this._touchViewMask.addClass('show');
17821         }
17822         
17823         this.doTouchViewQuery();
17824         
17825     },
17826     
17827     hideTouchView : function()
17828     {
17829         this.touchViewEl.removeClass(['in','show']);
17830
17831         if(this.animate){
17832             var _this = this;
17833             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17834         }else{
17835             this.touchViewEl.setStyle('display', 'none');
17836         }
17837         
17838         if(this._touchViewMask){
17839             this._touchViewMask.removeClass('show');
17840             Roo.get(document.body).removeClass("x-body-masked");
17841         }
17842     },
17843     
17844     setTouchViewValue : function()
17845     {
17846         if(this.multiple){
17847             this.clearItem();
17848         
17849             var _this = this;
17850
17851             Roo.each(this.tickItems, function(o){
17852                 this.addItem(o);
17853             }, this);
17854         }
17855         
17856         this.hideTouchView();
17857     },
17858     
17859     doTouchViewQuery : function()
17860     {
17861         var qe = {
17862             query: '',
17863             forceAll: true,
17864             combo: this,
17865             cancel:false
17866         };
17867         
17868         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17869             return false;
17870         }
17871         
17872         if(!this.alwaysQuery || this.mode == 'local'){
17873             this.onTouchViewLoad();
17874             return;
17875         }
17876         
17877         this.store.load();
17878     },
17879     
17880     onTouchViewBeforeLoad : function(combo,opts)
17881     {
17882         return;
17883     },
17884
17885     // private
17886     onTouchViewLoad : function()
17887     {
17888         if(this.store.getCount() < 1){
17889             this.onTouchViewEmptyResults();
17890             return;
17891         }
17892         
17893         this.clearTouchView();
17894         
17895         var rawValue = this.getRawValue();
17896         
17897         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17898         
17899         this.tickItems = [];
17900         
17901         this.store.data.each(function(d, rowIndex){
17902             var row = this.touchViewListGroup.createChild(template);
17903             
17904             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17905                 row.addClass(d.data.cls);
17906             }
17907             
17908             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17909                 var cfg = {
17910                     data : d.data,
17911                     html : d.data[this.displayField]
17912                 };
17913                 
17914                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17915                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17916                 }
17917             }
17918             row.removeClass('selected');
17919             if(!this.multiple && this.valueField &&
17920                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17921             {
17922                 // radio buttons..
17923                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17924                 row.addClass('selected');
17925             }
17926             
17927             if(this.multiple && this.valueField &&
17928                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17929             {
17930                 
17931                 // checkboxes...
17932                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17933                 this.tickItems.push(d.data);
17934             }
17935             
17936             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17937             
17938         }, this);
17939         
17940         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17941         
17942         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17943
17944         if(this.modalTitle.length){
17945             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17946         }
17947
17948         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17949         
17950         if(this.mobile_restrict_height && listHeight < bodyHeight){
17951             this.touchViewBodyEl.setHeight(listHeight);
17952         }
17953         
17954         var _this = this;
17955         
17956         if(firstChecked && listHeight > bodyHeight){
17957             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17958         }
17959         
17960     },
17961     
17962     onTouchViewLoadException : function()
17963     {
17964         this.hideTouchView();
17965     },
17966     
17967     onTouchViewEmptyResults : function()
17968     {
17969         this.clearTouchView();
17970         
17971         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17972         
17973         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17974         
17975     },
17976     
17977     clearTouchView : function()
17978     {
17979         this.touchViewListGroup.dom.innerHTML = '';
17980     },
17981     
17982     onTouchViewClick : function(e, el, o)
17983     {
17984         e.preventDefault();
17985         
17986         var row = o.row;
17987         var rowIndex = o.rowIndex;
17988         
17989         var r = this.store.getAt(rowIndex);
17990         
17991         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17992             
17993             if(!this.multiple){
17994                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17995                     c.dom.removeAttribute('checked');
17996                 }, this);
17997
17998                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17999
18000                 this.setFromData(r.data);
18001
18002                 var close = this.closeTriggerEl();
18003
18004                 if(close){
18005                     close.show();
18006                 }
18007
18008                 this.hideTouchView();
18009
18010                 this.fireEvent('select', this, r, rowIndex);
18011
18012                 return;
18013             }
18014
18015             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18016                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18017                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18018                 return;
18019             }
18020
18021             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18022             this.addItem(r.data);
18023             this.tickItems.push(r.data);
18024         }
18025     },
18026     
18027     getAutoCreateNativeIOS : function()
18028     {
18029         var cfg = {
18030             cls: 'form-group' //input-group,
18031         };
18032         
18033         var combobox =  {
18034             tag: 'select',
18035             cls : 'roo-ios-select'
18036         };
18037         
18038         if (this.name) {
18039             combobox.name = this.name;
18040         }
18041         
18042         if (this.disabled) {
18043             combobox.disabled = true;
18044         }
18045         
18046         var settings = this;
18047         
18048         ['xs','sm','md','lg'].map(function(size){
18049             if (settings[size]) {
18050                 cfg.cls += ' col-' + size + '-' + settings[size];
18051             }
18052         });
18053         
18054         cfg.cn = combobox;
18055         
18056         return cfg;
18057         
18058     },
18059     
18060     initIOSView : function()
18061     {
18062         this.store.on('load', this.onIOSViewLoad, this);
18063         
18064         return;
18065     },
18066     
18067     onIOSViewLoad : function()
18068     {
18069         if(this.store.getCount() < 1){
18070             return;
18071         }
18072         
18073         this.clearIOSView();
18074         
18075         if(this.allowBlank) {
18076             
18077             var default_text = '-- SELECT --';
18078             
18079             if(this.placeholder.length){
18080                 default_text = this.placeholder;
18081             }
18082             
18083             if(this.emptyTitle.length){
18084                 default_text += ' - ' + this.emptyTitle + ' -';
18085             }
18086             
18087             var opt = this.inputEl().createChild({
18088                 tag: 'option',
18089                 value : 0,
18090                 html : default_text
18091             });
18092             
18093             var o = {};
18094             o[this.valueField] = 0;
18095             o[this.displayField] = default_text;
18096             
18097             this.ios_options.push({
18098                 data : o,
18099                 el : opt
18100             });
18101             
18102         }
18103         
18104         this.store.data.each(function(d, rowIndex){
18105             
18106             var html = '';
18107             
18108             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18109                 html = d.data[this.displayField];
18110             }
18111             
18112             var value = '';
18113             
18114             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18115                 value = d.data[this.valueField];
18116             }
18117             
18118             var option = {
18119                 tag: 'option',
18120                 value : value,
18121                 html : html
18122             };
18123             
18124             if(this.value == d.data[this.valueField]){
18125                 option['selected'] = true;
18126             }
18127             
18128             var opt = this.inputEl().createChild(option);
18129             
18130             this.ios_options.push({
18131                 data : d.data,
18132                 el : opt
18133             });
18134             
18135         }, this);
18136         
18137         this.inputEl().on('change', function(){
18138            this.fireEvent('select', this);
18139         }, this);
18140         
18141     },
18142     
18143     clearIOSView: function()
18144     {
18145         this.inputEl().dom.innerHTML = '';
18146         
18147         this.ios_options = [];
18148     },
18149     
18150     setIOSValue: function(v)
18151     {
18152         this.value = v;
18153         
18154         if(!this.ios_options){
18155             return;
18156         }
18157         
18158         Roo.each(this.ios_options, function(opts){
18159            
18160            opts.el.dom.removeAttribute('selected');
18161            
18162            if(opts.data[this.valueField] != v){
18163                return;
18164            }
18165            
18166            opts.el.dom.setAttribute('selected', true);
18167            
18168         }, this);
18169     }
18170
18171     /** 
18172     * @cfg {Boolean} grow 
18173     * @hide 
18174     */
18175     /** 
18176     * @cfg {Number} growMin 
18177     * @hide 
18178     */
18179     /** 
18180     * @cfg {Number} growMax 
18181     * @hide 
18182     */
18183     /**
18184      * @hide
18185      * @method autoSize
18186      */
18187 });
18188
18189 Roo.apply(Roo.bootstrap.ComboBox,  {
18190     
18191     header : {
18192         tag: 'div',
18193         cls: 'modal-header',
18194         cn: [
18195             {
18196                 tag: 'h4',
18197                 cls: 'modal-title'
18198             }
18199         ]
18200     },
18201     
18202     body : {
18203         tag: 'div',
18204         cls: 'modal-body',
18205         cn: [
18206             {
18207                 tag: 'ul',
18208                 cls: 'list-group'
18209             }
18210         ]
18211     },
18212     
18213     listItemRadio : {
18214         tag: 'li',
18215         cls: 'list-group-item',
18216         cn: [
18217             {
18218                 tag: 'span',
18219                 cls: 'roo-combobox-list-group-item-value'
18220             },
18221             {
18222                 tag: 'div',
18223                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18224                 cn: [
18225                     {
18226                         tag: 'input',
18227                         type: 'radio'
18228                     },
18229                     {
18230                         tag: 'label'
18231                     }
18232                 ]
18233             }
18234         ]
18235     },
18236     
18237     listItemCheckbox : {
18238         tag: 'li',
18239         cls: 'list-group-item',
18240         cn: [
18241             {
18242                 tag: 'span',
18243                 cls: 'roo-combobox-list-group-item-value'
18244             },
18245             {
18246                 tag: 'div',
18247                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18248                 cn: [
18249                     {
18250                         tag: 'input',
18251                         type: 'checkbox'
18252                     },
18253                     {
18254                         tag: 'label'
18255                     }
18256                 ]
18257             }
18258         ]
18259     },
18260     
18261     emptyResult : {
18262         tag: 'div',
18263         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18264     },
18265     
18266     footer : {
18267         tag: 'div',
18268         cls: 'modal-footer',
18269         cn: [
18270             {
18271                 tag: 'div',
18272                 cls: 'row',
18273                 cn: [
18274                     {
18275                         tag: 'div',
18276                         cls: 'col-xs-6 text-left',
18277                         cn: {
18278                             tag: 'button',
18279                             cls: 'btn btn-danger roo-touch-view-cancel',
18280                             html: 'Cancel'
18281                         }
18282                     },
18283                     {
18284                         tag: 'div',
18285                         cls: 'col-xs-6 text-right',
18286                         cn: {
18287                             tag: 'button',
18288                             cls: 'btn btn-success roo-touch-view-ok',
18289                             html: 'OK'
18290                         }
18291                     }
18292                 ]
18293             }
18294         ]
18295         
18296     }
18297 });
18298
18299 Roo.apply(Roo.bootstrap.ComboBox,  {
18300     
18301     touchViewTemplate : {
18302         tag: 'div',
18303         cls: 'modal fade roo-combobox-touch-view',
18304         cn: [
18305             {
18306                 tag: 'div',
18307                 cls: 'modal-dialog',
18308                 style : 'position:fixed', // we have to fix position....
18309                 cn: [
18310                     {
18311                         tag: 'div',
18312                         cls: 'modal-content',
18313                         cn: [
18314                             Roo.bootstrap.ComboBox.header,
18315                             Roo.bootstrap.ComboBox.body,
18316                             Roo.bootstrap.ComboBox.footer
18317                         ]
18318                     }
18319                 ]
18320             }
18321         ]
18322     }
18323 });/*
18324  * Based on:
18325  * Ext JS Library 1.1.1
18326  * Copyright(c) 2006-2007, Ext JS, LLC.
18327  *
18328  * Originally Released Under LGPL - original licence link has changed is not relivant.
18329  *
18330  * Fork - LGPL
18331  * <script type="text/javascript">
18332  */
18333
18334 /**
18335  * @class Roo.View
18336  * @extends Roo.util.Observable
18337  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18338  * This class also supports single and multi selection modes. <br>
18339  * Create a data model bound view:
18340  <pre><code>
18341  var store = new Roo.data.Store(...);
18342
18343  var view = new Roo.View({
18344     el : "my-element",
18345     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18346  
18347     singleSelect: true,
18348     selectedClass: "ydataview-selected",
18349     store: store
18350  });
18351
18352  // listen for node click?
18353  view.on("click", function(vw, index, node, e){
18354  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18355  });
18356
18357  // load XML data
18358  dataModel.load("foobar.xml");
18359  </code></pre>
18360  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18361  * <br><br>
18362  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18363  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18364  * 
18365  * Note: old style constructor is still suported (container, template, config)
18366  * 
18367  * @constructor
18368  * Create a new View
18369  * @param {Object} config The config object
18370  * 
18371  */
18372 Roo.View = function(config, depreciated_tpl, depreciated_config){
18373     
18374     this.parent = false;
18375     
18376     if (typeof(depreciated_tpl) == 'undefined') {
18377         // new way.. - universal constructor.
18378         Roo.apply(this, config);
18379         this.el  = Roo.get(this.el);
18380     } else {
18381         // old format..
18382         this.el  = Roo.get(config);
18383         this.tpl = depreciated_tpl;
18384         Roo.apply(this, depreciated_config);
18385     }
18386     this.wrapEl  = this.el.wrap().wrap();
18387     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18388     
18389     
18390     if(typeof(this.tpl) == "string"){
18391         this.tpl = new Roo.Template(this.tpl);
18392     } else {
18393         // support xtype ctors..
18394         this.tpl = new Roo.factory(this.tpl, Roo);
18395     }
18396     
18397     
18398     this.tpl.compile();
18399     
18400     /** @private */
18401     this.addEvents({
18402         /**
18403          * @event beforeclick
18404          * Fires before a click is processed. Returns false to cancel the default action.
18405          * @param {Roo.View} this
18406          * @param {Number} index The index of the target node
18407          * @param {HTMLElement} node The target node
18408          * @param {Roo.EventObject} e The raw event object
18409          */
18410             "beforeclick" : true,
18411         /**
18412          * @event click
18413          * Fires when a template node is clicked.
18414          * @param {Roo.View} this
18415          * @param {Number} index The index of the target node
18416          * @param {HTMLElement} node The target node
18417          * @param {Roo.EventObject} e The raw event object
18418          */
18419             "click" : true,
18420         /**
18421          * @event dblclick
18422          * Fires when a template node is double clicked.
18423          * @param {Roo.View} this
18424          * @param {Number} index The index of the target node
18425          * @param {HTMLElement} node The target node
18426          * @param {Roo.EventObject} e The raw event object
18427          */
18428             "dblclick" : true,
18429         /**
18430          * @event contextmenu
18431          * Fires when a template node is right clicked.
18432          * @param {Roo.View} this
18433          * @param {Number} index The index of the target node
18434          * @param {HTMLElement} node The target node
18435          * @param {Roo.EventObject} e The raw event object
18436          */
18437             "contextmenu" : true,
18438         /**
18439          * @event selectionchange
18440          * Fires when the selected nodes change.
18441          * @param {Roo.View} this
18442          * @param {Array} selections Array of the selected nodes
18443          */
18444             "selectionchange" : true,
18445     
18446         /**
18447          * @event beforeselect
18448          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18449          * @param {Roo.View} this
18450          * @param {HTMLElement} node The node to be selected
18451          * @param {Array} selections Array of currently selected nodes
18452          */
18453             "beforeselect" : true,
18454         /**
18455          * @event preparedata
18456          * Fires on every row to render, to allow you to change the data.
18457          * @param {Roo.View} this
18458          * @param {Object} data to be rendered (change this)
18459          */
18460           "preparedata" : true
18461           
18462           
18463         });
18464
18465
18466
18467     this.el.on({
18468         "click": this.onClick,
18469         "dblclick": this.onDblClick,
18470         "contextmenu": this.onContextMenu,
18471         scope:this
18472     });
18473
18474     this.selections = [];
18475     this.nodes = [];
18476     this.cmp = new Roo.CompositeElementLite([]);
18477     if(this.store){
18478         this.store = Roo.factory(this.store, Roo.data);
18479         this.setStore(this.store, true);
18480     }
18481     
18482     if ( this.footer && this.footer.xtype) {
18483            
18484          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18485         
18486         this.footer.dataSource = this.store;
18487         this.footer.container = fctr;
18488         this.footer = Roo.factory(this.footer, Roo);
18489         fctr.insertFirst(this.el);
18490         
18491         // this is a bit insane - as the paging toolbar seems to detach the el..
18492 //        dom.parentNode.parentNode.parentNode
18493          // they get detached?
18494     }
18495     
18496     
18497     Roo.View.superclass.constructor.call(this);
18498     
18499     
18500 };
18501
18502 Roo.extend(Roo.View, Roo.util.Observable, {
18503     
18504      /**
18505      * @cfg {Roo.data.Store} store Data store to load data from.
18506      */
18507     store : false,
18508     
18509     /**
18510      * @cfg {String|Roo.Element} el The container element.
18511      */
18512     el : '',
18513     
18514     /**
18515      * @cfg {String|Roo.Template} tpl The template used by this View 
18516      */
18517     tpl : false,
18518     /**
18519      * @cfg {String} dataName the named area of the template to use as the data area
18520      *                          Works with domtemplates roo-name="name"
18521      */
18522     dataName: false,
18523     /**
18524      * @cfg {String} selectedClass The css class to add to selected nodes
18525      */
18526     selectedClass : "x-view-selected",
18527      /**
18528      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18529      */
18530     emptyText : "",
18531     
18532     /**
18533      * @cfg {String} text to display on mask (default Loading)
18534      */
18535     mask : false,
18536     /**
18537      * @cfg {Boolean} multiSelect Allow multiple selection
18538      */
18539     multiSelect : false,
18540     /**
18541      * @cfg {Boolean} singleSelect Allow single selection
18542      */
18543     singleSelect:  false,
18544     
18545     /**
18546      * @cfg {Boolean} toggleSelect - selecting 
18547      */
18548     toggleSelect : false,
18549     
18550     /**
18551      * @cfg {Boolean} tickable - selecting 
18552      */
18553     tickable : false,
18554     
18555     /**
18556      * Returns the element this view is bound to.
18557      * @return {Roo.Element}
18558      */
18559     getEl : function(){
18560         return this.wrapEl;
18561     },
18562     
18563     
18564
18565     /**
18566      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18567      */
18568     refresh : function(){
18569         //Roo.log('refresh');
18570         var t = this.tpl;
18571         
18572         // if we are using something like 'domtemplate', then
18573         // the what gets used is:
18574         // t.applySubtemplate(NAME, data, wrapping data..)
18575         // the outer template then get' applied with
18576         //     the store 'extra data'
18577         // and the body get's added to the
18578         //      roo-name="data" node?
18579         //      <span class='roo-tpl-{name}'></span> ?????
18580         
18581         
18582         
18583         this.clearSelections();
18584         this.el.update("");
18585         var html = [];
18586         var records = this.store.getRange();
18587         if(records.length < 1) {
18588             
18589             // is this valid??  = should it render a template??
18590             
18591             this.el.update(this.emptyText);
18592             return;
18593         }
18594         var el = this.el;
18595         if (this.dataName) {
18596             this.el.update(t.apply(this.store.meta)); //????
18597             el = this.el.child('.roo-tpl-' + this.dataName);
18598         }
18599         
18600         for(var i = 0, len = records.length; i < len; i++){
18601             var data = this.prepareData(records[i].data, i, records[i]);
18602             this.fireEvent("preparedata", this, data, i, records[i]);
18603             
18604             var d = Roo.apply({}, data);
18605             
18606             if(this.tickable){
18607                 Roo.apply(d, {'roo-id' : Roo.id()});
18608                 
18609                 var _this = this;
18610             
18611                 Roo.each(this.parent.item, function(item){
18612                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18613                         return;
18614                     }
18615                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18616                 });
18617             }
18618             
18619             html[html.length] = Roo.util.Format.trim(
18620                 this.dataName ?
18621                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18622                     t.apply(d)
18623             );
18624         }
18625         
18626         
18627         
18628         el.update(html.join(""));
18629         this.nodes = el.dom.childNodes;
18630         this.updateIndexes(0);
18631     },
18632     
18633
18634     /**
18635      * Function to override to reformat the data that is sent to
18636      * the template for each node.
18637      * DEPRICATED - use the preparedata event handler.
18638      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18639      * a JSON object for an UpdateManager bound view).
18640      */
18641     prepareData : function(data, index, record)
18642     {
18643         this.fireEvent("preparedata", this, data, index, record);
18644         return data;
18645     },
18646
18647     onUpdate : function(ds, record){
18648         // Roo.log('on update');   
18649         this.clearSelections();
18650         var index = this.store.indexOf(record);
18651         var n = this.nodes[index];
18652         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18653         n.parentNode.removeChild(n);
18654         this.updateIndexes(index, index);
18655     },
18656
18657     
18658     
18659 // --------- FIXME     
18660     onAdd : function(ds, records, index)
18661     {
18662         //Roo.log(['on Add', ds, records, index] );        
18663         this.clearSelections();
18664         if(this.nodes.length == 0){
18665             this.refresh();
18666             return;
18667         }
18668         var n = this.nodes[index];
18669         for(var i = 0, len = records.length; i < len; i++){
18670             var d = this.prepareData(records[i].data, i, records[i]);
18671             if(n){
18672                 this.tpl.insertBefore(n, d);
18673             }else{
18674                 
18675                 this.tpl.append(this.el, d);
18676             }
18677         }
18678         this.updateIndexes(index);
18679     },
18680
18681     onRemove : function(ds, record, index){
18682        // Roo.log('onRemove');
18683         this.clearSelections();
18684         var el = this.dataName  ?
18685             this.el.child('.roo-tpl-' + this.dataName) :
18686             this.el; 
18687         
18688         el.dom.removeChild(this.nodes[index]);
18689         this.updateIndexes(index);
18690     },
18691
18692     /**
18693      * Refresh an individual node.
18694      * @param {Number} index
18695      */
18696     refreshNode : function(index){
18697         this.onUpdate(this.store, this.store.getAt(index));
18698     },
18699
18700     updateIndexes : function(startIndex, endIndex){
18701         var ns = this.nodes;
18702         startIndex = startIndex || 0;
18703         endIndex = endIndex || ns.length - 1;
18704         for(var i = startIndex; i <= endIndex; i++){
18705             ns[i].nodeIndex = i;
18706         }
18707     },
18708
18709     /**
18710      * Changes the data store this view uses and refresh the view.
18711      * @param {Store} store
18712      */
18713     setStore : function(store, initial){
18714         if(!initial && this.store){
18715             this.store.un("datachanged", this.refresh);
18716             this.store.un("add", this.onAdd);
18717             this.store.un("remove", this.onRemove);
18718             this.store.un("update", this.onUpdate);
18719             this.store.un("clear", this.refresh);
18720             this.store.un("beforeload", this.onBeforeLoad);
18721             this.store.un("load", this.onLoad);
18722             this.store.un("loadexception", this.onLoad);
18723         }
18724         if(store){
18725           
18726             store.on("datachanged", this.refresh, this);
18727             store.on("add", this.onAdd, this);
18728             store.on("remove", this.onRemove, this);
18729             store.on("update", this.onUpdate, this);
18730             store.on("clear", this.refresh, this);
18731             store.on("beforeload", this.onBeforeLoad, this);
18732             store.on("load", this.onLoad, this);
18733             store.on("loadexception", this.onLoad, this);
18734         }
18735         
18736         if(store){
18737             this.refresh();
18738         }
18739     },
18740     /**
18741      * onbeforeLoad - masks the loading area.
18742      *
18743      */
18744     onBeforeLoad : function(store,opts)
18745     {
18746          //Roo.log('onBeforeLoad');   
18747         if (!opts.add) {
18748             this.el.update("");
18749         }
18750         this.el.mask(this.mask ? this.mask : "Loading" ); 
18751     },
18752     onLoad : function ()
18753     {
18754         this.el.unmask();
18755     },
18756     
18757
18758     /**
18759      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18760      * @param {HTMLElement} node
18761      * @return {HTMLElement} The template node
18762      */
18763     findItemFromChild : function(node){
18764         var el = this.dataName  ?
18765             this.el.child('.roo-tpl-' + this.dataName,true) :
18766             this.el.dom; 
18767         
18768         if(!node || node.parentNode == el){
18769                     return node;
18770             }
18771             var p = node.parentNode;
18772             while(p && p != el){
18773             if(p.parentNode == el){
18774                 return p;
18775             }
18776             p = p.parentNode;
18777         }
18778             return null;
18779     },
18780
18781     /** @ignore */
18782     onClick : function(e){
18783         var item = this.findItemFromChild(e.getTarget());
18784         if(item){
18785             var index = this.indexOf(item);
18786             if(this.onItemClick(item, index, e) !== false){
18787                 this.fireEvent("click", this, index, item, e);
18788             }
18789         }else{
18790             this.clearSelections();
18791         }
18792     },
18793
18794     /** @ignore */
18795     onContextMenu : function(e){
18796         var item = this.findItemFromChild(e.getTarget());
18797         if(item){
18798             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18799         }
18800     },
18801
18802     /** @ignore */
18803     onDblClick : function(e){
18804         var item = this.findItemFromChild(e.getTarget());
18805         if(item){
18806             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18807         }
18808     },
18809
18810     onItemClick : function(item, index, e)
18811     {
18812         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18813             return false;
18814         }
18815         if (this.toggleSelect) {
18816             var m = this.isSelected(item) ? 'unselect' : 'select';
18817             //Roo.log(m);
18818             var _t = this;
18819             _t[m](item, true, false);
18820             return true;
18821         }
18822         if(this.multiSelect || this.singleSelect){
18823             if(this.multiSelect && e.shiftKey && this.lastSelection){
18824                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18825             }else{
18826                 this.select(item, this.multiSelect && e.ctrlKey);
18827                 this.lastSelection = item;
18828             }
18829             
18830             if(!this.tickable){
18831                 e.preventDefault();
18832             }
18833             
18834         }
18835         return true;
18836     },
18837
18838     /**
18839      * Get the number of selected nodes.
18840      * @return {Number}
18841      */
18842     getSelectionCount : function(){
18843         return this.selections.length;
18844     },
18845
18846     /**
18847      * Get the currently selected nodes.
18848      * @return {Array} An array of HTMLElements
18849      */
18850     getSelectedNodes : function(){
18851         return this.selections;
18852     },
18853
18854     /**
18855      * Get the indexes of the selected nodes.
18856      * @return {Array}
18857      */
18858     getSelectedIndexes : function(){
18859         var indexes = [], s = this.selections;
18860         for(var i = 0, len = s.length; i < len; i++){
18861             indexes.push(s[i].nodeIndex);
18862         }
18863         return indexes;
18864     },
18865
18866     /**
18867      * Clear all selections
18868      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18869      */
18870     clearSelections : function(suppressEvent){
18871         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18872             this.cmp.elements = this.selections;
18873             this.cmp.removeClass(this.selectedClass);
18874             this.selections = [];
18875             if(!suppressEvent){
18876                 this.fireEvent("selectionchange", this, this.selections);
18877             }
18878         }
18879     },
18880
18881     /**
18882      * Returns true if the passed node is selected
18883      * @param {HTMLElement/Number} node The node or node index
18884      * @return {Boolean}
18885      */
18886     isSelected : function(node){
18887         var s = this.selections;
18888         if(s.length < 1){
18889             return false;
18890         }
18891         node = this.getNode(node);
18892         return s.indexOf(node) !== -1;
18893     },
18894
18895     /**
18896      * Selects nodes.
18897      * @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
18898      * @param {Boolean} keepExisting (optional) true to keep existing selections
18899      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18900      */
18901     select : function(nodeInfo, keepExisting, suppressEvent){
18902         if(nodeInfo instanceof Array){
18903             if(!keepExisting){
18904                 this.clearSelections(true);
18905             }
18906             for(var i = 0, len = nodeInfo.length; i < len; i++){
18907                 this.select(nodeInfo[i], true, true);
18908             }
18909             return;
18910         } 
18911         var node = this.getNode(nodeInfo);
18912         if(!node || this.isSelected(node)){
18913             return; // already selected.
18914         }
18915         if(!keepExisting){
18916             this.clearSelections(true);
18917         }
18918         
18919         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18920             Roo.fly(node).addClass(this.selectedClass);
18921             this.selections.push(node);
18922             if(!suppressEvent){
18923                 this.fireEvent("selectionchange", this, this.selections);
18924             }
18925         }
18926         
18927         
18928     },
18929       /**
18930      * Unselects nodes.
18931      * @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
18932      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18933      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18934      */
18935     unselect : function(nodeInfo, keepExisting, suppressEvent)
18936     {
18937         if(nodeInfo instanceof Array){
18938             Roo.each(this.selections, function(s) {
18939                 this.unselect(s, nodeInfo);
18940             }, this);
18941             return;
18942         }
18943         var node = this.getNode(nodeInfo);
18944         if(!node || !this.isSelected(node)){
18945             //Roo.log("not selected");
18946             return; // not selected.
18947         }
18948         // fireevent???
18949         var ns = [];
18950         Roo.each(this.selections, function(s) {
18951             if (s == node ) {
18952                 Roo.fly(node).removeClass(this.selectedClass);
18953
18954                 return;
18955             }
18956             ns.push(s);
18957         },this);
18958         
18959         this.selections= ns;
18960         this.fireEvent("selectionchange", this, this.selections);
18961     },
18962
18963     /**
18964      * Gets a template node.
18965      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18966      * @return {HTMLElement} The node or null if it wasn't found
18967      */
18968     getNode : function(nodeInfo){
18969         if(typeof nodeInfo == "string"){
18970             return document.getElementById(nodeInfo);
18971         }else if(typeof nodeInfo == "number"){
18972             return this.nodes[nodeInfo];
18973         }
18974         return nodeInfo;
18975     },
18976
18977     /**
18978      * Gets a range template nodes.
18979      * @param {Number} startIndex
18980      * @param {Number} endIndex
18981      * @return {Array} An array of nodes
18982      */
18983     getNodes : function(start, end){
18984         var ns = this.nodes;
18985         start = start || 0;
18986         end = typeof end == "undefined" ? ns.length - 1 : end;
18987         var nodes = [];
18988         if(start <= end){
18989             for(var i = start; i <= end; i++){
18990                 nodes.push(ns[i]);
18991             }
18992         } else{
18993             for(var i = start; i >= end; i--){
18994                 nodes.push(ns[i]);
18995             }
18996         }
18997         return nodes;
18998     },
18999
19000     /**
19001      * Finds the index of the passed node
19002      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19003      * @return {Number} The index of the node or -1
19004      */
19005     indexOf : function(node){
19006         node = this.getNode(node);
19007         if(typeof node.nodeIndex == "number"){
19008             return node.nodeIndex;
19009         }
19010         var ns = this.nodes;
19011         for(var i = 0, len = ns.length; i < len; i++){
19012             if(ns[i] == node){
19013                 return i;
19014             }
19015         }
19016         return -1;
19017     }
19018 });
19019 /*
19020  * - LGPL
19021  *
19022  * based on jquery fullcalendar
19023  * 
19024  */
19025
19026 Roo.bootstrap = Roo.bootstrap || {};
19027 /**
19028  * @class Roo.bootstrap.Calendar
19029  * @extends Roo.bootstrap.Component
19030  * Bootstrap Calendar class
19031  * @cfg {Boolean} loadMask (true|false) default false
19032  * @cfg {Object} header generate the user specific header of the calendar, default false
19033
19034  * @constructor
19035  * Create a new Container
19036  * @param {Object} config The config object
19037  */
19038
19039
19040
19041 Roo.bootstrap.Calendar = function(config){
19042     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19043      this.addEvents({
19044         /**
19045              * @event select
19046              * Fires when a date is selected
19047              * @param {DatePicker} this
19048              * @param {Date} date The selected date
19049              */
19050         'select': true,
19051         /**
19052              * @event monthchange
19053              * Fires when the displayed month changes 
19054              * @param {DatePicker} this
19055              * @param {Date} date The selected month
19056              */
19057         'monthchange': true,
19058         /**
19059              * @event evententer
19060              * Fires when mouse over an event
19061              * @param {Calendar} this
19062              * @param {event} Event
19063              */
19064         'evententer': true,
19065         /**
19066              * @event eventleave
19067              * Fires when the mouse leaves an
19068              * @param {Calendar} this
19069              * @param {event}
19070              */
19071         'eventleave': true,
19072         /**
19073              * @event eventclick
19074              * Fires when the mouse click an
19075              * @param {Calendar} this
19076              * @param {event}
19077              */
19078         'eventclick': true
19079         
19080     });
19081
19082 };
19083
19084 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19085     
19086      /**
19087      * @cfg {Number} startDay
19088      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19089      */
19090     startDay : 0,
19091     
19092     loadMask : false,
19093     
19094     header : false,
19095       
19096     getAutoCreate : function(){
19097         
19098         
19099         var fc_button = function(name, corner, style, content ) {
19100             return Roo.apply({},{
19101                 tag : 'span',
19102                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19103                          (corner.length ?
19104                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19105                             ''
19106                         ),
19107                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19108                 unselectable: 'on'
19109             });
19110         };
19111         
19112         var header = {};
19113         
19114         if(!this.header){
19115             header = {
19116                 tag : 'table',
19117                 cls : 'fc-header',
19118                 style : 'width:100%',
19119                 cn : [
19120                     {
19121                         tag: 'tr',
19122                         cn : [
19123                             {
19124                                 tag : 'td',
19125                                 cls : 'fc-header-left',
19126                                 cn : [
19127                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19128                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19129                                     { tag: 'span', cls: 'fc-header-space' },
19130                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19131
19132
19133                                 ]
19134                             },
19135
19136                             {
19137                                 tag : 'td',
19138                                 cls : 'fc-header-center',
19139                                 cn : [
19140                                     {
19141                                         tag: 'span',
19142                                         cls: 'fc-header-title',
19143                                         cn : {
19144                                             tag: 'H2',
19145                                             html : 'month / year'
19146                                         }
19147                                     }
19148
19149                                 ]
19150                             },
19151                             {
19152                                 tag : 'td',
19153                                 cls : 'fc-header-right',
19154                                 cn : [
19155                               /*      fc_button('month', 'left', '', 'month' ),
19156                                     fc_button('week', '', '', 'week' ),
19157                                     fc_button('day', 'right', '', 'day' )
19158                                 */    
19159
19160                                 ]
19161                             }
19162
19163                         ]
19164                     }
19165                 ]
19166             };
19167         }
19168         
19169         header = this.header;
19170         
19171        
19172         var cal_heads = function() {
19173             var ret = [];
19174             // fixme - handle this.
19175             
19176             for (var i =0; i < Date.dayNames.length; i++) {
19177                 var d = Date.dayNames[i];
19178                 ret.push({
19179                     tag: 'th',
19180                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19181                     html : d.substring(0,3)
19182                 });
19183                 
19184             }
19185             ret[0].cls += ' fc-first';
19186             ret[6].cls += ' fc-last';
19187             return ret;
19188         };
19189         var cal_cell = function(n) {
19190             return  {
19191                 tag: 'td',
19192                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19193                 cn : [
19194                     {
19195                         cn : [
19196                             {
19197                                 cls: 'fc-day-number',
19198                                 html: 'D'
19199                             },
19200                             {
19201                                 cls: 'fc-day-content',
19202                              
19203                                 cn : [
19204                                      {
19205                                         style: 'position: relative;' // height: 17px;
19206                                     }
19207                                 ]
19208                             }
19209                             
19210                             
19211                         ]
19212                     }
19213                 ]
19214                 
19215             }
19216         };
19217         var cal_rows = function() {
19218             
19219             var ret = [];
19220             for (var r = 0; r < 6; r++) {
19221                 var row= {
19222                     tag : 'tr',
19223                     cls : 'fc-week',
19224                     cn : []
19225                 };
19226                 
19227                 for (var i =0; i < Date.dayNames.length; i++) {
19228                     var d = Date.dayNames[i];
19229                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19230
19231                 }
19232                 row.cn[0].cls+=' fc-first';
19233                 row.cn[0].cn[0].style = 'min-height:90px';
19234                 row.cn[6].cls+=' fc-last';
19235                 ret.push(row);
19236                 
19237             }
19238             ret[0].cls += ' fc-first';
19239             ret[4].cls += ' fc-prev-last';
19240             ret[5].cls += ' fc-last';
19241             return ret;
19242             
19243         };
19244         
19245         var cal_table = {
19246             tag: 'table',
19247             cls: 'fc-border-separate',
19248             style : 'width:100%',
19249             cellspacing  : 0,
19250             cn : [
19251                 { 
19252                     tag: 'thead',
19253                     cn : [
19254                         { 
19255                             tag: 'tr',
19256                             cls : 'fc-first fc-last',
19257                             cn : cal_heads()
19258                         }
19259                     ]
19260                 },
19261                 { 
19262                     tag: 'tbody',
19263                     cn : cal_rows()
19264                 }
19265                   
19266             ]
19267         };
19268          
19269          var cfg = {
19270             cls : 'fc fc-ltr',
19271             cn : [
19272                 header,
19273                 {
19274                     cls : 'fc-content',
19275                     style : "position: relative;",
19276                     cn : [
19277                         {
19278                             cls : 'fc-view fc-view-month fc-grid',
19279                             style : 'position: relative',
19280                             unselectable : 'on',
19281                             cn : [
19282                                 {
19283                                     cls : 'fc-event-container',
19284                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19285                                 },
19286                                 cal_table
19287                             ]
19288                         }
19289                     ]
19290     
19291                 }
19292            ] 
19293             
19294         };
19295         
19296          
19297         
19298         return cfg;
19299     },
19300     
19301     
19302     initEvents : function()
19303     {
19304         if(!this.store){
19305             throw "can not find store for calendar";
19306         }
19307         
19308         var mark = {
19309             tag: "div",
19310             cls:"x-dlg-mask",
19311             style: "text-align:center",
19312             cn: [
19313                 {
19314                     tag: "div",
19315                     style: "background-color:white;width:50%;margin:250 auto",
19316                     cn: [
19317                         {
19318                             tag: "img",
19319                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19320                         },
19321                         {
19322                             tag: "span",
19323                             html: "Loading"
19324                         }
19325                         
19326                     ]
19327                 }
19328             ]
19329         };
19330         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19331         
19332         var size = this.el.select('.fc-content', true).first().getSize();
19333         this.maskEl.setSize(size.width, size.height);
19334         this.maskEl.enableDisplayMode("block");
19335         if(!this.loadMask){
19336             this.maskEl.hide();
19337         }
19338         
19339         this.store = Roo.factory(this.store, Roo.data);
19340         this.store.on('load', this.onLoad, this);
19341         this.store.on('beforeload', this.onBeforeLoad, this);
19342         
19343         this.resize();
19344         
19345         this.cells = this.el.select('.fc-day',true);
19346         //Roo.log(this.cells);
19347         this.textNodes = this.el.query('.fc-day-number');
19348         this.cells.addClassOnOver('fc-state-hover');
19349         
19350         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19351         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19352         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19353         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19354         
19355         this.on('monthchange', this.onMonthChange, this);
19356         
19357         this.update(new Date().clearTime());
19358     },
19359     
19360     resize : function() {
19361         var sz  = this.el.getSize();
19362         
19363         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19364         this.el.select('.fc-day-content div',true).setHeight(34);
19365     },
19366     
19367     
19368     // private
19369     showPrevMonth : function(e){
19370         this.update(this.activeDate.add("mo", -1));
19371     },
19372     showToday : function(e){
19373         this.update(new Date().clearTime());
19374     },
19375     // private
19376     showNextMonth : function(e){
19377         this.update(this.activeDate.add("mo", 1));
19378     },
19379
19380     // private
19381     showPrevYear : function(){
19382         this.update(this.activeDate.add("y", -1));
19383     },
19384
19385     // private
19386     showNextYear : function(){
19387         this.update(this.activeDate.add("y", 1));
19388     },
19389
19390     
19391    // private
19392     update : function(date)
19393     {
19394         var vd = this.activeDate;
19395         this.activeDate = date;
19396 //        if(vd && this.el){
19397 //            var t = date.getTime();
19398 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19399 //                Roo.log('using add remove');
19400 //                
19401 //                this.fireEvent('monthchange', this, date);
19402 //                
19403 //                this.cells.removeClass("fc-state-highlight");
19404 //                this.cells.each(function(c){
19405 //                   if(c.dateValue == t){
19406 //                       c.addClass("fc-state-highlight");
19407 //                       setTimeout(function(){
19408 //                            try{c.dom.firstChild.focus();}catch(e){}
19409 //                       }, 50);
19410 //                       return false;
19411 //                   }
19412 //                   return true;
19413 //                });
19414 //                return;
19415 //            }
19416 //        }
19417         
19418         var days = date.getDaysInMonth();
19419         
19420         var firstOfMonth = date.getFirstDateOfMonth();
19421         var startingPos = firstOfMonth.getDay()-this.startDay;
19422         
19423         if(startingPos < this.startDay){
19424             startingPos += 7;
19425         }
19426         
19427         var pm = date.add(Date.MONTH, -1);
19428         var prevStart = pm.getDaysInMonth()-startingPos;
19429 //        
19430         this.cells = this.el.select('.fc-day',true);
19431         this.textNodes = this.el.query('.fc-day-number');
19432         this.cells.addClassOnOver('fc-state-hover');
19433         
19434         var cells = this.cells.elements;
19435         var textEls = this.textNodes;
19436         
19437         Roo.each(cells, function(cell){
19438             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19439         });
19440         
19441         days += startingPos;
19442
19443         // convert everything to numbers so it's fast
19444         var day = 86400000;
19445         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19446         //Roo.log(d);
19447         //Roo.log(pm);
19448         //Roo.log(prevStart);
19449         
19450         var today = new Date().clearTime().getTime();
19451         var sel = date.clearTime().getTime();
19452         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19453         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19454         var ddMatch = this.disabledDatesRE;
19455         var ddText = this.disabledDatesText;
19456         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19457         var ddaysText = this.disabledDaysText;
19458         var format = this.format;
19459         
19460         var setCellClass = function(cal, cell){
19461             cell.row = 0;
19462             cell.events = [];
19463             cell.more = [];
19464             //Roo.log('set Cell Class');
19465             cell.title = "";
19466             var t = d.getTime();
19467             
19468             //Roo.log(d);
19469             
19470             cell.dateValue = t;
19471             if(t == today){
19472                 cell.className += " fc-today";
19473                 cell.className += " fc-state-highlight";
19474                 cell.title = cal.todayText;
19475             }
19476             if(t == sel){
19477                 // disable highlight in other month..
19478                 //cell.className += " fc-state-highlight";
19479                 
19480             }
19481             // disabling
19482             if(t < min) {
19483                 cell.className = " fc-state-disabled";
19484                 cell.title = cal.minText;
19485                 return;
19486             }
19487             if(t > max) {
19488                 cell.className = " fc-state-disabled";
19489                 cell.title = cal.maxText;
19490                 return;
19491             }
19492             if(ddays){
19493                 if(ddays.indexOf(d.getDay()) != -1){
19494                     cell.title = ddaysText;
19495                     cell.className = " fc-state-disabled";
19496                 }
19497             }
19498             if(ddMatch && format){
19499                 var fvalue = d.dateFormat(format);
19500                 if(ddMatch.test(fvalue)){
19501                     cell.title = ddText.replace("%0", fvalue);
19502                     cell.className = " fc-state-disabled";
19503                 }
19504             }
19505             
19506             if (!cell.initialClassName) {
19507                 cell.initialClassName = cell.dom.className;
19508             }
19509             
19510             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19511         };
19512
19513         var i = 0;
19514         
19515         for(; i < startingPos; i++) {
19516             textEls[i].innerHTML = (++prevStart);
19517             d.setDate(d.getDate()+1);
19518             
19519             cells[i].className = "fc-past fc-other-month";
19520             setCellClass(this, cells[i]);
19521         }
19522         
19523         var intDay = 0;
19524         
19525         for(; i < days; i++){
19526             intDay = i - startingPos + 1;
19527             textEls[i].innerHTML = (intDay);
19528             d.setDate(d.getDate()+1);
19529             
19530             cells[i].className = ''; // "x-date-active";
19531             setCellClass(this, cells[i]);
19532         }
19533         var extraDays = 0;
19534         
19535         for(; i < 42; i++) {
19536             textEls[i].innerHTML = (++extraDays);
19537             d.setDate(d.getDate()+1);
19538             
19539             cells[i].className = "fc-future fc-other-month";
19540             setCellClass(this, cells[i]);
19541         }
19542         
19543         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19544         
19545         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19546         
19547         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19548         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19549         
19550         if(totalRows != 6){
19551             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19552             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19553         }
19554         
19555         this.fireEvent('monthchange', this, date);
19556         
19557         
19558         /*
19559         if(!this.internalRender){
19560             var main = this.el.dom.firstChild;
19561             var w = main.offsetWidth;
19562             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19563             Roo.fly(main).setWidth(w);
19564             this.internalRender = true;
19565             // opera does not respect the auto grow header center column
19566             // then, after it gets a width opera refuses to recalculate
19567             // without a second pass
19568             if(Roo.isOpera && !this.secondPass){
19569                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19570                 this.secondPass = true;
19571                 this.update.defer(10, this, [date]);
19572             }
19573         }
19574         */
19575         
19576     },
19577     
19578     findCell : function(dt) {
19579         dt = dt.clearTime().getTime();
19580         var ret = false;
19581         this.cells.each(function(c){
19582             //Roo.log("check " +c.dateValue + '?=' + dt);
19583             if(c.dateValue == dt){
19584                 ret = c;
19585                 return false;
19586             }
19587             return true;
19588         });
19589         
19590         return ret;
19591     },
19592     
19593     findCells : function(ev) {
19594         var s = ev.start.clone().clearTime().getTime();
19595        // Roo.log(s);
19596         var e= ev.end.clone().clearTime().getTime();
19597        // Roo.log(e);
19598         var ret = [];
19599         this.cells.each(function(c){
19600              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19601             
19602             if(c.dateValue > e){
19603                 return ;
19604             }
19605             if(c.dateValue < s){
19606                 return ;
19607             }
19608             ret.push(c);
19609         });
19610         
19611         return ret;    
19612     },
19613     
19614 //    findBestRow: function(cells)
19615 //    {
19616 //        var ret = 0;
19617 //        
19618 //        for (var i =0 ; i < cells.length;i++) {
19619 //            ret  = Math.max(cells[i].rows || 0,ret);
19620 //        }
19621 //        return ret;
19622 //        
19623 //    },
19624     
19625     
19626     addItem : function(ev)
19627     {
19628         // look for vertical location slot in
19629         var cells = this.findCells(ev);
19630         
19631 //        ev.row = this.findBestRow(cells);
19632         
19633         // work out the location.
19634         
19635         var crow = false;
19636         var rows = [];
19637         for(var i =0; i < cells.length; i++) {
19638             
19639             cells[i].row = cells[0].row;
19640             
19641             if(i == 0){
19642                 cells[i].row = cells[i].row + 1;
19643             }
19644             
19645             if (!crow) {
19646                 crow = {
19647                     start : cells[i],
19648                     end :  cells[i]
19649                 };
19650                 continue;
19651             }
19652             if (crow.start.getY() == cells[i].getY()) {
19653                 // on same row.
19654                 crow.end = cells[i];
19655                 continue;
19656             }
19657             // different row.
19658             rows.push(crow);
19659             crow = {
19660                 start: cells[i],
19661                 end : cells[i]
19662             };
19663             
19664         }
19665         
19666         rows.push(crow);
19667         ev.els = [];
19668         ev.rows = rows;
19669         ev.cells = cells;
19670         
19671         cells[0].events.push(ev);
19672         
19673         this.calevents.push(ev);
19674     },
19675     
19676     clearEvents: function() {
19677         
19678         if(!this.calevents){
19679             return;
19680         }
19681         
19682         Roo.each(this.cells.elements, function(c){
19683             c.row = 0;
19684             c.events = [];
19685             c.more = [];
19686         });
19687         
19688         Roo.each(this.calevents, function(e) {
19689             Roo.each(e.els, function(el) {
19690                 el.un('mouseenter' ,this.onEventEnter, this);
19691                 el.un('mouseleave' ,this.onEventLeave, this);
19692                 el.remove();
19693             },this);
19694         },this);
19695         
19696         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19697             e.remove();
19698         });
19699         
19700     },
19701     
19702     renderEvents: function()
19703     {   
19704         var _this = this;
19705         
19706         this.cells.each(function(c) {
19707             
19708             if(c.row < 5){
19709                 return;
19710             }
19711             
19712             var ev = c.events;
19713             
19714             var r = 4;
19715             if(c.row != c.events.length){
19716                 r = 4 - (4 - (c.row - c.events.length));
19717             }
19718             
19719             c.events = ev.slice(0, r);
19720             c.more = ev.slice(r);
19721             
19722             if(c.more.length && c.more.length == 1){
19723                 c.events.push(c.more.pop());
19724             }
19725             
19726             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19727             
19728         });
19729             
19730         this.cells.each(function(c) {
19731             
19732             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19733             
19734             
19735             for (var e = 0; e < c.events.length; e++){
19736                 var ev = c.events[e];
19737                 var rows = ev.rows;
19738                 
19739                 for(var i = 0; i < rows.length; i++) {
19740                 
19741                     // how many rows should it span..
19742
19743                     var  cfg = {
19744                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19745                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19746
19747                         unselectable : "on",
19748                         cn : [
19749                             {
19750                                 cls: 'fc-event-inner',
19751                                 cn : [
19752     //                                {
19753     //                                  tag:'span',
19754     //                                  cls: 'fc-event-time',
19755     //                                  html : cells.length > 1 ? '' : ev.time
19756     //                                },
19757                                     {
19758                                       tag:'span',
19759                                       cls: 'fc-event-title',
19760                                       html : String.format('{0}', ev.title)
19761                                     }
19762
19763
19764                                 ]
19765                             },
19766                             {
19767                                 cls: 'ui-resizable-handle ui-resizable-e',
19768                                 html : '&nbsp;&nbsp;&nbsp'
19769                             }
19770
19771                         ]
19772                     };
19773
19774                     if (i == 0) {
19775                         cfg.cls += ' fc-event-start';
19776                     }
19777                     if ((i+1) == rows.length) {
19778                         cfg.cls += ' fc-event-end';
19779                     }
19780
19781                     var ctr = _this.el.select('.fc-event-container',true).first();
19782                     var cg = ctr.createChild(cfg);
19783
19784                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19785                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19786
19787                     var r = (c.more.length) ? 1 : 0;
19788                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19789                     cg.setWidth(ebox.right - sbox.x -2);
19790
19791                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19792                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19793                     cg.on('click', _this.onEventClick, _this, ev);
19794
19795                     ev.els.push(cg);
19796                     
19797                 }
19798                 
19799             }
19800             
19801             
19802             if(c.more.length){
19803                 var  cfg = {
19804                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19805                     style : 'position: absolute',
19806                     unselectable : "on",
19807                     cn : [
19808                         {
19809                             cls: 'fc-event-inner',
19810                             cn : [
19811                                 {
19812                                   tag:'span',
19813                                   cls: 'fc-event-title',
19814                                   html : 'More'
19815                                 }
19816
19817
19818                             ]
19819                         },
19820                         {
19821                             cls: 'ui-resizable-handle ui-resizable-e',
19822                             html : '&nbsp;&nbsp;&nbsp'
19823                         }
19824
19825                     ]
19826                 };
19827
19828                 var ctr = _this.el.select('.fc-event-container',true).first();
19829                 var cg = ctr.createChild(cfg);
19830
19831                 var sbox = c.select('.fc-day-content',true).first().getBox();
19832                 var ebox = c.select('.fc-day-content',true).first().getBox();
19833                 //Roo.log(cg);
19834                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19835                 cg.setWidth(ebox.right - sbox.x -2);
19836
19837                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19838                 
19839             }
19840             
19841         });
19842         
19843         
19844         
19845     },
19846     
19847     onEventEnter: function (e, el,event,d) {
19848         this.fireEvent('evententer', this, el, event);
19849     },
19850     
19851     onEventLeave: function (e, el,event,d) {
19852         this.fireEvent('eventleave', this, el, event);
19853     },
19854     
19855     onEventClick: function (e, el,event,d) {
19856         this.fireEvent('eventclick', this, el, event);
19857     },
19858     
19859     onMonthChange: function () {
19860         this.store.load();
19861     },
19862     
19863     onMoreEventClick: function(e, el, more)
19864     {
19865         var _this = this;
19866         
19867         this.calpopover.placement = 'right';
19868         this.calpopover.setTitle('More');
19869         
19870         this.calpopover.setContent('');
19871         
19872         var ctr = this.calpopover.el.select('.popover-content', true).first();
19873         
19874         Roo.each(more, function(m){
19875             var cfg = {
19876                 cls : 'fc-event-hori fc-event-draggable',
19877                 html : m.title
19878             };
19879             var cg = ctr.createChild(cfg);
19880             
19881             cg.on('click', _this.onEventClick, _this, m);
19882         });
19883         
19884         this.calpopover.show(el);
19885         
19886         
19887     },
19888     
19889     onLoad: function () 
19890     {   
19891         this.calevents = [];
19892         var cal = this;
19893         
19894         if(this.store.getCount() > 0){
19895             this.store.data.each(function(d){
19896                cal.addItem({
19897                     id : d.data.id,
19898                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19899                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19900                     time : d.data.start_time,
19901                     title : d.data.title,
19902                     description : d.data.description,
19903                     venue : d.data.venue
19904                 });
19905             });
19906         }
19907         
19908         this.renderEvents();
19909         
19910         if(this.calevents.length && this.loadMask){
19911             this.maskEl.hide();
19912         }
19913     },
19914     
19915     onBeforeLoad: function()
19916     {
19917         this.clearEvents();
19918         if(this.loadMask){
19919             this.maskEl.show();
19920         }
19921     }
19922 });
19923
19924  
19925  /*
19926  * - LGPL
19927  *
19928  * element
19929  * 
19930  */
19931
19932 /**
19933  * @class Roo.bootstrap.Popover
19934  * @extends Roo.bootstrap.Component
19935  * Bootstrap Popover class
19936  * @cfg {String} html contents of the popover   (or false to use children..)
19937  * @cfg {String} title of popover (or false to hide)
19938  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19939  * @cfg {String} trigger click || hover (or false to trigger manually)
19940  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19941  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19942  *      - if false and it has a 'parent' then it will be automatically added to that element
19943  *      - if string - Roo.get  will be called 
19944  * @cfg {Number} delay - delay before showing
19945  
19946  * @constructor
19947  * Create a new Popover
19948  * @param {Object} config The config object
19949  */
19950
19951 Roo.bootstrap.Popover = function(config){
19952     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19953     
19954     this.addEvents({
19955         // raw events
19956          /**
19957          * @event show
19958          * After the popover show
19959          * 
19960          * @param {Roo.bootstrap.Popover} this
19961          */
19962         "show" : true,
19963         /**
19964          * @event hide
19965          * After the popover hide
19966          * 
19967          * @param {Roo.bootstrap.Popover} this
19968          */
19969         "hide" : true
19970     });
19971 };
19972
19973 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19974     
19975     title: false,
19976     html: false,
19977     
19978     placement : 'right',
19979     trigger : 'hover', // hover
19980     modal : false,
19981     delay : 0,
19982     
19983     over: false,
19984     
19985     can_build_overlaid : false,
19986     
19987     maskEl : false, // the mask element
19988     headerEl : false,
19989     contentEl : false,
19990     alignEl : false, // when show is called with an element - this get's stored.
19991     
19992     getChildContainer : function()
19993     {
19994         return this.contentEl;
19995         
19996     },
19997     getPopoverHeader : function()
19998     {
19999         this.title = true; // flag not to hide it..
20000         this.headerEl.addClass('p-0');
20001         return this.headerEl
20002     },
20003     
20004     
20005     getAutoCreate : function(){
20006          
20007         var cfg = {
20008            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20009            style: 'display:block',
20010            cn : [
20011                 {
20012                     cls : 'arrow'
20013                 },
20014                 {
20015                     cls : 'popover-inner ',
20016                     cn : [
20017                         {
20018                             tag: 'h3',
20019                             cls: 'popover-title popover-header',
20020                             html : this.title === false ? '' : this.title
20021                         },
20022                         {
20023                             cls : 'popover-content popover-body '  + (this.cls || ''),
20024                             html : this.html || ''
20025                         }
20026                     ]
20027                     
20028                 }
20029            ]
20030         };
20031         
20032         return cfg;
20033     },
20034     /**
20035      * @param {string} the title
20036      */
20037     setTitle: function(str)
20038     {
20039         this.title = str;
20040         if (this.el) {
20041             this.headerEl.dom.innerHTML = str;
20042         }
20043         
20044     },
20045     /**
20046      * @param {string} the body content
20047      */
20048     setContent: function(str)
20049     {
20050         this.html = str;
20051         if (this.contentEl) {
20052             this.contentEl.dom.innerHTML = str;
20053         }
20054         
20055     },
20056     // as it get's added to the bottom of the page.
20057     onRender : function(ct, position)
20058     {
20059         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20060         
20061         
20062         
20063         if(!this.el){
20064             var cfg = Roo.apply({},  this.getAutoCreate());
20065             cfg.id = Roo.id();
20066             
20067             if (this.cls) {
20068                 cfg.cls += ' ' + this.cls;
20069             }
20070             if (this.style) {
20071                 cfg.style = this.style;
20072             }
20073             //Roo.log("adding to ");
20074             this.el = Roo.get(document.body).createChild(cfg, position);
20075 //            Roo.log(this.el);
20076         }
20077         
20078         this.contentEl = this.el.select('.popover-content',true).first();
20079         this.headerEl =  this.el.select('.popover-title',true).first();
20080         
20081         var nitems = [];
20082         if(typeof(this.items) != 'undefined'){
20083             var items = this.items;
20084             delete this.items;
20085
20086             for(var i =0;i < items.length;i++) {
20087                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20088             }
20089         }
20090
20091         this.items = nitems;
20092         
20093         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20094         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20095         
20096         
20097         
20098         this.initEvents();
20099     },
20100     
20101     resizeMask : function()
20102     {
20103         this.maskEl.setSize(
20104             Roo.lib.Dom.getViewWidth(true),
20105             Roo.lib.Dom.getViewHeight(true)
20106         );
20107     },
20108     
20109     initEvents : function()
20110     {
20111         
20112         if (!this.modal) { 
20113             Roo.bootstrap.Popover.register(this);
20114         }
20115          
20116         this.arrowEl = this.el.select('.arrow',true).first();
20117         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20118         this.el.enableDisplayMode('block');
20119         this.el.hide();
20120  
20121         
20122         if (this.over === false && !this.parent()) {
20123             return; 
20124         }
20125         if (this.triggers === false) {
20126             return;
20127         }
20128          
20129         // support parent
20130         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20131         var triggers = this.trigger ? this.trigger.split(' ') : [];
20132         Roo.each(triggers, function(trigger) {
20133         
20134             if (trigger == 'click') {
20135                 on_el.on('click', this.toggle, this);
20136             } else if (trigger != 'manual') {
20137                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20138                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20139       
20140                 on_el.on(eventIn  ,this.enter, this);
20141                 on_el.on(eventOut, this.leave, this);
20142             }
20143         }, this);
20144     },
20145     
20146     
20147     // private
20148     timeout : null,
20149     hoverState : null,
20150     
20151     toggle : function () {
20152         this.hoverState == 'in' ? this.leave() : this.enter();
20153     },
20154     
20155     enter : function () {
20156         
20157         clearTimeout(this.timeout);
20158     
20159         this.hoverState = 'in';
20160     
20161         if (!this.delay || !this.delay.show) {
20162             this.show();
20163             return;
20164         }
20165         var _t = this;
20166         this.timeout = setTimeout(function () {
20167             if (_t.hoverState == 'in') {
20168                 _t.show();
20169             }
20170         }, this.delay.show)
20171     },
20172     
20173     leave : function() {
20174         clearTimeout(this.timeout);
20175     
20176         this.hoverState = 'out';
20177     
20178         if (!this.delay || !this.delay.hide) {
20179             this.hide();
20180             return;
20181         }
20182         var _t = this;
20183         this.timeout = setTimeout(function () {
20184             if (_t.hoverState == 'out') {
20185                 _t.hide();
20186             }
20187         }, this.delay.hide)
20188     },
20189     /**
20190      * Show the popover
20191      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20192      * @param {string} (left|right|top|bottom) position
20193      */
20194     show : function (on_el, placement)
20195     {
20196         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20197         on_el = on_el || false; // default to false
20198          
20199         if (!on_el) {
20200             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20201                 on_el = this.parent().el;
20202             } else if (this.over) {
20203                 Roo.get(this.over);
20204             }
20205             
20206         }
20207         
20208         this.alignEl = Roo.get( on_el );
20209
20210         if (!this.el) {
20211             this.render(document.body);
20212         }
20213         
20214         
20215          
20216         
20217         if (this.title === false) {
20218             this.headerEl.hide();
20219         }
20220         
20221        
20222         this.el.show();
20223         this.el.dom.style.display = 'block';
20224          
20225  
20226         if (this.alignEl) {
20227             this.updatePosition(this.placement, true);
20228              
20229         } else {
20230             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20231             var es = this.el.getSize();
20232             var x = Roo.lib.Dom.getViewWidth()/2;
20233             var y = Roo.lib.Dom.getViewHeight()/2;
20234             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20235             
20236         }
20237
20238         
20239         //var arrow = this.el.select('.arrow',true).first();
20240         //arrow.set(align[2], 
20241         
20242         this.el.addClass('in');
20243         
20244          
20245         
20246         this.hoverState = 'in';
20247         
20248         if (this.modal) {
20249             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20250             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20251             this.maskEl.dom.style.display = 'block';
20252             this.maskEl.addClass('show');
20253         }
20254         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20255  
20256         this.fireEvent('show', this);
20257         
20258     },
20259     /**
20260      * fire this manually after loading a grid in the table for example
20261      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20262      * @param {Boolean} try and move it if we cant get right position.
20263      */
20264     updatePosition : function(placement, try_move)
20265     {
20266         // allow for calling with no parameters
20267         placement = placement   ? placement :  this.placement;
20268         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20269         
20270         this.el.removeClass([
20271             'fade','top','bottom', 'left', 'right','in',
20272             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20273         ]);
20274         this.el.addClass(placement + ' bs-popover-' + placement);
20275         
20276         if (!this.alignEl ) {
20277             return false;
20278         }
20279         
20280         switch (placement) {
20281             case 'right':
20282                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20283                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20284                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20285                     //normal display... or moved up/down.
20286                     this.el.setXY(offset);
20287                     var xy = this.alignEl.getAnchorXY('tr', false);
20288                     xy[0]+=2;xy[1]+=5;
20289                     this.arrowEl.setXY(xy);
20290                     return true;
20291                 }
20292                 // continue through...
20293                 return this.updatePosition('left', false);
20294                 
20295             
20296             case 'left':
20297                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20298                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20299                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20300                     //normal display... or moved up/down.
20301                     this.el.setXY(offset);
20302                     var xy = this.alignEl.getAnchorXY('tl', false);
20303                     xy[0]-=10;xy[1]+=5; // << fix me
20304                     this.arrowEl.setXY(xy);
20305                     return true;
20306                 }
20307                 // call self...
20308                 return this.updatePosition('right', false);
20309             
20310             case 'top':
20311                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20312                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20313                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20314                     //normal display... or moved up/down.
20315                     this.el.setXY(offset);
20316                     var xy = this.alignEl.getAnchorXY('t', false);
20317                     xy[1]-=10; // << fix me
20318                     this.arrowEl.setXY(xy);
20319                     return true;
20320                 }
20321                 // fall through
20322                return this.updatePosition('bottom', false);
20323             
20324             case 'bottom':
20325                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20326                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20327                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20328                     //normal display... or moved up/down.
20329                     this.el.setXY(offset);
20330                     var xy = this.alignEl.getAnchorXY('b', false);
20331                      xy[1]+=2; // << fix me
20332                     this.arrowEl.setXY(xy);
20333                     return true;
20334                 }
20335                 // fall through
20336                 return this.updatePosition('top', false);
20337                 
20338             
20339         }
20340         
20341         
20342         return false;
20343     },
20344     
20345     hide : function()
20346     {
20347         this.el.setXY([0,0]);
20348         this.el.removeClass('in');
20349         this.el.hide();
20350         this.hoverState = null;
20351         this.maskEl.hide(); // always..
20352         this.fireEvent('hide', this);
20353     }
20354     
20355 });
20356
20357
20358 Roo.apply(Roo.bootstrap.Popover, {
20359
20360     alignment : {
20361         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20362         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20363         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20364         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20365     },
20366     
20367     zIndex : 20001,
20368
20369     clickHander : false,
20370     
20371
20372     onMouseDown : function(e)
20373     {
20374         if (!e.getTarget(".roo-popover")) {
20375             this.hideAll();
20376         }
20377          
20378     },
20379     
20380     popups : [],
20381     
20382     register : function(popup)
20383     {
20384         if (!Roo.bootstrap.Popover.clickHandler) {
20385             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20386         }
20387         // hide other popups.
20388         this.hideAll();
20389         this.popups.push(popup);
20390     },
20391     hideAll : function()
20392     {
20393         this.popups.forEach(function(p) {
20394             p.hide();
20395         });
20396     }
20397
20398 });/*
20399  * - LGPL
20400  *
20401  * Card header - holder for the card header elements.
20402  * 
20403  */
20404
20405 /**
20406  * @class Roo.bootstrap.PopoverNav
20407  * @extends Roo.bootstrap.NavGroup
20408  * Bootstrap Popover header navigation class
20409  * @constructor
20410  * Create a new Popover Header Navigation 
20411  * @param {Object} config The config object
20412  */
20413
20414 Roo.bootstrap.PopoverNav = function(config){
20415     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20416 };
20417
20418 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20419     
20420     
20421     container_method : 'getPopoverHeader' 
20422     
20423      
20424     
20425     
20426    
20427 });
20428
20429  
20430
20431  /*
20432  * - LGPL
20433  *
20434  * Progress
20435  * 
20436  */
20437
20438 /**
20439  * @class Roo.bootstrap.Progress
20440  * @extends Roo.bootstrap.Component
20441  * Bootstrap Progress class
20442  * @cfg {Boolean} striped striped of the progress bar
20443  * @cfg {Boolean} active animated of the progress bar
20444  * 
20445  * 
20446  * @constructor
20447  * Create a new Progress
20448  * @param {Object} config The config object
20449  */
20450
20451 Roo.bootstrap.Progress = function(config){
20452     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20453 };
20454
20455 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20456     
20457     striped : false,
20458     active: false,
20459     
20460     getAutoCreate : function(){
20461         var cfg = {
20462             tag: 'div',
20463             cls: 'progress'
20464         };
20465         
20466         
20467         if(this.striped){
20468             cfg.cls += ' progress-striped';
20469         }
20470       
20471         if(this.active){
20472             cfg.cls += ' active';
20473         }
20474         
20475         
20476         return cfg;
20477     }
20478    
20479 });
20480
20481  
20482
20483  /*
20484  * - LGPL
20485  *
20486  * ProgressBar
20487  * 
20488  */
20489
20490 /**
20491  * @class Roo.bootstrap.ProgressBar
20492  * @extends Roo.bootstrap.Component
20493  * Bootstrap ProgressBar class
20494  * @cfg {Number} aria_valuenow aria-value now
20495  * @cfg {Number} aria_valuemin aria-value min
20496  * @cfg {Number} aria_valuemax aria-value max
20497  * @cfg {String} label label for the progress bar
20498  * @cfg {String} panel (success | info | warning | danger )
20499  * @cfg {String} role role of the progress bar
20500  * @cfg {String} sr_only text
20501  * 
20502  * 
20503  * @constructor
20504  * Create a new ProgressBar
20505  * @param {Object} config The config object
20506  */
20507
20508 Roo.bootstrap.ProgressBar = function(config){
20509     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20510 };
20511
20512 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20513     
20514     aria_valuenow : 0,
20515     aria_valuemin : 0,
20516     aria_valuemax : 100,
20517     label : false,
20518     panel : false,
20519     role : false,
20520     sr_only: false,
20521     
20522     getAutoCreate : function()
20523     {
20524         
20525         var cfg = {
20526             tag: 'div',
20527             cls: 'progress-bar',
20528             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20529         };
20530         
20531         if(this.sr_only){
20532             cfg.cn = {
20533                 tag: 'span',
20534                 cls: 'sr-only',
20535                 html: this.sr_only
20536             }
20537         }
20538         
20539         if(this.role){
20540             cfg.role = this.role;
20541         }
20542         
20543         if(this.aria_valuenow){
20544             cfg['aria-valuenow'] = this.aria_valuenow;
20545         }
20546         
20547         if(this.aria_valuemin){
20548             cfg['aria-valuemin'] = this.aria_valuemin;
20549         }
20550         
20551         if(this.aria_valuemax){
20552             cfg['aria-valuemax'] = this.aria_valuemax;
20553         }
20554         
20555         if(this.label && !this.sr_only){
20556             cfg.html = this.label;
20557         }
20558         
20559         if(this.panel){
20560             cfg.cls += ' progress-bar-' + this.panel;
20561         }
20562         
20563         return cfg;
20564     },
20565     
20566     update : function(aria_valuenow)
20567     {
20568         this.aria_valuenow = aria_valuenow;
20569         
20570         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20571     }
20572    
20573 });
20574
20575  
20576
20577  /*
20578  * - LGPL
20579  *
20580  * column
20581  * 
20582  */
20583
20584 /**
20585  * @class Roo.bootstrap.TabGroup
20586  * @extends Roo.bootstrap.Column
20587  * Bootstrap Column class
20588  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20589  * @cfg {Boolean} carousel true to make the group behave like a carousel
20590  * @cfg {Boolean} bullets show bullets for the panels
20591  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20592  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20593  * @cfg {Boolean} showarrow (true|false) show arrow default true
20594  * 
20595  * @constructor
20596  * Create a new TabGroup
20597  * @param {Object} config The config object
20598  */
20599
20600 Roo.bootstrap.TabGroup = function(config){
20601     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20602     if (!this.navId) {
20603         this.navId = Roo.id();
20604     }
20605     this.tabs = [];
20606     Roo.bootstrap.TabGroup.register(this);
20607     
20608 };
20609
20610 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20611     
20612     carousel : false,
20613     transition : false,
20614     bullets : 0,
20615     timer : 0,
20616     autoslide : false,
20617     slideFn : false,
20618     slideOnTouch : false,
20619     showarrow : true,
20620     
20621     getAutoCreate : function()
20622     {
20623         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20624         
20625         cfg.cls += ' tab-content';
20626         
20627         if (this.carousel) {
20628             cfg.cls += ' carousel slide';
20629             
20630             cfg.cn = [{
20631                cls : 'carousel-inner',
20632                cn : []
20633             }];
20634         
20635             if(this.bullets  && !Roo.isTouch){
20636                 
20637                 var bullets = {
20638                     cls : 'carousel-bullets',
20639                     cn : []
20640                 };
20641                
20642                 if(this.bullets_cls){
20643                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20644                 }
20645                 
20646                 bullets.cn.push({
20647                     cls : 'clear'
20648                 });
20649                 
20650                 cfg.cn[0].cn.push(bullets);
20651             }
20652             
20653             if(this.showarrow){
20654                 cfg.cn[0].cn.push({
20655                     tag : 'div',
20656                     class : 'carousel-arrow',
20657                     cn : [
20658                         {
20659                             tag : 'div',
20660                             class : 'carousel-prev',
20661                             cn : [
20662                                 {
20663                                     tag : 'i',
20664                                     class : 'fa fa-chevron-left'
20665                                 }
20666                             ]
20667                         },
20668                         {
20669                             tag : 'div',
20670                             class : 'carousel-next',
20671                             cn : [
20672                                 {
20673                                     tag : 'i',
20674                                     class : 'fa fa-chevron-right'
20675                                 }
20676                             ]
20677                         }
20678                     ]
20679                 });
20680             }
20681             
20682         }
20683         
20684         return cfg;
20685     },
20686     
20687     initEvents:  function()
20688     {
20689 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20690 //            this.el.on("touchstart", this.onTouchStart, this);
20691 //        }
20692         
20693         if(this.autoslide){
20694             var _this = this;
20695             
20696             this.slideFn = window.setInterval(function() {
20697                 _this.showPanelNext();
20698             }, this.timer);
20699         }
20700         
20701         if(this.showarrow){
20702             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20703             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20704         }
20705         
20706         
20707     },
20708     
20709 //    onTouchStart : function(e, el, o)
20710 //    {
20711 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20712 //            return;
20713 //        }
20714 //        
20715 //        this.showPanelNext();
20716 //    },
20717     
20718     
20719     getChildContainer : function()
20720     {
20721         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20722     },
20723     
20724     /**
20725     * register a Navigation item
20726     * @param {Roo.bootstrap.NavItem} the navitem to add
20727     */
20728     register : function(item)
20729     {
20730         this.tabs.push( item);
20731         item.navId = this.navId; // not really needed..
20732         this.addBullet();
20733     
20734     },
20735     
20736     getActivePanel : function()
20737     {
20738         var r = false;
20739         Roo.each(this.tabs, function(t) {
20740             if (t.active) {
20741                 r = t;
20742                 return false;
20743             }
20744             return null;
20745         });
20746         return r;
20747         
20748     },
20749     getPanelByName : function(n)
20750     {
20751         var r = false;
20752         Roo.each(this.tabs, function(t) {
20753             if (t.tabId == n) {
20754                 r = t;
20755                 return false;
20756             }
20757             return null;
20758         });
20759         return r;
20760     },
20761     indexOfPanel : function(p)
20762     {
20763         var r = false;
20764         Roo.each(this.tabs, function(t,i) {
20765             if (t.tabId == p.tabId) {
20766                 r = i;
20767                 return false;
20768             }
20769             return null;
20770         });
20771         return r;
20772     },
20773     /**
20774      * show a specific panel
20775      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20776      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20777      */
20778     showPanel : function (pan)
20779     {
20780         if(this.transition || typeof(pan) == 'undefined'){
20781             Roo.log("waiting for the transitionend");
20782             return false;
20783         }
20784         
20785         if (typeof(pan) == 'number') {
20786             pan = this.tabs[pan];
20787         }
20788         
20789         if (typeof(pan) == 'string') {
20790             pan = this.getPanelByName(pan);
20791         }
20792         
20793         var cur = this.getActivePanel();
20794         
20795         if(!pan || !cur){
20796             Roo.log('pan or acitve pan is undefined');
20797             return false;
20798         }
20799         
20800         if (pan.tabId == this.getActivePanel().tabId) {
20801             return true;
20802         }
20803         
20804         if (false === cur.fireEvent('beforedeactivate')) {
20805             return false;
20806         }
20807         
20808         if(this.bullets > 0 && !Roo.isTouch){
20809             this.setActiveBullet(this.indexOfPanel(pan));
20810         }
20811         
20812         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20813             
20814             //class="carousel-item carousel-item-next carousel-item-left"
20815             
20816             this.transition = true;
20817             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20818             var lr = dir == 'next' ? 'left' : 'right';
20819             pan.el.addClass(dir); // or prev
20820             pan.el.addClass('carousel-item-' + dir); // or prev
20821             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20822             cur.el.addClass(lr); // or right
20823             pan.el.addClass(lr);
20824             cur.el.addClass('carousel-item-' +lr); // or right
20825             pan.el.addClass('carousel-item-' +lr);
20826             
20827             
20828             var _this = this;
20829             cur.el.on('transitionend', function() {
20830                 Roo.log("trans end?");
20831                 
20832                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20833                 pan.setActive(true);
20834                 
20835                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20836                 cur.setActive(false);
20837                 
20838                 _this.transition = false;
20839                 
20840             }, this, { single:  true } );
20841             
20842             return true;
20843         }
20844         
20845         cur.setActive(false);
20846         pan.setActive(true);
20847         
20848         return true;
20849         
20850     },
20851     showPanelNext : function()
20852     {
20853         var i = this.indexOfPanel(this.getActivePanel());
20854         
20855         if (i >= this.tabs.length - 1 && !this.autoslide) {
20856             return;
20857         }
20858         
20859         if (i >= this.tabs.length - 1 && this.autoslide) {
20860             i = -1;
20861         }
20862         
20863         this.showPanel(this.tabs[i+1]);
20864     },
20865     
20866     showPanelPrev : function()
20867     {
20868         var i = this.indexOfPanel(this.getActivePanel());
20869         
20870         if (i  < 1 && !this.autoslide) {
20871             return;
20872         }
20873         
20874         if (i < 1 && this.autoslide) {
20875             i = this.tabs.length;
20876         }
20877         
20878         this.showPanel(this.tabs[i-1]);
20879     },
20880     
20881     
20882     addBullet: function()
20883     {
20884         if(!this.bullets || Roo.isTouch){
20885             return;
20886         }
20887         var ctr = this.el.select('.carousel-bullets',true).first();
20888         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20889         var bullet = ctr.createChild({
20890             cls : 'bullet bullet-' + i
20891         },ctr.dom.lastChild);
20892         
20893         
20894         var _this = this;
20895         
20896         bullet.on('click', (function(e, el, o, ii, t){
20897
20898             e.preventDefault();
20899
20900             this.showPanel(ii);
20901
20902             if(this.autoslide && this.slideFn){
20903                 clearInterval(this.slideFn);
20904                 this.slideFn = window.setInterval(function() {
20905                     _this.showPanelNext();
20906                 }, this.timer);
20907             }
20908
20909         }).createDelegate(this, [i, bullet], true));
20910                 
20911         
20912     },
20913      
20914     setActiveBullet : function(i)
20915     {
20916         if(Roo.isTouch){
20917             return;
20918         }
20919         
20920         Roo.each(this.el.select('.bullet', true).elements, function(el){
20921             el.removeClass('selected');
20922         });
20923
20924         var bullet = this.el.select('.bullet-' + i, true).first();
20925         
20926         if(!bullet){
20927             return;
20928         }
20929         
20930         bullet.addClass('selected');
20931     }
20932     
20933     
20934   
20935 });
20936
20937  
20938
20939  
20940  
20941 Roo.apply(Roo.bootstrap.TabGroup, {
20942     
20943     groups: {},
20944      /**
20945     * register a Navigation Group
20946     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20947     */
20948     register : function(navgrp)
20949     {
20950         this.groups[navgrp.navId] = navgrp;
20951         
20952     },
20953     /**
20954     * fetch a Navigation Group based on the navigation ID
20955     * if one does not exist , it will get created.
20956     * @param {string} the navgroup to add
20957     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20958     */
20959     get: function(navId) {
20960         if (typeof(this.groups[navId]) == 'undefined') {
20961             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20962         }
20963         return this.groups[navId] ;
20964     }
20965     
20966     
20967     
20968 });
20969
20970  /*
20971  * - LGPL
20972  *
20973  * TabPanel
20974  * 
20975  */
20976
20977 /**
20978  * @class Roo.bootstrap.TabPanel
20979  * @extends Roo.bootstrap.Component
20980  * Bootstrap TabPanel class
20981  * @cfg {Boolean} active panel active
20982  * @cfg {String} html panel content
20983  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20984  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20985  * @cfg {String} href click to link..
20986  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20987  * 
20988  * 
20989  * @constructor
20990  * Create a new TabPanel
20991  * @param {Object} config The config object
20992  */
20993
20994 Roo.bootstrap.TabPanel = function(config){
20995     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20996     this.addEvents({
20997         /**
20998              * @event changed
20999              * Fires when the active status changes
21000              * @param {Roo.bootstrap.TabPanel} this
21001              * @param {Boolean} state the new state
21002             
21003          */
21004         'changed': true,
21005         /**
21006              * @event beforedeactivate
21007              * Fires before a tab is de-activated - can be used to do validation on a form.
21008              * @param {Roo.bootstrap.TabPanel} this
21009              * @return {Boolean} false if there is an error
21010             
21011          */
21012         'beforedeactivate': true
21013      });
21014     
21015     this.tabId = this.tabId || Roo.id();
21016   
21017 };
21018
21019 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21020     
21021     active: false,
21022     html: false,
21023     tabId: false,
21024     navId : false,
21025     href : '',
21026     touchSlide : false,
21027     getAutoCreate : function(){
21028         
21029         
21030         var cfg = {
21031             tag: 'div',
21032             // item is needed for carousel - not sure if it has any effect otherwise
21033             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21034             html: this.html || ''
21035         };
21036         
21037         if(this.active){
21038             cfg.cls += ' active';
21039         }
21040         
21041         if(this.tabId){
21042             cfg.tabId = this.tabId;
21043         }
21044         
21045         
21046         
21047         return cfg;
21048     },
21049     
21050     initEvents:  function()
21051     {
21052         var p = this.parent();
21053         
21054         this.navId = this.navId || p.navId;
21055         
21056         if (typeof(this.navId) != 'undefined') {
21057             // not really needed.. but just in case.. parent should be a NavGroup.
21058             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21059             
21060             tg.register(this);
21061             
21062             var i = tg.tabs.length - 1;
21063             
21064             if(this.active && tg.bullets > 0 && i < tg.bullets){
21065                 tg.setActiveBullet(i);
21066             }
21067         }
21068         
21069         this.el.on('click', this.onClick, this);
21070         
21071         if(Roo.isTouch && this.touchSlide){
21072             this.el.on("touchstart", this.onTouchStart, this);
21073             this.el.on("touchmove", this.onTouchMove, this);
21074             this.el.on("touchend", this.onTouchEnd, this);
21075         }
21076         
21077     },
21078     
21079     onRender : function(ct, position)
21080     {
21081         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21082     },
21083     
21084     setActive : function(state)
21085     {
21086         Roo.log("panel - set active " + this.tabId + "=" + state);
21087         
21088         this.active = state;
21089         if (!state) {
21090             this.el.removeClass('active');
21091             
21092         } else  if (!this.el.hasClass('active')) {
21093             this.el.addClass('active');
21094         }
21095         
21096         this.fireEvent('changed', this, state);
21097     },
21098     
21099     onClick : function(e)
21100     {
21101         e.preventDefault();
21102         
21103         if(!this.href.length){
21104             return;
21105         }
21106         
21107         window.location.href = this.href;
21108     },
21109     
21110     startX : 0,
21111     startY : 0,
21112     endX : 0,
21113     endY : 0,
21114     swiping : false,
21115     
21116     onTouchStart : function(e)
21117     {
21118         this.swiping = false;
21119         
21120         this.startX = e.browserEvent.touches[0].clientX;
21121         this.startY = e.browserEvent.touches[0].clientY;
21122     },
21123     
21124     onTouchMove : function(e)
21125     {
21126         this.swiping = true;
21127         
21128         this.endX = e.browserEvent.touches[0].clientX;
21129         this.endY = e.browserEvent.touches[0].clientY;
21130     },
21131     
21132     onTouchEnd : function(e)
21133     {
21134         if(!this.swiping){
21135             this.onClick(e);
21136             return;
21137         }
21138         
21139         var tabGroup = this.parent();
21140         
21141         if(this.endX > this.startX){ // swiping right
21142             tabGroup.showPanelPrev();
21143             return;
21144         }
21145         
21146         if(this.startX > this.endX){ // swiping left
21147             tabGroup.showPanelNext();
21148             return;
21149         }
21150     }
21151     
21152     
21153 });
21154  
21155
21156  
21157
21158  /*
21159  * - LGPL
21160  *
21161  * DateField
21162  * 
21163  */
21164
21165 /**
21166  * @class Roo.bootstrap.DateField
21167  * @extends Roo.bootstrap.Input
21168  * Bootstrap DateField class
21169  * @cfg {Number} weekStart default 0
21170  * @cfg {String} viewMode default empty, (months|years)
21171  * @cfg {String} minViewMode default empty, (months|years)
21172  * @cfg {Number} startDate default -Infinity
21173  * @cfg {Number} endDate default Infinity
21174  * @cfg {Boolean} todayHighlight default false
21175  * @cfg {Boolean} todayBtn default false
21176  * @cfg {Boolean} calendarWeeks default false
21177  * @cfg {Object} daysOfWeekDisabled default empty
21178  * @cfg {Boolean} singleMode default false (true | false)
21179  * 
21180  * @cfg {Boolean} keyboardNavigation default true
21181  * @cfg {String} language default en
21182  * 
21183  * @constructor
21184  * Create a new DateField
21185  * @param {Object} config The config object
21186  */
21187
21188 Roo.bootstrap.DateField = function(config){
21189     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21190      this.addEvents({
21191             /**
21192              * @event show
21193              * Fires when this field show.
21194              * @param {Roo.bootstrap.DateField} this
21195              * @param {Mixed} date The date value
21196              */
21197             show : true,
21198             /**
21199              * @event show
21200              * Fires when this field hide.
21201              * @param {Roo.bootstrap.DateField} this
21202              * @param {Mixed} date The date value
21203              */
21204             hide : true,
21205             /**
21206              * @event select
21207              * Fires when select a date.
21208              * @param {Roo.bootstrap.DateField} this
21209              * @param {Mixed} date The date value
21210              */
21211             select : true,
21212             /**
21213              * @event beforeselect
21214              * Fires when before select a date.
21215              * @param {Roo.bootstrap.DateField} this
21216              * @param {Mixed} date The date value
21217              */
21218             beforeselect : true
21219         });
21220 };
21221
21222 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21223     
21224     /**
21225      * @cfg {String} format
21226      * The default date format string which can be overriden for localization support.  The format must be
21227      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21228      */
21229     format : "m/d/y",
21230     /**
21231      * @cfg {String} altFormats
21232      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21233      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21234      */
21235     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21236     
21237     weekStart : 0,
21238     
21239     viewMode : '',
21240     
21241     minViewMode : '',
21242     
21243     todayHighlight : false,
21244     
21245     todayBtn: false,
21246     
21247     language: 'en',
21248     
21249     keyboardNavigation: true,
21250     
21251     calendarWeeks: false,
21252     
21253     startDate: -Infinity,
21254     
21255     endDate: Infinity,
21256     
21257     daysOfWeekDisabled: [],
21258     
21259     _events: [],
21260     
21261     singleMode : false,
21262     
21263     UTCDate: function()
21264     {
21265         return new Date(Date.UTC.apply(Date, arguments));
21266     },
21267     
21268     UTCToday: function()
21269     {
21270         var today = new Date();
21271         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21272     },
21273     
21274     getDate: function() {
21275             var d = this.getUTCDate();
21276             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21277     },
21278     
21279     getUTCDate: function() {
21280             return this.date;
21281     },
21282     
21283     setDate: function(d) {
21284             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21285     },
21286     
21287     setUTCDate: function(d) {
21288             this.date = d;
21289             this.setValue(this.formatDate(this.date));
21290     },
21291         
21292     onRender: function(ct, position)
21293     {
21294         
21295         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21296         
21297         this.language = this.language || 'en';
21298         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21299         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21300         
21301         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21302         this.format = this.format || 'm/d/y';
21303         this.isInline = false;
21304         this.isInput = true;
21305         this.component = this.el.select('.add-on', true).first() || false;
21306         this.component = (this.component && this.component.length === 0) ? false : this.component;
21307         this.hasInput = this.component && this.inputEl().length;
21308         
21309         if (typeof(this.minViewMode === 'string')) {
21310             switch (this.minViewMode) {
21311                 case 'months':
21312                     this.minViewMode = 1;
21313                     break;
21314                 case 'years':
21315                     this.minViewMode = 2;
21316                     break;
21317                 default:
21318                     this.minViewMode = 0;
21319                     break;
21320             }
21321         }
21322         
21323         if (typeof(this.viewMode === 'string')) {
21324             switch (this.viewMode) {
21325                 case 'months':
21326                     this.viewMode = 1;
21327                     break;
21328                 case 'years':
21329                     this.viewMode = 2;
21330                     break;
21331                 default:
21332                     this.viewMode = 0;
21333                     break;
21334             }
21335         }
21336                 
21337         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21338         
21339 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21340         
21341         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21342         
21343         this.picker().on('mousedown', this.onMousedown, this);
21344         this.picker().on('click', this.onClick, this);
21345         
21346         this.picker().addClass('datepicker-dropdown');
21347         
21348         this.startViewMode = this.viewMode;
21349         
21350         if(this.singleMode){
21351             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21352                 v.setVisibilityMode(Roo.Element.DISPLAY);
21353                 v.hide();
21354             });
21355             
21356             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21357                 v.setStyle('width', '189px');
21358             });
21359         }
21360         
21361         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21362             if(!this.calendarWeeks){
21363                 v.remove();
21364                 return;
21365             }
21366             
21367             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21368             v.attr('colspan', function(i, val){
21369                 return parseInt(val) + 1;
21370             });
21371         });
21372                         
21373         
21374         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21375         
21376         this.setStartDate(this.startDate);
21377         this.setEndDate(this.endDate);
21378         
21379         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21380         
21381         this.fillDow();
21382         this.fillMonths();
21383         this.update();
21384         this.showMode();
21385         
21386         if(this.isInline) {
21387             this.showPopup();
21388         }
21389     },
21390     
21391     picker : function()
21392     {
21393         return this.pickerEl;
21394 //        return this.el.select('.datepicker', true).first();
21395     },
21396     
21397     fillDow: function()
21398     {
21399         var dowCnt = this.weekStart;
21400         
21401         var dow = {
21402             tag: 'tr',
21403             cn: [
21404                 
21405             ]
21406         };
21407         
21408         if(this.calendarWeeks){
21409             dow.cn.push({
21410                 tag: 'th',
21411                 cls: 'cw',
21412                 html: '&nbsp;'
21413             })
21414         }
21415         
21416         while (dowCnt < this.weekStart + 7) {
21417             dow.cn.push({
21418                 tag: 'th',
21419                 cls: 'dow',
21420                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21421             });
21422         }
21423         
21424         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21425     },
21426     
21427     fillMonths: function()
21428     {    
21429         var i = 0;
21430         var months = this.picker().select('>.datepicker-months td', true).first();
21431         
21432         months.dom.innerHTML = '';
21433         
21434         while (i < 12) {
21435             var month = {
21436                 tag: 'span',
21437                 cls: 'month',
21438                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21439             };
21440             
21441             months.createChild(month);
21442         }
21443         
21444     },
21445     
21446     update: function()
21447     {
21448         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;
21449         
21450         if (this.date < this.startDate) {
21451             this.viewDate = new Date(this.startDate);
21452         } else if (this.date > this.endDate) {
21453             this.viewDate = new Date(this.endDate);
21454         } else {
21455             this.viewDate = new Date(this.date);
21456         }
21457         
21458         this.fill();
21459     },
21460     
21461     fill: function() 
21462     {
21463         var d = new Date(this.viewDate),
21464                 year = d.getUTCFullYear(),
21465                 month = d.getUTCMonth(),
21466                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21467                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21468                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21469                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21470                 currentDate = this.date && this.date.valueOf(),
21471                 today = this.UTCToday();
21472         
21473         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21474         
21475 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21476         
21477 //        this.picker.select('>tfoot th.today').
21478 //                                              .text(dates[this.language].today)
21479 //                                              .toggle(this.todayBtn !== false);
21480     
21481         this.updateNavArrows();
21482         this.fillMonths();
21483                                                 
21484         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21485         
21486         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21487          
21488         prevMonth.setUTCDate(day);
21489         
21490         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21491         
21492         var nextMonth = new Date(prevMonth);
21493         
21494         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21495         
21496         nextMonth = nextMonth.valueOf();
21497         
21498         var fillMonths = false;
21499         
21500         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21501         
21502         while(prevMonth.valueOf() <= nextMonth) {
21503             var clsName = '';
21504             
21505             if (prevMonth.getUTCDay() === this.weekStart) {
21506                 if(fillMonths){
21507                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21508                 }
21509                     
21510                 fillMonths = {
21511                     tag: 'tr',
21512                     cn: []
21513                 };
21514                 
21515                 if(this.calendarWeeks){
21516                     // ISO 8601: First week contains first thursday.
21517                     // ISO also states week starts on Monday, but we can be more abstract here.
21518                     var
21519                     // Start of current week: based on weekstart/current date
21520                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21521                     // Thursday of this week
21522                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21523                     // First Thursday of year, year from thursday
21524                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21525                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21526                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21527                     
21528                     fillMonths.cn.push({
21529                         tag: 'td',
21530                         cls: 'cw',
21531                         html: calWeek
21532                     });
21533                 }
21534             }
21535             
21536             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21537                 clsName += ' old';
21538             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21539                 clsName += ' new';
21540             }
21541             if (this.todayHighlight &&
21542                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21543                 prevMonth.getUTCMonth() == today.getMonth() &&
21544                 prevMonth.getUTCDate() == today.getDate()) {
21545                 clsName += ' today';
21546             }
21547             
21548             if (currentDate && prevMonth.valueOf() === currentDate) {
21549                 clsName += ' active';
21550             }
21551             
21552             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21553                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21554                     clsName += ' disabled';
21555             }
21556             
21557             fillMonths.cn.push({
21558                 tag: 'td',
21559                 cls: 'day ' + clsName,
21560                 html: prevMonth.getDate()
21561             });
21562             
21563             prevMonth.setDate(prevMonth.getDate()+1);
21564         }
21565           
21566         var currentYear = this.date && this.date.getUTCFullYear();
21567         var currentMonth = this.date && this.date.getUTCMonth();
21568         
21569         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21570         
21571         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21572             v.removeClass('active');
21573             
21574             if(currentYear === year && k === currentMonth){
21575                 v.addClass('active');
21576             }
21577             
21578             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21579                 v.addClass('disabled');
21580             }
21581             
21582         });
21583         
21584         
21585         year = parseInt(year/10, 10) * 10;
21586         
21587         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21588         
21589         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21590         
21591         year -= 1;
21592         for (var i = -1; i < 11; i++) {
21593             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21594                 tag: 'span',
21595                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21596                 html: year
21597             });
21598             
21599             year += 1;
21600         }
21601     },
21602     
21603     showMode: function(dir) 
21604     {
21605         if (dir) {
21606             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21607         }
21608         
21609         Roo.each(this.picker().select('>div',true).elements, function(v){
21610             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21611             v.hide();
21612         });
21613         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21614     },
21615     
21616     place: function()
21617     {
21618         if(this.isInline) {
21619             return;
21620         }
21621         
21622         this.picker().removeClass(['bottom', 'top']);
21623         
21624         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21625             /*
21626              * place to the top of element!
21627              *
21628              */
21629             
21630             this.picker().addClass('top');
21631             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21632             
21633             return;
21634         }
21635         
21636         this.picker().addClass('bottom');
21637         
21638         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21639     },
21640     
21641     parseDate : function(value)
21642     {
21643         if(!value || value instanceof Date){
21644             return value;
21645         }
21646         var v = Date.parseDate(value, this.format);
21647         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21648             v = Date.parseDate(value, 'Y-m-d');
21649         }
21650         if(!v && this.altFormats){
21651             if(!this.altFormatsArray){
21652                 this.altFormatsArray = this.altFormats.split("|");
21653             }
21654             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21655                 v = Date.parseDate(value, this.altFormatsArray[i]);
21656             }
21657         }
21658         return v;
21659     },
21660     
21661     formatDate : function(date, fmt)
21662     {   
21663         return (!date || !(date instanceof Date)) ?
21664         date : date.dateFormat(fmt || this.format);
21665     },
21666     
21667     onFocus : function()
21668     {
21669         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21670         this.showPopup();
21671     },
21672     
21673     onBlur : function()
21674     {
21675         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21676         
21677         var d = this.inputEl().getValue();
21678         
21679         this.setValue(d);
21680                 
21681         this.hidePopup();
21682     },
21683     
21684     showPopup : function()
21685     {
21686         this.picker().show();
21687         this.update();
21688         this.place();
21689         
21690         this.fireEvent('showpopup', this, this.date);
21691     },
21692     
21693     hidePopup : function()
21694     {
21695         if(this.isInline) {
21696             return;
21697         }
21698         this.picker().hide();
21699         this.viewMode = this.startViewMode;
21700         this.showMode();
21701         
21702         this.fireEvent('hidepopup', this, this.date);
21703         
21704     },
21705     
21706     onMousedown: function(e)
21707     {
21708         e.stopPropagation();
21709         e.preventDefault();
21710     },
21711     
21712     keyup: function(e)
21713     {
21714         Roo.bootstrap.DateField.superclass.keyup.call(this);
21715         this.update();
21716     },
21717
21718     setValue: function(v)
21719     {
21720         if(this.fireEvent('beforeselect', this, v) !== false){
21721             var d = new Date(this.parseDate(v) ).clearTime();
21722         
21723             if(isNaN(d.getTime())){
21724                 this.date = this.viewDate = '';
21725                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21726                 return;
21727             }
21728
21729             v = this.formatDate(d);
21730
21731             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21732
21733             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21734
21735             this.update();
21736
21737             this.fireEvent('select', this, this.date);
21738         }
21739     },
21740     
21741     getValue: function()
21742     {
21743         return this.formatDate(this.date);
21744     },
21745     
21746     fireKey: function(e)
21747     {
21748         if (!this.picker().isVisible()){
21749             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21750                 this.showPopup();
21751             }
21752             return;
21753         }
21754         
21755         var dateChanged = false,
21756         dir, day, month,
21757         newDate, newViewDate;
21758         
21759         switch(e.keyCode){
21760             case 27: // escape
21761                 this.hidePopup();
21762                 e.preventDefault();
21763                 break;
21764             case 37: // left
21765             case 39: // right
21766                 if (!this.keyboardNavigation) {
21767                     break;
21768                 }
21769                 dir = e.keyCode == 37 ? -1 : 1;
21770                 
21771                 if (e.ctrlKey){
21772                     newDate = this.moveYear(this.date, dir);
21773                     newViewDate = this.moveYear(this.viewDate, dir);
21774                 } else if (e.shiftKey){
21775                     newDate = this.moveMonth(this.date, dir);
21776                     newViewDate = this.moveMonth(this.viewDate, dir);
21777                 } else {
21778                     newDate = new Date(this.date);
21779                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21780                     newViewDate = new Date(this.viewDate);
21781                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21782                 }
21783                 if (this.dateWithinRange(newDate)){
21784                     this.date = newDate;
21785                     this.viewDate = newViewDate;
21786                     this.setValue(this.formatDate(this.date));
21787 //                    this.update();
21788                     e.preventDefault();
21789                     dateChanged = true;
21790                 }
21791                 break;
21792             case 38: // up
21793             case 40: // down
21794                 if (!this.keyboardNavigation) {
21795                     break;
21796                 }
21797                 dir = e.keyCode == 38 ? -1 : 1;
21798                 if (e.ctrlKey){
21799                     newDate = this.moveYear(this.date, dir);
21800                     newViewDate = this.moveYear(this.viewDate, dir);
21801                 } else if (e.shiftKey){
21802                     newDate = this.moveMonth(this.date, dir);
21803                     newViewDate = this.moveMonth(this.viewDate, dir);
21804                 } else {
21805                     newDate = new Date(this.date);
21806                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21807                     newViewDate = new Date(this.viewDate);
21808                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21809                 }
21810                 if (this.dateWithinRange(newDate)){
21811                     this.date = newDate;
21812                     this.viewDate = newViewDate;
21813                     this.setValue(this.formatDate(this.date));
21814 //                    this.update();
21815                     e.preventDefault();
21816                     dateChanged = true;
21817                 }
21818                 break;
21819             case 13: // enter
21820                 this.setValue(this.formatDate(this.date));
21821                 this.hidePopup();
21822                 e.preventDefault();
21823                 break;
21824             case 9: // tab
21825                 this.setValue(this.formatDate(this.date));
21826                 this.hidePopup();
21827                 break;
21828             case 16: // shift
21829             case 17: // ctrl
21830             case 18: // alt
21831                 break;
21832             default :
21833                 this.hidePopup();
21834                 
21835         }
21836     },
21837     
21838     
21839     onClick: function(e) 
21840     {
21841         e.stopPropagation();
21842         e.preventDefault();
21843         
21844         var target = e.getTarget();
21845         
21846         if(target.nodeName.toLowerCase() === 'i'){
21847             target = Roo.get(target).dom.parentNode;
21848         }
21849         
21850         var nodeName = target.nodeName;
21851         var className = target.className;
21852         var html = target.innerHTML;
21853         //Roo.log(nodeName);
21854         
21855         switch(nodeName.toLowerCase()) {
21856             case 'th':
21857                 switch(className) {
21858                     case 'switch':
21859                         this.showMode(1);
21860                         break;
21861                     case 'prev':
21862                     case 'next':
21863                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21864                         switch(this.viewMode){
21865                                 case 0:
21866                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21867                                         break;
21868                                 case 1:
21869                                 case 2:
21870                                         this.viewDate = this.moveYear(this.viewDate, dir);
21871                                         break;
21872                         }
21873                         this.fill();
21874                         break;
21875                     case 'today':
21876                         var date = new Date();
21877                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21878 //                        this.fill()
21879                         this.setValue(this.formatDate(this.date));
21880                         
21881                         this.hidePopup();
21882                         break;
21883                 }
21884                 break;
21885             case 'span':
21886                 if (className.indexOf('disabled') < 0) {
21887                     this.viewDate.setUTCDate(1);
21888                     if (className.indexOf('month') > -1) {
21889                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21890                     } else {
21891                         var year = parseInt(html, 10) || 0;
21892                         this.viewDate.setUTCFullYear(year);
21893                         
21894                     }
21895                     
21896                     if(this.singleMode){
21897                         this.setValue(this.formatDate(this.viewDate));
21898                         this.hidePopup();
21899                         return;
21900                     }
21901                     
21902                     this.showMode(-1);
21903                     this.fill();
21904                 }
21905                 break;
21906                 
21907             case 'td':
21908                 //Roo.log(className);
21909                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21910                     var day = parseInt(html, 10) || 1;
21911                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21912                         month = (this.viewDate || new Date()).getUTCMonth();
21913
21914                     if (className.indexOf('old') > -1) {
21915                         if(month === 0 ){
21916                             month = 11;
21917                             year -= 1;
21918                         }else{
21919                             month -= 1;
21920                         }
21921                     } else if (className.indexOf('new') > -1) {
21922                         if (month == 11) {
21923                             month = 0;
21924                             year += 1;
21925                         } else {
21926                             month += 1;
21927                         }
21928                     }
21929                     //Roo.log([year,month,day]);
21930                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21931                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21932 //                    this.fill();
21933                     //Roo.log(this.formatDate(this.date));
21934                     this.setValue(this.formatDate(this.date));
21935                     this.hidePopup();
21936                 }
21937                 break;
21938         }
21939     },
21940     
21941     setStartDate: function(startDate)
21942     {
21943         this.startDate = startDate || -Infinity;
21944         if (this.startDate !== -Infinity) {
21945             this.startDate = this.parseDate(this.startDate);
21946         }
21947         this.update();
21948         this.updateNavArrows();
21949     },
21950
21951     setEndDate: function(endDate)
21952     {
21953         this.endDate = endDate || Infinity;
21954         if (this.endDate !== Infinity) {
21955             this.endDate = this.parseDate(this.endDate);
21956         }
21957         this.update();
21958         this.updateNavArrows();
21959     },
21960     
21961     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21962     {
21963         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21964         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21965             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21966         }
21967         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21968             return parseInt(d, 10);
21969         });
21970         this.update();
21971         this.updateNavArrows();
21972     },
21973     
21974     updateNavArrows: function() 
21975     {
21976         if(this.singleMode){
21977             return;
21978         }
21979         
21980         var d = new Date(this.viewDate),
21981         year = d.getUTCFullYear(),
21982         month = d.getUTCMonth();
21983         
21984         Roo.each(this.picker().select('.prev', true).elements, function(v){
21985             v.show();
21986             switch (this.viewMode) {
21987                 case 0:
21988
21989                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21990                         v.hide();
21991                     }
21992                     break;
21993                 case 1:
21994                 case 2:
21995                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21996                         v.hide();
21997                     }
21998                     break;
21999             }
22000         });
22001         
22002         Roo.each(this.picker().select('.next', true).elements, function(v){
22003             v.show();
22004             switch (this.viewMode) {
22005                 case 0:
22006
22007                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22008                         v.hide();
22009                     }
22010                     break;
22011                 case 1:
22012                 case 2:
22013                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22014                         v.hide();
22015                     }
22016                     break;
22017             }
22018         })
22019     },
22020     
22021     moveMonth: function(date, dir)
22022     {
22023         if (!dir) {
22024             return date;
22025         }
22026         var new_date = new Date(date.valueOf()),
22027         day = new_date.getUTCDate(),
22028         month = new_date.getUTCMonth(),
22029         mag = Math.abs(dir),
22030         new_month, test;
22031         dir = dir > 0 ? 1 : -1;
22032         if (mag == 1){
22033             test = dir == -1
22034             // If going back one month, make sure month is not current month
22035             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22036             ? function(){
22037                 return new_date.getUTCMonth() == month;
22038             }
22039             // If going forward one month, make sure month is as expected
22040             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22041             : function(){
22042                 return new_date.getUTCMonth() != new_month;
22043             };
22044             new_month = month + dir;
22045             new_date.setUTCMonth(new_month);
22046             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22047             if (new_month < 0 || new_month > 11) {
22048                 new_month = (new_month + 12) % 12;
22049             }
22050         } else {
22051             // For magnitudes >1, move one month at a time...
22052             for (var i=0; i<mag; i++) {
22053                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22054                 new_date = this.moveMonth(new_date, dir);
22055             }
22056             // ...then reset the day, keeping it in the new month
22057             new_month = new_date.getUTCMonth();
22058             new_date.setUTCDate(day);
22059             test = function(){
22060                 return new_month != new_date.getUTCMonth();
22061             };
22062         }
22063         // Common date-resetting loop -- if date is beyond end of month, make it
22064         // end of month
22065         while (test()){
22066             new_date.setUTCDate(--day);
22067             new_date.setUTCMonth(new_month);
22068         }
22069         return new_date;
22070     },
22071
22072     moveYear: function(date, dir)
22073     {
22074         return this.moveMonth(date, dir*12);
22075     },
22076
22077     dateWithinRange: function(date)
22078     {
22079         return date >= this.startDate && date <= this.endDate;
22080     },
22081
22082     
22083     remove: function() 
22084     {
22085         this.picker().remove();
22086     },
22087     
22088     validateValue : function(value)
22089     {
22090         if(this.getVisibilityEl().hasClass('hidden')){
22091             return true;
22092         }
22093         
22094         if(value.length < 1)  {
22095             if(this.allowBlank){
22096                 return true;
22097             }
22098             return false;
22099         }
22100         
22101         if(value.length < this.minLength){
22102             return false;
22103         }
22104         if(value.length > this.maxLength){
22105             return false;
22106         }
22107         if(this.vtype){
22108             var vt = Roo.form.VTypes;
22109             if(!vt[this.vtype](value, this)){
22110                 return false;
22111             }
22112         }
22113         if(typeof this.validator == "function"){
22114             var msg = this.validator(value);
22115             if(msg !== true){
22116                 return false;
22117             }
22118         }
22119         
22120         if(this.regex && !this.regex.test(value)){
22121             return false;
22122         }
22123         
22124         if(typeof(this.parseDate(value)) == 'undefined'){
22125             return false;
22126         }
22127         
22128         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22129             return false;
22130         }      
22131         
22132         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22133             return false;
22134         } 
22135         
22136         
22137         return true;
22138     },
22139     
22140     reset : function()
22141     {
22142         this.date = this.viewDate = '';
22143         
22144         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22145     }
22146    
22147 });
22148
22149 Roo.apply(Roo.bootstrap.DateField,  {
22150     
22151     head : {
22152         tag: 'thead',
22153         cn: [
22154         {
22155             tag: 'tr',
22156             cn: [
22157             {
22158                 tag: 'th',
22159                 cls: 'prev',
22160                 html: '<i class="fa fa-arrow-left"/>'
22161             },
22162             {
22163                 tag: 'th',
22164                 cls: 'switch',
22165                 colspan: '5'
22166             },
22167             {
22168                 tag: 'th',
22169                 cls: 'next',
22170                 html: '<i class="fa fa-arrow-right"/>'
22171             }
22172
22173             ]
22174         }
22175         ]
22176     },
22177     
22178     content : {
22179         tag: 'tbody',
22180         cn: [
22181         {
22182             tag: 'tr',
22183             cn: [
22184             {
22185                 tag: 'td',
22186                 colspan: '7'
22187             }
22188             ]
22189         }
22190         ]
22191     },
22192     
22193     footer : {
22194         tag: 'tfoot',
22195         cn: [
22196         {
22197             tag: 'tr',
22198             cn: [
22199             {
22200                 tag: 'th',
22201                 colspan: '7',
22202                 cls: 'today'
22203             }
22204                     
22205             ]
22206         }
22207         ]
22208     },
22209     
22210     dates:{
22211         en: {
22212             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22213             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22214             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22215             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22216             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22217             today: "Today"
22218         }
22219     },
22220     
22221     modes: [
22222     {
22223         clsName: 'days',
22224         navFnc: 'Month',
22225         navStep: 1
22226     },
22227     {
22228         clsName: 'months',
22229         navFnc: 'FullYear',
22230         navStep: 1
22231     },
22232     {
22233         clsName: 'years',
22234         navFnc: 'FullYear',
22235         navStep: 10
22236     }]
22237 });
22238
22239 Roo.apply(Roo.bootstrap.DateField,  {
22240   
22241     template : {
22242         tag: 'div',
22243         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22244         cn: [
22245         {
22246             tag: 'div',
22247             cls: 'datepicker-days',
22248             cn: [
22249             {
22250                 tag: 'table',
22251                 cls: 'table-condensed',
22252                 cn:[
22253                 Roo.bootstrap.DateField.head,
22254                 {
22255                     tag: 'tbody'
22256                 },
22257                 Roo.bootstrap.DateField.footer
22258                 ]
22259             }
22260             ]
22261         },
22262         {
22263             tag: 'div',
22264             cls: 'datepicker-months',
22265             cn: [
22266             {
22267                 tag: 'table',
22268                 cls: 'table-condensed',
22269                 cn:[
22270                 Roo.bootstrap.DateField.head,
22271                 Roo.bootstrap.DateField.content,
22272                 Roo.bootstrap.DateField.footer
22273                 ]
22274             }
22275             ]
22276         },
22277         {
22278             tag: 'div',
22279             cls: 'datepicker-years',
22280             cn: [
22281             {
22282                 tag: 'table',
22283                 cls: 'table-condensed',
22284                 cn:[
22285                 Roo.bootstrap.DateField.head,
22286                 Roo.bootstrap.DateField.content,
22287                 Roo.bootstrap.DateField.footer
22288                 ]
22289             }
22290             ]
22291         }
22292         ]
22293     }
22294 });
22295
22296  
22297
22298  /*
22299  * - LGPL
22300  *
22301  * TimeField
22302  * 
22303  */
22304
22305 /**
22306  * @class Roo.bootstrap.TimeField
22307  * @extends Roo.bootstrap.Input
22308  * Bootstrap DateField class
22309  * 
22310  * 
22311  * @constructor
22312  * Create a new TimeField
22313  * @param {Object} config The config object
22314  */
22315
22316 Roo.bootstrap.TimeField = function(config){
22317     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22318     this.addEvents({
22319             /**
22320              * @event show
22321              * Fires when this field show.
22322              * @param {Roo.bootstrap.DateField} thisthis
22323              * @param {Mixed} date The date value
22324              */
22325             show : true,
22326             /**
22327              * @event show
22328              * Fires when this field hide.
22329              * @param {Roo.bootstrap.DateField} this
22330              * @param {Mixed} date The date value
22331              */
22332             hide : true,
22333             /**
22334              * @event select
22335              * Fires when select a date.
22336              * @param {Roo.bootstrap.DateField} this
22337              * @param {Mixed} date The date value
22338              */
22339             select : true
22340         });
22341 };
22342
22343 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22344     
22345     /**
22346      * @cfg {String} format
22347      * The default time format string which can be overriden for localization support.  The format must be
22348      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22349      */
22350     format : "H:i",
22351
22352     getAutoCreate : function()
22353     {
22354         this.after = '<i class="fa far fa-clock"></i>';
22355         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22356         
22357          
22358     },
22359     onRender: function(ct, position)
22360     {
22361         
22362         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22363                 
22364         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22365         
22366         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22367         
22368         this.pop = this.picker().select('>.datepicker-time',true).first();
22369         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22370         
22371         this.picker().on('mousedown', this.onMousedown, this);
22372         this.picker().on('click', this.onClick, this);
22373         
22374         this.picker().addClass('datepicker-dropdown');
22375     
22376         this.fillTime();
22377         this.update();
22378             
22379         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22380         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22381         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22382         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22383         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22384         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22385
22386     },
22387     
22388     fireKey: function(e){
22389         if (!this.picker().isVisible()){
22390             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22391                 this.show();
22392             }
22393             return;
22394         }
22395
22396         e.preventDefault();
22397         
22398         switch(e.keyCode){
22399             case 27: // escape
22400                 this.hide();
22401                 break;
22402             case 37: // left
22403             case 39: // right
22404                 this.onTogglePeriod();
22405                 break;
22406             case 38: // up
22407                 this.onIncrementMinutes();
22408                 break;
22409             case 40: // down
22410                 this.onDecrementMinutes();
22411                 break;
22412             case 13: // enter
22413             case 9: // tab
22414                 this.setTime();
22415                 break;
22416         }
22417     },
22418     
22419     onClick: function(e) {
22420         e.stopPropagation();
22421         e.preventDefault();
22422     },
22423     
22424     picker : function()
22425     {
22426         return this.pickerEl;
22427     },
22428     
22429     fillTime: function()
22430     {    
22431         var time = this.pop.select('tbody', true).first();
22432         
22433         time.dom.innerHTML = '';
22434         
22435         time.createChild({
22436             tag: 'tr',
22437             cn: [
22438                 {
22439                     tag: 'td',
22440                     cn: [
22441                         {
22442                             tag: 'a',
22443                             href: '#',
22444                             cls: 'btn',
22445                             cn: [
22446                                 {
22447                                     tag: 'i',
22448                                     cls: 'hours-up fa fas fa-chevron-up'
22449                                 }
22450                             ]
22451                         } 
22452                     ]
22453                 },
22454                 {
22455                     tag: 'td',
22456                     cls: 'separator'
22457                 },
22458                 {
22459                     tag: 'td',
22460                     cn: [
22461                         {
22462                             tag: 'a',
22463                             href: '#',
22464                             cls: 'btn',
22465                             cn: [
22466                                 {
22467                                     tag: 'i',
22468                                     cls: 'minutes-up fa fas fa-chevron-up'
22469                                 }
22470                             ]
22471                         }
22472                     ]
22473                 },
22474                 {
22475                     tag: 'td',
22476                     cls: 'separator'
22477                 }
22478             ]
22479         });
22480         
22481         time.createChild({
22482             tag: 'tr',
22483             cn: [
22484                 {
22485                     tag: 'td',
22486                     cn: [
22487                         {
22488                             tag: 'span',
22489                             cls: 'timepicker-hour',
22490                             html: '00'
22491                         }  
22492                     ]
22493                 },
22494                 {
22495                     tag: 'td',
22496                     cls: 'separator',
22497                     html: ':'
22498                 },
22499                 {
22500                     tag: 'td',
22501                     cn: [
22502                         {
22503                             tag: 'span',
22504                             cls: 'timepicker-minute',
22505                             html: '00'
22506                         }  
22507                     ]
22508                 },
22509                 {
22510                     tag: 'td',
22511                     cls: 'separator'
22512                 },
22513                 {
22514                     tag: 'td',
22515                     cn: [
22516                         {
22517                             tag: 'button',
22518                             type: 'button',
22519                             cls: 'btn btn-primary period',
22520                             html: 'AM'
22521                             
22522                         }
22523                     ]
22524                 }
22525             ]
22526         });
22527         
22528         time.createChild({
22529             tag: 'tr',
22530             cn: [
22531                 {
22532                     tag: 'td',
22533                     cn: [
22534                         {
22535                             tag: 'a',
22536                             href: '#',
22537                             cls: 'btn',
22538                             cn: [
22539                                 {
22540                                     tag: 'span',
22541                                     cls: 'hours-down fa fas fa-chevron-down'
22542                                 }
22543                             ]
22544                         }
22545                     ]
22546                 },
22547                 {
22548                     tag: 'td',
22549                     cls: 'separator'
22550                 },
22551                 {
22552                     tag: 'td',
22553                     cn: [
22554                         {
22555                             tag: 'a',
22556                             href: '#',
22557                             cls: 'btn',
22558                             cn: [
22559                                 {
22560                                     tag: 'span',
22561                                     cls: 'minutes-down fa fas fa-chevron-down'
22562                                 }
22563                             ]
22564                         }
22565                     ]
22566                 },
22567                 {
22568                     tag: 'td',
22569                     cls: 'separator'
22570                 }
22571             ]
22572         });
22573         
22574     },
22575     
22576     update: function()
22577     {
22578         
22579         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22580         
22581         this.fill();
22582     },
22583     
22584     fill: function() 
22585     {
22586         var hours = this.time.getHours();
22587         var minutes = this.time.getMinutes();
22588         var period = 'AM';
22589         
22590         if(hours > 11){
22591             period = 'PM';
22592         }
22593         
22594         if(hours == 0){
22595             hours = 12;
22596         }
22597         
22598         
22599         if(hours > 12){
22600             hours = hours - 12;
22601         }
22602         
22603         if(hours < 10){
22604             hours = '0' + hours;
22605         }
22606         
22607         if(minutes < 10){
22608             minutes = '0' + minutes;
22609         }
22610         
22611         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22612         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22613         this.pop.select('button', true).first().dom.innerHTML = period;
22614         
22615     },
22616     
22617     place: function()
22618     {   
22619         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22620         
22621         var cls = ['bottom'];
22622         
22623         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22624             cls.pop();
22625             cls.push('top');
22626         }
22627         
22628         cls.push('right');
22629         
22630         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22631             cls.pop();
22632             cls.push('left');
22633         }
22634         //this.picker().setXY(20000,20000);
22635         this.picker().addClass(cls.join('-'));
22636         
22637         var _this = this;
22638         
22639         Roo.each(cls, function(c){
22640             if(c == 'bottom'){
22641                 (function() {
22642                  //  
22643                 }).defer(200);
22644                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22645                 //_this.picker().setTop(_this.inputEl().getHeight());
22646                 return;
22647             }
22648             if(c == 'top'){
22649                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22650                 
22651                 //_this.picker().setTop(0 - _this.picker().getHeight());
22652                 return;
22653             }
22654             /*
22655             if(c == 'left'){
22656                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22657                 return;
22658             }
22659             if(c == 'right'){
22660                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22661                 return;
22662             }
22663             */
22664         });
22665         
22666     },
22667   
22668     onFocus : function()
22669     {
22670         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22671         this.show();
22672     },
22673     
22674     onBlur : function()
22675     {
22676         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22677         this.hide();
22678     },
22679     
22680     show : function()
22681     {
22682         this.picker().show();
22683         this.pop.show();
22684         this.update();
22685         this.place();
22686         
22687         this.fireEvent('show', this, this.date);
22688     },
22689     
22690     hide : function()
22691     {
22692         this.picker().hide();
22693         this.pop.hide();
22694         
22695         this.fireEvent('hide', this, this.date);
22696     },
22697     
22698     setTime : function()
22699     {
22700         this.hide();
22701         this.setValue(this.time.format(this.format));
22702         
22703         this.fireEvent('select', this, this.date);
22704         
22705         
22706     },
22707     
22708     onMousedown: function(e){
22709         e.stopPropagation();
22710         e.preventDefault();
22711     },
22712     
22713     onIncrementHours: function()
22714     {
22715         Roo.log('onIncrementHours');
22716         this.time = this.time.add(Date.HOUR, 1);
22717         this.update();
22718         
22719     },
22720     
22721     onDecrementHours: function()
22722     {
22723         Roo.log('onDecrementHours');
22724         this.time = this.time.add(Date.HOUR, -1);
22725         this.update();
22726     },
22727     
22728     onIncrementMinutes: function()
22729     {
22730         Roo.log('onIncrementMinutes');
22731         this.time = this.time.add(Date.MINUTE, 1);
22732         this.update();
22733     },
22734     
22735     onDecrementMinutes: function()
22736     {
22737         Roo.log('onDecrementMinutes');
22738         this.time = this.time.add(Date.MINUTE, -1);
22739         this.update();
22740     },
22741     
22742     onTogglePeriod: function()
22743     {
22744         Roo.log('onTogglePeriod');
22745         this.time = this.time.add(Date.HOUR, 12);
22746         this.update();
22747     }
22748     
22749    
22750 });
22751  
22752
22753 Roo.apply(Roo.bootstrap.TimeField,  {
22754   
22755     template : {
22756         tag: 'div',
22757         cls: 'datepicker dropdown-menu',
22758         cn: [
22759             {
22760                 tag: 'div',
22761                 cls: 'datepicker-time',
22762                 cn: [
22763                 {
22764                     tag: 'table',
22765                     cls: 'table-condensed',
22766                     cn:[
22767                         {
22768                             tag: 'tbody',
22769                             cn: [
22770                                 {
22771                                     tag: 'tr',
22772                                     cn: [
22773                                     {
22774                                         tag: 'td',
22775                                         colspan: '7'
22776                                     }
22777                                     ]
22778                                 }
22779                             ]
22780                         },
22781                         {
22782                             tag: 'tfoot',
22783                             cn: [
22784                                 {
22785                                     tag: 'tr',
22786                                     cn: [
22787                                     {
22788                                         tag: 'th',
22789                                         colspan: '7',
22790                                         cls: '',
22791                                         cn: [
22792                                             {
22793                                                 tag: 'button',
22794                                                 cls: 'btn btn-info ok',
22795                                                 html: 'OK'
22796                                             }
22797                                         ]
22798                                     }
22799                     
22800                                     ]
22801                                 }
22802                             ]
22803                         }
22804                     ]
22805                 }
22806                 ]
22807             }
22808         ]
22809     }
22810 });
22811
22812  
22813
22814  /*
22815  * - LGPL
22816  *
22817  * MonthField
22818  * 
22819  */
22820
22821 /**
22822  * @class Roo.bootstrap.MonthField
22823  * @extends Roo.bootstrap.Input
22824  * Bootstrap MonthField class
22825  * 
22826  * @cfg {String} language default en
22827  * 
22828  * @constructor
22829  * Create a new MonthField
22830  * @param {Object} config The config object
22831  */
22832
22833 Roo.bootstrap.MonthField = function(config){
22834     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22835     
22836     this.addEvents({
22837         /**
22838          * @event show
22839          * Fires when this field show.
22840          * @param {Roo.bootstrap.MonthField} this
22841          * @param {Mixed} date The date value
22842          */
22843         show : true,
22844         /**
22845          * @event show
22846          * Fires when this field hide.
22847          * @param {Roo.bootstrap.MonthField} this
22848          * @param {Mixed} date The date value
22849          */
22850         hide : true,
22851         /**
22852          * @event select
22853          * Fires when select a date.
22854          * @param {Roo.bootstrap.MonthField} this
22855          * @param {String} oldvalue The old value
22856          * @param {String} newvalue The new value
22857          */
22858         select : true
22859     });
22860 };
22861
22862 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22863     
22864     onRender: function(ct, position)
22865     {
22866         
22867         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22868         
22869         this.language = this.language || 'en';
22870         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22871         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22872         
22873         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22874         this.isInline = false;
22875         this.isInput = true;
22876         this.component = this.el.select('.add-on', true).first() || false;
22877         this.component = (this.component && this.component.length === 0) ? false : this.component;
22878         this.hasInput = this.component && this.inputEL().length;
22879         
22880         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22881         
22882         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22883         
22884         this.picker().on('mousedown', this.onMousedown, this);
22885         this.picker().on('click', this.onClick, this);
22886         
22887         this.picker().addClass('datepicker-dropdown');
22888         
22889         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22890             v.setStyle('width', '189px');
22891         });
22892         
22893         this.fillMonths();
22894         
22895         this.update();
22896         
22897         if(this.isInline) {
22898             this.show();
22899         }
22900         
22901     },
22902     
22903     setValue: function(v, suppressEvent)
22904     {   
22905         var o = this.getValue();
22906         
22907         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22908         
22909         this.update();
22910
22911         if(suppressEvent !== true){
22912             this.fireEvent('select', this, o, v);
22913         }
22914         
22915     },
22916     
22917     getValue: function()
22918     {
22919         return this.value;
22920     },
22921     
22922     onClick: function(e) 
22923     {
22924         e.stopPropagation();
22925         e.preventDefault();
22926         
22927         var target = e.getTarget();
22928         
22929         if(target.nodeName.toLowerCase() === 'i'){
22930             target = Roo.get(target).dom.parentNode;
22931         }
22932         
22933         var nodeName = target.nodeName;
22934         var className = target.className;
22935         var html = target.innerHTML;
22936         
22937         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22938             return;
22939         }
22940         
22941         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22942         
22943         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22944         
22945         this.hide();
22946                         
22947     },
22948     
22949     picker : function()
22950     {
22951         return this.pickerEl;
22952     },
22953     
22954     fillMonths: function()
22955     {    
22956         var i = 0;
22957         var months = this.picker().select('>.datepicker-months td', true).first();
22958         
22959         months.dom.innerHTML = '';
22960         
22961         while (i < 12) {
22962             var month = {
22963                 tag: 'span',
22964                 cls: 'month',
22965                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22966             };
22967             
22968             months.createChild(month);
22969         }
22970         
22971     },
22972     
22973     update: function()
22974     {
22975         var _this = this;
22976         
22977         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22978             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22979         }
22980         
22981         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22982             e.removeClass('active');
22983             
22984             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22985                 e.addClass('active');
22986             }
22987         })
22988     },
22989     
22990     place: function()
22991     {
22992         if(this.isInline) {
22993             return;
22994         }
22995         
22996         this.picker().removeClass(['bottom', 'top']);
22997         
22998         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22999             /*
23000              * place to the top of element!
23001              *
23002              */
23003             
23004             this.picker().addClass('top');
23005             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23006             
23007             return;
23008         }
23009         
23010         this.picker().addClass('bottom');
23011         
23012         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23013     },
23014     
23015     onFocus : function()
23016     {
23017         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23018         this.show();
23019     },
23020     
23021     onBlur : function()
23022     {
23023         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23024         
23025         var d = this.inputEl().getValue();
23026         
23027         this.setValue(d);
23028                 
23029         this.hide();
23030     },
23031     
23032     show : function()
23033     {
23034         this.picker().show();
23035         this.picker().select('>.datepicker-months', true).first().show();
23036         this.update();
23037         this.place();
23038         
23039         this.fireEvent('show', this, this.date);
23040     },
23041     
23042     hide : function()
23043     {
23044         if(this.isInline) {
23045             return;
23046         }
23047         this.picker().hide();
23048         this.fireEvent('hide', this, this.date);
23049         
23050     },
23051     
23052     onMousedown: function(e)
23053     {
23054         e.stopPropagation();
23055         e.preventDefault();
23056     },
23057     
23058     keyup: function(e)
23059     {
23060         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23061         this.update();
23062     },
23063
23064     fireKey: function(e)
23065     {
23066         if (!this.picker().isVisible()){
23067             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23068                 this.show();
23069             }
23070             return;
23071         }
23072         
23073         var dir;
23074         
23075         switch(e.keyCode){
23076             case 27: // escape
23077                 this.hide();
23078                 e.preventDefault();
23079                 break;
23080             case 37: // left
23081             case 39: // right
23082                 dir = e.keyCode == 37 ? -1 : 1;
23083                 
23084                 this.vIndex = this.vIndex + dir;
23085                 
23086                 if(this.vIndex < 0){
23087                     this.vIndex = 0;
23088                 }
23089                 
23090                 if(this.vIndex > 11){
23091                     this.vIndex = 11;
23092                 }
23093                 
23094                 if(isNaN(this.vIndex)){
23095                     this.vIndex = 0;
23096                 }
23097                 
23098                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23099                 
23100                 break;
23101             case 38: // up
23102             case 40: // down
23103                 
23104                 dir = e.keyCode == 38 ? -1 : 1;
23105                 
23106                 this.vIndex = this.vIndex + dir * 4;
23107                 
23108                 if(this.vIndex < 0){
23109                     this.vIndex = 0;
23110                 }
23111                 
23112                 if(this.vIndex > 11){
23113                     this.vIndex = 11;
23114                 }
23115                 
23116                 if(isNaN(this.vIndex)){
23117                     this.vIndex = 0;
23118                 }
23119                 
23120                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23121                 break;
23122                 
23123             case 13: // enter
23124                 
23125                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23126                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23127                 }
23128                 
23129                 this.hide();
23130                 e.preventDefault();
23131                 break;
23132             case 9: // tab
23133                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23134                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23135                 }
23136                 this.hide();
23137                 break;
23138             case 16: // shift
23139             case 17: // ctrl
23140             case 18: // alt
23141                 break;
23142             default :
23143                 this.hide();
23144                 
23145         }
23146     },
23147     
23148     remove: function() 
23149     {
23150         this.picker().remove();
23151     }
23152    
23153 });
23154
23155 Roo.apply(Roo.bootstrap.MonthField,  {
23156     
23157     content : {
23158         tag: 'tbody',
23159         cn: [
23160         {
23161             tag: 'tr',
23162             cn: [
23163             {
23164                 tag: 'td',
23165                 colspan: '7'
23166             }
23167             ]
23168         }
23169         ]
23170     },
23171     
23172     dates:{
23173         en: {
23174             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23175             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23176         }
23177     }
23178 });
23179
23180 Roo.apply(Roo.bootstrap.MonthField,  {
23181   
23182     template : {
23183         tag: 'div',
23184         cls: 'datepicker dropdown-menu roo-dynamic',
23185         cn: [
23186             {
23187                 tag: 'div',
23188                 cls: 'datepicker-months',
23189                 cn: [
23190                 {
23191                     tag: 'table',
23192                     cls: 'table-condensed',
23193                     cn:[
23194                         Roo.bootstrap.DateField.content
23195                     ]
23196                 }
23197                 ]
23198             }
23199         ]
23200     }
23201 });
23202
23203  
23204
23205  
23206  /*
23207  * - LGPL
23208  *
23209  * CheckBox
23210  * 
23211  */
23212
23213 /**
23214  * @class Roo.bootstrap.CheckBox
23215  * @extends Roo.bootstrap.Input
23216  * Bootstrap CheckBox class
23217  * 
23218  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23219  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23220  * @cfg {String} boxLabel The text that appears beside the checkbox
23221  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23222  * @cfg {Boolean} checked initnal the element
23223  * @cfg {Boolean} inline inline the element (default false)
23224  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23225  * @cfg {String} tooltip label tooltip
23226  * 
23227  * @constructor
23228  * Create a new CheckBox
23229  * @param {Object} config The config object
23230  */
23231
23232 Roo.bootstrap.CheckBox = function(config){
23233     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23234    
23235     this.addEvents({
23236         /**
23237         * @event check
23238         * Fires when the element is checked or unchecked.
23239         * @param {Roo.bootstrap.CheckBox} this This input
23240         * @param {Boolean} checked The new checked value
23241         */
23242        check : true,
23243        /**
23244         * @event click
23245         * Fires when the element is click.
23246         * @param {Roo.bootstrap.CheckBox} this This input
23247         */
23248        click : true
23249     });
23250     
23251 };
23252
23253 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23254   
23255     inputType: 'checkbox',
23256     inputValue: 1,
23257     valueOff: 0,
23258     boxLabel: false,
23259     checked: false,
23260     weight : false,
23261     inline: false,
23262     tooltip : '',
23263     
23264     // checkbox success does not make any sense really.. 
23265     invalidClass : "",
23266     validClass : "",
23267     
23268     
23269     getAutoCreate : function()
23270     {
23271         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23272         
23273         var id = Roo.id();
23274         
23275         var cfg = {};
23276         
23277         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23278         
23279         if(this.inline){
23280             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23281         }
23282         
23283         var input =  {
23284             tag: 'input',
23285             id : id,
23286             type : this.inputType,
23287             value : this.inputValue,
23288             cls : 'roo-' + this.inputType, //'form-box',
23289             placeholder : this.placeholder || ''
23290             
23291         };
23292         
23293         if(this.inputType != 'radio'){
23294             var hidden =  {
23295                 tag: 'input',
23296                 type : 'hidden',
23297                 cls : 'roo-hidden-value',
23298                 value : this.checked ? this.inputValue : this.valueOff
23299             };
23300         }
23301         
23302             
23303         if (this.weight) { // Validity check?
23304             cfg.cls += " " + this.inputType + "-" + this.weight;
23305         }
23306         
23307         if (this.disabled) {
23308             input.disabled=true;
23309         }
23310         
23311         if(this.checked){
23312             input.checked = this.checked;
23313         }
23314         
23315         if (this.name) {
23316             
23317             input.name = this.name;
23318             
23319             if(this.inputType != 'radio'){
23320                 hidden.name = this.name;
23321                 input.name = '_hidden_' + this.name;
23322             }
23323         }
23324         
23325         if (this.size) {
23326             input.cls += ' input-' + this.size;
23327         }
23328         
23329         var settings=this;
23330         
23331         ['xs','sm','md','lg'].map(function(size){
23332             if (settings[size]) {
23333                 cfg.cls += ' col-' + size + '-' + settings[size];
23334             }
23335         });
23336         
23337         var inputblock = input;
23338          
23339         if (this.before || this.after) {
23340             
23341             inputblock = {
23342                 cls : 'input-group',
23343                 cn :  [] 
23344             };
23345             
23346             if (this.before) {
23347                 inputblock.cn.push({
23348                     tag :'span',
23349                     cls : 'input-group-addon',
23350                     html : this.before
23351                 });
23352             }
23353             
23354             inputblock.cn.push(input);
23355             
23356             if(this.inputType != 'radio'){
23357                 inputblock.cn.push(hidden);
23358             }
23359             
23360             if (this.after) {
23361                 inputblock.cn.push({
23362                     tag :'span',
23363                     cls : 'input-group-addon',
23364                     html : this.after
23365                 });
23366             }
23367             
23368         }
23369         var boxLabelCfg = false;
23370         
23371         if(this.boxLabel){
23372            
23373             boxLabelCfg = {
23374                 tag: 'label',
23375                 //'for': id, // box label is handled by onclick - so no for...
23376                 cls: 'box-label',
23377                 html: this.boxLabel
23378             };
23379             if(this.tooltip){
23380                 boxLabelCfg.tooltip = this.tooltip;
23381             }
23382              
23383         }
23384         
23385         
23386         if (align ==='left' && this.fieldLabel.length) {
23387 //                Roo.log("left and has label");
23388             cfg.cn = [
23389                 {
23390                     tag: 'label',
23391                     'for' :  id,
23392                     cls : 'control-label',
23393                     html : this.fieldLabel
23394                 },
23395                 {
23396                     cls : "", 
23397                     cn: [
23398                         inputblock
23399                     ]
23400                 }
23401             ];
23402             
23403             if (boxLabelCfg) {
23404                 cfg.cn[1].cn.push(boxLabelCfg);
23405             }
23406             
23407             if(this.labelWidth > 12){
23408                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23409             }
23410             
23411             if(this.labelWidth < 13 && this.labelmd == 0){
23412                 this.labelmd = this.labelWidth;
23413             }
23414             
23415             if(this.labellg > 0){
23416                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23417                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23418             }
23419             
23420             if(this.labelmd > 0){
23421                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23422                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23423             }
23424             
23425             if(this.labelsm > 0){
23426                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23427                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23428             }
23429             
23430             if(this.labelxs > 0){
23431                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23432                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23433             }
23434             
23435         } else if ( this.fieldLabel.length) {
23436 //                Roo.log(" label");
23437                 cfg.cn = [
23438                    
23439                     {
23440                         tag: this.boxLabel ? 'span' : 'label',
23441                         'for': id,
23442                         cls: 'control-label box-input-label',
23443                         //cls : 'input-group-addon',
23444                         html : this.fieldLabel
23445                     },
23446                     
23447                     inputblock
23448                     
23449                 ];
23450                 if (boxLabelCfg) {
23451                     cfg.cn.push(boxLabelCfg);
23452                 }
23453
23454         } else {
23455             
23456 //                Roo.log(" no label && no align");
23457                 cfg.cn = [  inputblock ] ;
23458                 if (boxLabelCfg) {
23459                     cfg.cn.push(boxLabelCfg);
23460                 }
23461
23462                 
23463         }
23464         
23465        
23466         
23467         if(this.inputType != 'radio'){
23468             cfg.cn.push(hidden);
23469         }
23470         
23471         return cfg;
23472         
23473     },
23474     
23475     /**
23476      * return the real input element.
23477      */
23478     inputEl: function ()
23479     {
23480         return this.el.select('input.roo-' + this.inputType,true).first();
23481     },
23482     hiddenEl: function ()
23483     {
23484         return this.el.select('input.roo-hidden-value',true).first();
23485     },
23486     
23487     labelEl: function()
23488     {
23489         return this.el.select('label.control-label',true).first();
23490     },
23491     /* depricated... */
23492     
23493     label: function()
23494     {
23495         return this.labelEl();
23496     },
23497     
23498     boxLabelEl: function()
23499     {
23500         return this.el.select('label.box-label',true).first();
23501     },
23502     
23503     initEvents : function()
23504     {
23505 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23506         
23507         this.inputEl().on('click', this.onClick,  this);
23508         
23509         if (this.boxLabel) { 
23510             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23511         }
23512         
23513         this.startValue = this.getValue();
23514         
23515         if(this.groupId){
23516             Roo.bootstrap.CheckBox.register(this);
23517         }
23518     },
23519     
23520     onClick : function(e)
23521     {   
23522         if(this.fireEvent('click', this, e) !== false){
23523             this.setChecked(!this.checked);
23524         }
23525         
23526     },
23527     
23528     setChecked : function(state,suppressEvent)
23529     {
23530         this.startValue = this.getValue();
23531
23532         if(this.inputType == 'radio'){
23533             
23534             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23535                 e.dom.checked = false;
23536             });
23537             
23538             this.inputEl().dom.checked = true;
23539             
23540             this.inputEl().dom.value = this.inputValue;
23541             
23542             if(suppressEvent !== true){
23543                 this.fireEvent('check', this, true);
23544             }
23545             
23546             this.validate();
23547             
23548             return;
23549         }
23550         
23551         this.checked = state;
23552         
23553         this.inputEl().dom.checked = state;
23554         
23555         
23556         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23557         
23558         if(suppressEvent !== true){
23559             this.fireEvent('check', this, state);
23560         }
23561         
23562         this.validate();
23563     },
23564     
23565     getValue : function()
23566     {
23567         if(this.inputType == 'radio'){
23568             return this.getGroupValue();
23569         }
23570         
23571         return this.hiddenEl().dom.value;
23572         
23573     },
23574     
23575     getGroupValue : function()
23576     {
23577         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23578             return '';
23579         }
23580         
23581         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23582     },
23583     
23584     setValue : function(v,suppressEvent)
23585     {
23586         if(this.inputType == 'radio'){
23587             this.setGroupValue(v, suppressEvent);
23588             return;
23589         }
23590         
23591         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23592         
23593         this.validate();
23594     },
23595     
23596     setGroupValue : function(v, suppressEvent)
23597     {
23598         this.startValue = this.getValue();
23599         
23600         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23601             e.dom.checked = false;
23602             
23603             if(e.dom.value == v){
23604                 e.dom.checked = true;
23605             }
23606         });
23607         
23608         if(suppressEvent !== true){
23609             this.fireEvent('check', this, true);
23610         }
23611
23612         this.validate();
23613         
23614         return;
23615     },
23616     
23617     validate : function()
23618     {
23619         if(this.getVisibilityEl().hasClass('hidden')){
23620             return true;
23621         }
23622         
23623         if(
23624                 this.disabled || 
23625                 (this.inputType == 'radio' && this.validateRadio()) ||
23626                 (this.inputType == 'checkbox' && this.validateCheckbox())
23627         ){
23628             this.markValid();
23629             return true;
23630         }
23631         
23632         this.markInvalid();
23633         return false;
23634     },
23635     
23636     validateRadio : function()
23637     {
23638         if(this.getVisibilityEl().hasClass('hidden')){
23639             return true;
23640         }
23641         
23642         if(this.allowBlank){
23643             return true;
23644         }
23645         
23646         var valid = false;
23647         
23648         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23649             if(!e.dom.checked){
23650                 return;
23651             }
23652             
23653             valid = true;
23654             
23655             return false;
23656         });
23657         
23658         return valid;
23659     },
23660     
23661     validateCheckbox : function()
23662     {
23663         if(!this.groupId){
23664             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23665             //return (this.getValue() == this.inputValue) ? true : false;
23666         }
23667         
23668         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23669         
23670         if(!group){
23671             return false;
23672         }
23673         
23674         var r = false;
23675         
23676         for(var i in group){
23677             if(group[i].el.isVisible(true)){
23678                 r = false;
23679                 break;
23680             }
23681             
23682             r = true;
23683         }
23684         
23685         for(var i in group){
23686             if(r){
23687                 break;
23688             }
23689             
23690             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23691         }
23692         
23693         return r;
23694     },
23695     
23696     /**
23697      * Mark this field as valid
23698      */
23699     markValid : function()
23700     {
23701         var _this = this;
23702         
23703         this.fireEvent('valid', this);
23704         
23705         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23706         
23707         if(this.groupId){
23708             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23709         }
23710         
23711         if(label){
23712             label.markValid();
23713         }
23714
23715         if(this.inputType == 'radio'){
23716             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23717                 var fg = e.findParent('.form-group', false, true);
23718                 if (Roo.bootstrap.version == 3) {
23719                     fg.removeClass([_this.invalidClass, _this.validClass]);
23720                     fg.addClass(_this.validClass);
23721                 } else {
23722                     fg.removeClass(['is-valid', 'is-invalid']);
23723                     fg.addClass('is-valid');
23724                 }
23725             });
23726             
23727             return;
23728         }
23729
23730         if(!this.groupId){
23731             var fg = this.el.findParent('.form-group', false, true);
23732             if (Roo.bootstrap.version == 3) {
23733                 fg.removeClass([this.invalidClass, this.validClass]);
23734                 fg.addClass(this.validClass);
23735             } else {
23736                 fg.removeClass(['is-valid', 'is-invalid']);
23737                 fg.addClass('is-valid');
23738             }
23739             return;
23740         }
23741         
23742         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23743         
23744         if(!group){
23745             return;
23746         }
23747         
23748         for(var i in group){
23749             var fg = group[i].el.findParent('.form-group', false, true);
23750             if (Roo.bootstrap.version == 3) {
23751                 fg.removeClass([this.invalidClass, this.validClass]);
23752                 fg.addClass(this.validClass);
23753             } else {
23754                 fg.removeClass(['is-valid', 'is-invalid']);
23755                 fg.addClass('is-valid');
23756             }
23757         }
23758     },
23759     
23760      /**
23761      * Mark this field as invalid
23762      * @param {String} msg The validation message
23763      */
23764     markInvalid : function(msg)
23765     {
23766         if(this.allowBlank){
23767             return;
23768         }
23769         
23770         var _this = this;
23771         
23772         this.fireEvent('invalid', this, msg);
23773         
23774         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23775         
23776         if(this.groupId){
23777             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23778         }
23779         
23780         if(label){
23781             label.markInvalid();
23782         }
23783             
23784         if(this.inputType == 'radio'){
23785             
23786             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23787                 var fg = e.findParent('.form-group', false, true);
23788                 if (Roo.bootstrap.version == 3) {
23789                     fg.removeClass([_this.invalidClass, _this.validClass]);
23790                     fg.addClass(_this.invalidClass);
23791                 } else {
23792                     fg.removeClass(['is-invalid', 'is-valid']);
23793                     fg.addClass('is-invalid');
23794                 }
23795             });
23796             
23797             return;
23798         }
23799         
23800         if(!this.groupId){
23801             var fg = this.el.findParent('.form-group', false, true);
23802             if (Roo.bootstrap.version == 3) {
23803                 fg.removeClass([_this.invalidClass, _this.validClass]);
23804                 fg.addClass(_this.invalidClass);
23805             } else {
23806                 fg.removeClass(['is-invalid', 'is-valid']);
23807                 fg.addClass('is-invalid');
23808             }
23809             return;
23810         }
23811         
23812         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23813         
23814         if(!group){
23815             return;
23816         }
23817         
23818         for(var i in group){
23819             var fg = group[i].el.findParent('.form-group', false, true);
23820             if (Roo.bootstrap.version == 3) {
23821                 fg.removeClass([_this.invalidClass, _this.validClass]);
23822                 fg.addClass(_this.invalidClass);
23823             } else {
23824                 fg.removeClass(['is-invalid', 'is-valid']);
23825                 fg.addClass('is-invalid');
23826             }
23827         }
23828         
23829     },
23830     
23831     clearInvalid : function()
23832     {
23833         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23834         
23835         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23836         
23837         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23838         
23839         if (label && label.iconEl) {
23840             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23841             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23842         }
23843     },
23844     
23845     disable : function()
23846     {
23847         if(this.inputType != 'radio'){
23848             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23849             return;
23850         }
23851         
23852         var _this = this;
23853         
23854         if(this.rendered){
23855             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23856                 _this.getActionEl().addClass(this.disabledClass);
23857                 e.dom.disabled = true;
23858             });
23859         }
23860         
23861         this.disabled = true;
23862         this.fireEvent("disable", this);
23863         return this;
23864     },
23865
23866     enable : function()
23867     {
23868         if(this.inputType != 'radio'){
23869             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23870             return;
23871         }
23872         
23873         var _this = this;
23874         
23875         if(this.rendered){
23876             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23877                 _this.getActionEl().removeClass(this.disabledClass);
23878                 e.dom.disabled = false;
23879             });
23880         }
23881         
23882         this.disabled = false;
23883         this.fireEvent("enable", this);
23884         return this;
23885     },
23886     
23887     setBoxLabel : function(v)
23888     {
23889         this.boxLabel = v;
23890         
23891         if(this.rendered){
23892             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23893         }
23894     }
23895
23896 });
23897
23898 Roo.apply(Roo.bootstrap.CheckBox, {
23899     
23900     groups: {},
23901     
23902      /**
23903     * register a CheckBox Group
23904     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23905     */
23906     register : function(checkbox)
23907     {
23908         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23909             this.groups[checkbox.groupId] = {};
23910         }
23911         
23912         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23913             return;
23914         }
23915         
23916         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23917         
23918     },
23919     /**
23920     * fetch a CheckBox Group based on the group ID
23921     * @param {string} the group ID
23922     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23923     */
23924     get: function(groupId) {
23925         if (typeof(this.groups[groupId]) == 'undefined') {
23926             return false;
23927         }
23928         
23929         return this.groups[groupId] ;
23930     }
23931     
23932     
23933 });
23934 /*
23935  * - LGPL
23936  *
23937  * RadioItem
23938  * 
23939  */
23940
23941 /**
23942  * @class Roo.bootstrap.Radio
23943  * @extends Roo.bootstrap.Component
23944  * Bootstrap Radio class
23945  * @cfg {String} boxLabel - the label associated
23946  * @cfg {String} value - the value of radio
23947  * 
23948  * @constructor
23949  * Create a new Radio
23950  * @param {Object} config The config object
23951  */
23952 Roo.bootstrap.Radio = function(config){
23953     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23954     
23955 };
23956
23957 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23958     
23959     boxLabel : '',
23960     
23961     value : '',
23962     
23963     getAutoCreate : function()
23964     {
23965         var cfg = {
23966             tag : 'div',
23967             cls : 'form-group radio',
23968             cn : [
23969                 {
23970                     tag : 'label',
23971                     cls : 'box-label',
23972                     html : this.boxLabel
23973                 }
23974             ]
23975         };
23976         
23977         return cfg;
23978     },
23979     
23980     initEvents : function() 
23981     {
23982         this.parent().register(this);
23983         
23984         this.el.on('click', this.onClick, this);
23985         
23986     },
23987     
23988     onClick : function(e)
23989     {
23990         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23991             this.setChecked(true);
23992         }
23993     },
23994     
23995     setChecked : function(state, suppressEvent)
23996     {
23997         this.parent().setValue(this.value, suppressEvent);
23998         
23999     },
24000     
24001     setBoxLabel : function(v)
24002     {
24003         this.boxLabel = v;
24004         
24005         if(this.rendered){
24006             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24007         }
24008     }
24009     
24010 });
24011  
24012
24013  /*
24014  * - LGPL
24015  *
24016  * Input
24017  * 
24018  */
24019
24020 /**
24021  * @class Roo.bootstrap.SecurePass
24022  * @extends Roo.bootstrap.Input
24023  * Bootstrap SecurePass class
24024  *
24025  * 
24026  * @constructor
24027  * Create a new SecurePass
24028  * @param {Object} config The config object
24029  */
24030  
24031 Roo.bootstrap.SecurePass = function (config) {
24032     // these go here, so the translation tool can replace them..
24033     this.errors = {
24034         PwdEmpty: "Please type a password, and then retype it to confirm.",
24035         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24036         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24037         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24038         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24039         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24040         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24041         TooWeak: "Your password is Too Weak."
24042     },
24043     this.meterLabel = "Password strength:";
24044     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24045     this.meterClass = [
24046         "roo-password-meter-tooweak", 
24047         "roo-password-meter-weak", 
24048         "roo-password-meter-medium", 
24049         "roo-password-meter-strong", 
24050         "roo-password-meter-grey"
24051     ];
24052     
24053     this.errors = {};
24054     
24055     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24056 }
24057
24058 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24059     /**
24060      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24061      * {
24062      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24063      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24064      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24065      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24066      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24067      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24068      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24069      * })
24070      */
24071     // private
24072     
24073     meterWidth: 300,
24074     errorMsg :'',    
24075     errors: false,
24076     imageRoot: '/',
24077     /**
24078      * @cfg {String/Object} Label for the strength meter (defaults to
24079      * 'Password strength:')
24080      */
24081     // private
24082     meterLabel: '',
24083     /**
24084      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24085      * ['Weak', 'Medium', 'Strong'])
24086      */
24087     // private    
24088     pwdStrengths: false,    
24089     // private
24090     strength: 0,
24091     // private
24092     _lastPwd: null,
24093     // private
24094     kCapitalLetter: 0,
24095     kSmallLetter: 1,
24096     kDigit: 2,
24097     kPunctuation: 3,
24098     
24099     insecure: false,
24100     // private
24101     initEvents: function ()
24102     {
24103         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24104
24105         if (this.el.is('input[type=password]') && Roo.isSafari) {
24106             this.el.on('keydown', this.SafariOnKeyDown, this);
24107         }
24108
24109         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24110     },
24111     // private
24112     onRender: function (ct, position)
24113     {
24114         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24115         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24116         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24117
24118         this.trigger.createChild({
24119                    cn: [
24120                     {
24121                     //id: 'PwdMeter',
24122                     tag: 'div',
24123                     cls: 'roo-password-meter-grey col-xs-12',
24124                     style: {
24125                         //width: 0,
24126                         //width: this.meterWidth + 'px'                                                
24127                         }
24128                     },
24129                     {                            
24130                          cls: 'roo-password-meter-text'                          
24131                     }
24132                 ]            
24133         });
24134
24135          
24136         if (this.hideTrigger) {
24137             this.trigger.setDisplayed(false);
24138         }
24139         this.setSize(this.width || '', this.height || '');
24140     },
24141     // private
24142     onDestroy: function ()
24143     {
24144         if (this.trigger) {
24145             this.trigger.removeAllListeners();
24146             this.trigger.remove();
24147         }
24148         if (this.wrap) {
24149             this.wrap.remove();
24150         }
24151         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24152     },
24153     // private
24154     checkStrength: function ()
24155     {
24156         var pwd = this.inputEl().getValue();
24157         if (pwd == this._lastPwd) {
24158             return;
24159         }
24160
24161         var strength;
24162         if (this.ClientSideStrongPassword(pwd)) {
24163             strength = 3;
24164         } else if (this.ClientSideMediumPassword(pwd)) {
24165             strength = 2;
24166         } else if (this.ClientSideWeakPassword(pwd)) {
24167             strength = 1;
24168         } else {
24169             strength = 0;
24170         }
24171         
24172         Roo.log('strength1: ' + strength);
24173         
24174         //var pm = this.trigger.child('div/div/div').dom;
24175         var pm = this.trigger.child('div/div');
24176         pm.removeClass(this.meterClass);
24177         pm.addClass(this.meterClass[strength]);
24178                 
24179         
24180         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24181                 
24182         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24183         
24184         this._lastPwd = pwd;
24185     },
24186     reset: function ()
24187     {
24188         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24189         
24190         this._lastPwd = '';
24191         
24192         var pm = this.trigger.child('div/div');
24193         pm.removeClass(this.meterClass);
24194         pm.addClass('roo-password-meter-grey');        
24195         
24196         
24197         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24198         
24199         pt.innerHTML = '';
24200         this.inputEl().dom.type='password';
24201     },
24202     // private
24203     validateValue: function (value)
24204     {
24205         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24206             return false;
24207         }
24208         if (value.length == 0) {
24209             if (this.allowBlank) {
24210                 this.clearInvalid();
24211                 return true;
24212             }
24213
24214             this.markInvalid(this.errors.PwdEmpty);
24215             this.errorMsg = this.errors.PwdEmpty;
24216             return false;
24217         }
24218         
24219         if(this.insecure){
24220             return true;
24221         }
24222         
24223         if (!value.match(/[\x21-\x7e]+/)) {
24224             this.markInvalid(this.errors.PwdBadChar);
24225             this.errorMsg = this.errors.PwdBadChar;
24226             return false;
24227         }
24228         if (value.length < 6) {
24229             this.markInvalid(this.errors.PwdShort);
24230             this.errorMsg = this.errors.PwdShort;
24231             return false;
24232         }
24233         if (value.length > 16) {
24234             this.markInvalid(this.errors.PwdLong);
24235             this.errorMsg = this.errors.PwdLong;
24236             return false;
24237         }
24238         var strength;
24239         if (this.ClientSideStrongPassword(value)) {
24240             strength = 3;
24241         } else if (this.ClientSideMediumPassword(value)) {
24242             strength = 2;
24243         } else if (this.ClientSideWeakPassword(value)) {
24244             strength = 1;
24245         } else {
24246             strength = 0;
24247         }
24248
24249         
24250         if (strength < 2) {
24251             //this.markInvalid(this.errors.TooWeak);
24252             this.errorMsg = this.errors.TooWeak;
24253             //return false;
24254         }
24255         
24256         
24257         console.log('strength2: ' + strength);
24258         
24259         //var pm = this.trigger.child('div/div/div').dom;
24260         
24261         var pm = this.trigger.child('div/div');
24262         pm.removeClass(this.meterClass);
24263         pm.addClass(this.meterClass[strength]);
24264                 
24265         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24266                 
24267         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24268         
24269         this.errorMsg = ''; 
24270         return true;
24271     },
24272     // private
24273     CharacterSetChecks: function (type)
24274     {
24275         this.type = type;
24276         this.fResult = false;
24277     },
24278     // private
24279     isctype: function (character, type)
24280     {
24281         switch (type) {  
24282             case this.kCapitalLetter:
24283                 if (character >= 'A' && character <= 'Z') {
24284                     return true;
24285                 }
24286                 break;
24287             
24288             case this.kSmallLetter:
24289                 if (character >= 'a' && character <= 'z') {
24290                     return true;
24291                 }
24292                 break;
24293             
24294             case this.kDigit:
24295                 if (character >= '0' && character <= '9') {
24296                     return true;
24297                 }
24298                 break;
24299             
24300             case this.kPunctuation:
24301                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24302                     return true;
24303                 }
24304                 break;
24305             
24306             default:
24307                 return false;
24308         }
24309
24310     },
24311     // private
24312     IsLongEnough: function (pwd, size)
24313     {
24314         return !(pwd == null || isNaN(size) || pwd.length < size);
24315     },
24316     // private
24317     SpansEnoughCharacterSets: function (word, nb)
24318     {
24319         if (!this.IsLongEnough(word, nb))
24320         {
24321             return false;
24322         }
24323
24324         var characterSetChecks = new Array(
24325             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24326             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24327         );
24328         
24329         for (var index = 0; index < word.length; ++index) {
24330             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24331                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24332                     characterSetChecks[nCharSet].fResult = true;
24333                     break;
24334                 }
24335             }
24336         }
24337
24338         var nCharSets = 0;
24339         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24340             if (characterSetChecks[nCharSet].fResult) {
24341                 ++nCharSets;
24342             }
24343         }
24344
24345         if (nCharSets < nb) {
24346             return false;
24347         }
24348         return true;
24349     },
24350     // private
24351     ClientSideStrongPassword: function (pwd)
24352     {
24353         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24354     },
24355     // private
24356     ClientSideMediumPassword: function (pwd)
24357     {
24358         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24359     },
24360     // private
24361     ClientSideWeakPassword: function (pwd)
24362     {
24363         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24364     }
24365           
24366 })//<script type="text/javascript">
24367
24368 /*
24369  * Based  Ext JS Library 1.1.1
24370  * Copyright(c) 2006-2007, Ext JS, LLC.
24371  * LGPL
24372  *
24373  */
24374  
24375 /**
24376  * @class Roo.HtmlEditorCore
24377  * @extends Roo.Component
24378  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24379  *
24380  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24381  */
24382
24383 Roo.HtmlEditorCore = function(config){
24384     
24385     
24386     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24387     
24388     
24389     this.addEvents({
24390         /**
24391          * @event initialize
24392          * Fires when the editor is fully initialized (including the iframe)
24393          * @param {Roo.HtmlEditorCore} this
24394          */
24395         initialize: true,
24396         /**
24397          * @event activate
24398          * Fires when the editor is first receives the focus. Any insertion must wait
24399          * until after this event.
24400          * @param {Roo.HtmlEditorCore} this
24401          */
24402         activate: true,
24403          /**
24404          * @event beforesync
24405          * Fires before the textarea is updated with content from the editor iframe. Return false
24406          * to cancel the sync.
24407          * @param {Roo.HtmlEditorCore} this
24408          * @param {String} html
24409          */
24410         beforesync: true,
24411          /**
24412          * @event beforepush
24413          * Fires before the iframe editor is updated with content from the textarea. Return false
24414          * to cancel the push.
24415          * @param {Roo.HtmlEditorCore} this
24416          * @param {String} html
24417          */
24418         beforepush: true,
24419          /**
24420          * @event sync
24421          * Fires when the textarea is updated with content from the editor iframe.
24422          * @param {Roo.HtmlEditorCore} this
24423          * @param {String} html
24424          */
24425         sync: true,
24426          /**
24427          * @event push
24428          * Fires when the iframe editor is updated with content from the textarea.
24429          * @param {Roo.HtmlEditorCore} this
24430          * @param {String} html
24431          */
24432         push: true,
24433         
24434         /**
24435          * @event editorevent
24436          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24437          * @param {Roo.HtmlEditorCore} this
24438          */
24439         editorevent: true
24440         
24441     });
24442     
24443     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24444     
24445     // defaults : white / black...
24446     this.applyBlacklists();
24447     
24448     
24449     
24450 };
24451
24452
24453 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24454
24455
24456      /**
24457      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24458      */
24459     
24460     owner : false,
24461     
24462      /**
24463      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24464      *                        Roo.resizable.
24465      */
24466     resizable : false,
24467      /**
24468      * @cfg {Number} height (in pixels)
24469      */   
24470     height: 300,
24471    /**
24472      * @cfg {Number} width (in pixels)
24473      */   
24474     width: 500,
24475     
24476     /**
24477      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24478      * 
24479      */
24480     stylesheets: false,
24481     
24482     // id of frame..
24483     frameId: false,
24484     
24485     // private properties
24486     validationEvent : false,
24487     deferHeight: true,
24488     initialized : false,
24489     activated : false,
24490     sourceEditMode : false,
24491     onFocus : Roo.emptyFn,
24492     iframePad:3,
24493     hideMode:'offsets',
24494     
24495     clearUp: true,
24496     
24497     // blacklist + whitelisted elements..
24498     black: false,
24499     white: false,
24500      
24501     bodyCls : '',
24502
24503     /**
24504      * Protected method that will not generally be called directly. It
24505      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24506      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24507      */
24508     getDocMarkup : function(){
24509         // body styles..
24510         var st = '';
24511         
24512         // inherit styels from page...?? 
24513         if (this.stylesheets === false) {
24514             
24515             Roo.get(document.head).select('style').each(function(node) {
24516                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24517             });
24518             
24519             Roo.get(document.head).select('link').each(function(node) { 
24520                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24521             });
24522             
24523         } else if (!this.stylesheets.length) {
24524                 // simple..
24525                 st = '<style type="text/css">' +
24526                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24527                    '</style>';
24528         } else {
24529             for (var i in this.stylesheets) { 
24530                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24531             }
24532             
24533         }
24534         
24535         st +=  '<style type="text/css">' +
24536             'IMG { cursor: pointer } ' +
24537         '</style>';
24538
24539         var cls = 'roo-htmleditor-body';
24540         
24541         if(this.bodyCls.length){
24542             cls += ' ' + this.bodyCls;
24543         }
24544         
24545         return '<html><head>' + st  +
24546             //<style type="text/css">' +
24547             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24548             //'</style>' +
24549             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24550     },
24551
24552     // private
24553     onRender : function(ct, position)
24554     {
24555         var _t = this;
24556         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24557         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24558         
24559         
24560         this.el.dom.style.border = '0 none';
24561         this.el.dom.setAttribute('tabIndex', -1);
24562         this.el.addClass('x-hidden hide');
24563         
24564         
24565         
24566         if(Roo.isIE){ // fix IE 1px bogus margin
24567             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24568         }
24569        
24570         
24571         this.frameId = Roo.id();
24572         
24573          
24574         
24575         var iframe = this.owner.wrap.createChild({
24576             tag: 'iframe',
24577             cls: 'form-control', // bootstrap..
24578             id: this.frameId,
24579             name: this.frameId,
24580             frameBorder : 'no',
24581             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24582         }, this.el
24583         );
24584         
24585         
24586         this.iframe = iframe.dom;
24587
24588          this.assignDocWin();
24589         
24590         this.doc.designMode = 'on';
24591        
24592         this.doc.open();
24593         this.doc.write(this.getDocMarkup());
24594         this.doc.close();
24595
24596         
24597         var task = { // must defer to wait for browser to be ready
24598             run : function(){
24599                 //console.log("run task?" + this.doc.readyState);
24600                 this.assignDocWin();
24601                 if(this.doc.body || this.doc.readyState == 'complete'){
24602                     try {
24603                         this.doc.designMode="on";
24604                     } catch (e) {
24605                         return;
24606                     }
24607                     Roo.TaskMgr.stop(task);
24608                     this.initEditor.defer(10, this);
24609                 }
24610             },
24611             interval : 10,
24612             duration: 10000,
24613             scope: this
24614         };
24615         Roo.TaskMgr.start(task);
24616
24617     },
24618
24619     // private
24620     onResize : function(w, h)
24621     {
24622          Roo.log('resize: ' +w + ',' + h );
24623         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24624         if(!this.iframe){
24625             return;
24626         }
24627         if(typeof w == 'number'){
24628             
24629             this.iframe.style.width = w + 'px';
24630         }
24631         if(typeof h == 'number'){
24632             
24633             this.iframe.style.height = h + 'px';
24634             if(this.doc){
24635                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24636             }
24637         }
24638         
24639     },
24640
24641     /**
24642      * Toggles the editor between standard and source edit mode.
24643      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24644      */
24645     toggleSourceEdit : function(sourceEditMode){
24646         
24647         this.sourceEditMode = sourceEditMode === true;
24648         
24649         if(this.sourceEditMode){
24650  
24651             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24652             
24653         }else{
24654             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24655             //this.iframe.className = '';
24656             this.deferFocus();
24657         }
24658         //this.setSize(this.owner.wrap.getSize());
24659         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24660     },
24661
24662     
24663   
24664
24665     /**
24666      * Protected method that will not generally be called directly. If you need/want
24667      * custom HTML cleanup, this is the method you should override.
24668      * @param {String} html The HTML to be cleaned
24669      * return {String} The cleaned HTML
24670      */
24671     cleanHtml : function(html){
24672         html = String(html);
24673         if(html.length > 5){
24674             if(Roo.isSafari){ // strip safari nonsense
24675                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24676             }
24677         }
24678         if(html == '&nbsp;'){
24679             html = '';
24680         }
24681         return html;
24682     },
24683
24684     /**
24685      * HTML Editor -> Textarea
24686      * Protected method that will not generally be called directly. Syncs the contents
24687      * of the editor iframe with the textarea.
24688      */
24689     syncValue : function(){
24690         if(this.initialized){
24691             var bd = (this.doc.body || this.doc.documentElement);
24692             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24693             var html = bd.innerHTML;
24694             if(Roo.isSafari){
24695                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24696                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24697                 if(m && m[1]){
24698                     html = '<div style="'+m[0]+'">' + html + '</div>';
24699                 }
24700             }
24701             html = this.cleanHtml(html);
24702             // fix up the special chars.. normaly like back quotes in word...
24703             // however we do not want to do this with chinese..
24704             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24705                 
24706                 var cc = match.charCodeAt();
24707
24708                 // Get the character value, handling surrogate pairs
24709                 if (match.length == 2) {
24710                     // It's a surrogate pair, calculate the Unicode code point
24711                     var high = match.charCodeAt(0) - 0xD800;
24712                     var low  = match.charCodeAt(1) - 0xDC00;
24713                     cc = (high * 0x400) + low + 0x10000;
24714                 }  else if (
24715                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24716                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24717                     (cc >= 0xf900 && cc < 0xfb00 )
24718                 ) {
24719                         return match;
24720                 }  
24721          
24722                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24723                 return "&#" + cc + ";";
24724                 
24725                 
24726             });
24727             
24728             
24729              
24730             if(this.owner.fireEvent('beforesync', this, html) !== false){
24731                 this.el.dom.value = html;
24732                 this.owner.fireEvent('sync', this, html);
24733             }
24734         }
24735     },
24736
24737     /**
24738      * Protected method that will not generally be called directly. Pushes the value of the textarea
24739      * into the iframe editor.
24740      */
24741     pushValue : function(){
24742         if(this.initialized){
24743             var v = this.el.dom.value.trim();
24744             
24745 //            if(v.length < 1){
24746 //                v = '&#160;';
24747 //            }
24748             
24749             if(this.owner.fireEvent('beforepush', this, v) !== false){
24750                 var d = (this.doc.body || this.doc.documentElement);
24751                 d.innerHTML = v;
24752                 this.cleanUpPaste();
24753                 this.el.dom.value = d.innerHTML;
24754                 this.owner.fireEvent('push', this, v);
24755             }
24756         }
24757     },
24758
24759     // private
24760     deferFocus : function(){
24761         this.focus.defer(10, this);
24762     },
24763
24764     // doc'ed in Field
24765     focus : function(){
24766         if(this.win && !this.sourceEditMode){
24767             this.win.focus();
24768         }else{
24769             this.el.focus();
24770         }
24771     },
24772     
24773     assignDocWin: function()
24774     {
24775         var iframe = this.iframe;
24776         
24777          if(Roo.isIE){
24778             this.doc = iframe.contentWindow.document;
24779             this.win = iframe.contentWindow;
24780         } else {
24781 //            if (!Roo.get(this.frameId)) {
24782 //                return;
24783 //            }
24784 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24785 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24786             
24787             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24788                 return;
24789             }
24790             
24791             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24792             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24793         }
24794     },
24795     
24796     // private
24797     initEditor : function(){
24798         //console.log("INIT EDITOR");
24799         this.assignDocWin();
24800         
24801         
24802         
24803         this.doc.designMode="on";
24804         this.doc.open();
24805         this.doc.write(this.getDocMarkup());
24806         this.doc.close();
24807         
24808         var dbody = (this.doc.body || this.doc.documentElement);
24809         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24810         // this copies styles from the containing element into thsi one..
24811         // not sure why we need all of this..
24812         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24813         
24814         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24815         //ss['background-attachment'] = 'fixed'; // w3c
24816         dbody.bgProperties = 'fixed'; // ie
24817         //Roo.DomHelper.applyStyles(dbody, ss);
24818         Roo.EventManager.on(this.doc, {
24819             //'mousedown': this.onEditorEvent,
24820             'mouseup': this.onEditorEvent,
24821             'dblclick': this.onEditorEvent,
24822             'click': this.onEditorEvent,
24823             'keyup': this.onEditorEvent,
24824             buffer:100,
24825             scope: this
24826         });
24827         if(Roo.isGecko){
24828             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24829         }
24830         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24831             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24832         }
24833         this.initialized = true;
24834
24835         this.owner.fireEvent('initialize', this);
24836         this.pushValue();
24837     },
24838
24839     // private
24840     onDestroy : function(){
24841         
24842         
24843         
24844         if(this.rendered){
24845             
24846             //for (var i =0; i < this.toolbars.length;i++) {
24847             //    // fixme - ask toolbars for heights?
24848             //    this.toolbars[i].onDestroy();
24849            // }
24850             
24851             //this.wrap.dom.innerHTML = '';
24852             //this.wrap.remove();
24853         }
24854     },
24855
24856     // private
24857     onFirstFocus : function(){
24858         
24859         this.assignDocWin();
24860         
24861         
24862         this.activated = true;
24863          
24864     
24865         if(Roo.isGecko){ // prevent silly gecko errors
24866             this.win.focus();
24867             var s = this.win.getSelection();
24868             if(!s.focusNode || s.focusNode.nodeType != 3){
24869                 var r = s.getRangeAt(0);
24870                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24871                 r.collapse(true);
24872                 this.deferFocus();
24873             }
24874             try{
24875                 this.execCmd('useCSS', true);
24876                 this.execCmd('styleWithCSS', false);
24877             }catch(e){}
24878         }
24879         this.owner.fireEvent('activate', this);
24880     },
24881
24882     // private
24883     adjustFont: function(btn){
24884         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24885         //if(Roo.isSafari){ // safari
24886         //    adjust *= 2;
24887        // }
24888         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24889         if(Roo.isSafari){ // safari
24890             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24891             v =  (v < 10) ? 10 : v;
24892             v =  (v > 48) ? 48 : v;
24893             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24894             
24895         }
24896         
24897         
24898         v = Math.max(1, v+adjust);
24899         
24900         this.execCmd('FontSize', v  );
24901     },
24902
24903     onEditorEvent : function(e)
24904     {
24905         this.owner.fireEvent('editorevent', this, e);
24906       //  this.updateToolbar();
24907         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24908     },
24909
24910     insertTag : function(tg)
24911     {
24912         // could be a bit smarter... -> wrap the current selected tRoo..
24913         if (tg.toLowerCase() == 'span' ||
24914             tg.toLowerCase() == 'code' ||
24915             tg.toLowerCase() == 'sup' ||
24916             tg.toLowerCase() == 'sub' 
24917             ) {
24918             
24919             range = this.createRange(this.getSelection());
24920             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24921             wrappingNode.appendChild(range.extractContents());
24922             range.insertNode(wrappingNode);
24923
24924             return;
24925             
24926             
24927             
24928         }
24929         this.execCmd("formatblock",   tg);
24930         
24931     },
24932     
24933     insertText : function(txt)
24934     {
24935         
24936         
24937         var range = this.createRange();
24938         range.deleteContents();
24939                //alert(Sender.getAttribute('label'));
24940                
24941         range.insertNode(this.doc.createTextNode(txt));
24942     } ,
24943     
24944      
24945
24946     /**
24947      * Executes a Midas editor command on the editor document and performs necessary focus and
24948      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24949      * @param {String} cmd The Midas command
24950      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24951      */
24952     relayCmd : function(cmd, value){
24953         this.win.focus();
24954         this.execCmd(cmd, value);
24955         this.owner.fireEvent('editorevent', this);
24956         //this.updateToolbar();
24957         this.owner.deferFocus();
24958     },
24959
24960     /**
24961      * Executes a Midas editor command directly on the editor document.
24962      * For visual commands, you should use {@link #relayCmd} instead.
24963      * <b>This should only be called after the editor is initialized.</b>
24964      * @param {String} cmd The Midas command
24965      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24966      */
24967     execCmd : function(cmd, value){
24968         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24969         this.syncValue();
24970     },
24971  
24972  
24973    
24974     /**
24975      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24976      * to insert tRoo.
24977      * @param {String} text | dom node.. 
24978      */
24979     insertAtCursor : function(text)
24980     {
24981         
24982         if(!this.activated){
24983             return;
24984         }
24985         /*
24986         if(Roo.isIE){
24987             this.win.focus();
24988             var r = this.doc.selection.createRange();
24989             if(r){
24990                 r.collapse(true);
24991                 r.pasteHTML(text);
24992                 this.syncValue();
24993                 this.deferFocus();
24994             
24995             }
24996             return;
24997         }
24998         */
24999         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25000             this.win.focus();
25001             
25002             
25003             // from jquery ui (MIT licenced)
25004             var range, node;
25005             var win = this.win;
25006             
25007             if (win.getSelection && win.getSelection().getRangeAt) {
25008                 range = win.getSelection().getRangeAt(0);
25009                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25010                 range.insertNode(node);
25011             } else if (win.document.selection && win.document.selection.createRange) {
25012                 // no firefox support
25013                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25014                 win.document.selection.createRange().pasteHTML(txt);
25015             } else {
25016                 // no firefox support
25017                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25018                 this.execCmd('InsertHTML', txt);
25019             } 
25020             
25021             this.syncValue();
25022             
25023             this.deferFocus();
25024         }
25025     },
25026  // private
25027     mozKeyPress : function(e){
25028         if(e.ctrlKey){
25029             var c = e.getCharCode(), cmd;
25030           
25031             if(c > 0){
25032                 c = String.fromCharCode(c).toLowerCase();
25033                 switch(c){
25034                     case 'b':
25035                         cmd = 'bold';
25036                         break;
25037                     case 'i':
25038                         cmd = 'italic';
25039                         break;
25040                     
25041                     case 'u':
25042                         cmd = 'underline';
25043                         break;
25044                     
25045                     case 'v':
25046                         this.cleanUpPaste.defer(100, this);
25047                         return;
25048                         
25049                 }
25050                 if(cmd){
25051                     this.win.focus();
25052                     this.execCmd(cmd);
25053                     this.deferFocus();
25054                     e.preventDefault();
25055                 }
25056                 
25057             }
25058         }
25059     },
25060
25061     // private
25062     fixKeys : function(){ // load time branching for fastest keydown performance
25063         if(Roo.isIE){
25064             return function(e){
25065                 var k = e.getKey(), r;
25066                 if(k == e.TAB){
25067                     e.stopEvent();
25068                     r = this.doc.selection.createRange();
25069                     if(r){
25070                         r.collapse(true);
25071                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25072                         this.deferFocus();
25073                     }
25074                     return;
25075                 }
25076                 
25077                 if(k == e.ENTER){
25078                     r = this.doc.selection.createRange();
25079                     if(r){
25080                         var target = r.parentElement();
25081                         if(!target || target.tagName.toLowerCase() != 'li'){
25082                             e.stopEvent();
25083                             r.pasteHTML('<br />');
25084                             r.collapse(false);
25085                             r.select();
25086                         }
25087                     }
25088                 }
25089                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25090                     this.cleanUpPaste.defer(100, this);
25091                     return;
25092                 }
25093                 
25094                 
25095             };
25096         }else if(Roo.isOpera){
25097             return function(e){
25098                 var k = e.getKey();
25099                 if(k == e.TAB){
25100                     e.stopEvent();
25101                     this.win.focus();
25102                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25103                     this.deferFocus();
25104                 }
25105                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25106                     this.cleanUpPaste.defer(100, this);
25107                     return;
25108                 }
25109                 
25110             };
25111         }else if(Roo.isSafari){
25112             return function(e){
25113                 var k = e.getKey();
25114                 
25115                 if(k == e.TAB){
25116                     e.stopEvent();
25117                     this.execCmd('InsertText','\t');
25118                     this.deferFocus();
25119                     return;
25120                 }
25121                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25122                     this.cleanUpPaste.defer(100, this);
25123                     return;
25124                 }
25125                 
25126              };
25127         }
25128     }(),
25129     
25130     getAllAncestors: function()
25131     {
25132         var p = this.getSelectedNode();
25133         var a = [];
25134         if (!p) {
25135             a.push(p); // push blank onto stack..
25136             p = this.getParentElement();
25137         }
25138         
25139         
25140         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25141             a.push(p);
25142             p = p.parentNode;
25143         }
25144         a.push(this.doc.body);
25145         return a;
25146     },
25147     lastSel : false,
25148     lastSelNode : false,
25149     
25150     
25151     getSelection : function() 
25152     {
25153         this.assignDocWin();
25154         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25155     },
25156     
25157     getSelectedNode: function() 
25158     {
25159         // this may only work on Gecko!!!
25160         
25161         // should we cache this!!!!
25162         
25163         
25164         
25165          
25166         var range = this.createRange(this.getSelection()).cloneRange();
25167         
25168         if (Roo.isIE) {
25169             var parent = range.parentElement();
25170             while (true) {
25171                 var testRange = range.duplicate();
25172                 testRange.moveToElementText(parent);
25173                 if (testRange.inRange(range)) {
25174                     break;
25175                 }
25176                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25177                     break;
25178                 }
25179                 parent = parent.parentElement;
25180             }
25181             return parent;
25182         }
25183         
25184         // is ancestor a text element.
25185         var ac =  range.commonAncestorContainer;
25186         if (ac.nodeType == 3) {
25187             ac = ac.parentNode;
25188         }
25189         
25190         var ar = ac.childNodes;
25191          
25192         var nodes = [];
25193         var other_nodes = [];
25194         var has_other_nodes = false;
25195         for (var i=0;i<ar.length;i++) {
25196             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25197                 continue;
25198             }
25199             // fullly contained node.
25200             
25201             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25202                 nodes.push(ar[i]);
25203                 continue;
25204             }
25205             
25206             // probably selected..
25207             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25208                 other_nodes.push(ar[i]);
25209                 continue;
25210             }
25211             // outer..
25212             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25213                 continue;
25214             }
25215             
25216             
25217             has_other_nodes = true;
25218         }
25219         if (!nodes.length && other_nodes.length) {
25220             nodes= other_nodes;
25221         }
25222         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25223             return false;
25224         }
25225         
25226         return nodes[0];
25227     },
25228     createRange: function(sel)
25229     {
25230         // this has strange effects when using with 
25231         // top toolbar - not sure if it's a great idea.
25232         //this.editor.contentWindow.focus();
25233         if (typeof sel != "undefined") {
25234             try {
25235                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25236             } catch(e) {
25237                 return this.doc.createRange();
25238             }
25239         } else {
25240             return this.doc.createRange();
25241         }
25242     },
25243     getParentElement: function()
25244     {
25245         
25246         this.assignDocWin();
25247         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25248         
25249         var range = this.createRange(sel);
25250          
25251         try {
25252             var p = range.commonAncestorContainer;
25253             while (p.nodeType == 3) { // text node
25254                 p = p.parentNode;
25255             }
25256             return p;
25257         } catch (e) {
25258             return null;
25259         }
25260     
25261     },
25262     /***
25263      *
25264      * Range intersection.. the hard stuff...
25265      *  '-1' = before
25266      *  '0' = hits..
25267      *  '1' = after.
25268      *         [ -- selected range --- ]
25269      *   [fail]                        [fail]
25270      *
25271      *    basically..
25272      *      if end is before start or  hits it. fail.
25273      *      if start is after end or hits it fail.
25274      *
25275      *   if either hits (but other is outside. - then it's not 
25276      *   
25277      *    
25278      **/
25279     
25280     
25281     // @see http://www.thismuchiknow.co.uk/?p=64.
25282     rangeIntersectsNode : function(range, node)
25283     {
25284         var nodeRange = node.ownerDocument.createRange();
25285         try {
25286             nodeRange.selectNode(node);
25287         } catch (e) {
25288             nodeRange.selectNodeContents(node);
25289         }
25290     
25291         var rangeStartRange = range.cloneRange();
25292         rangeStartRange.collapse(true);
25293     
25294         var rangeEndRange = range.cloneRange();
25295         rangeEndRange.collapse(false);
25296     
25297         var nodeStartRange = nodeRange.cloneRange();
25298         nodeStartRange.collapse(true);
25299     
25300         var nodeEndRange = nodeRange.cloneRange();
25301         nodeEndRange.collapse(false);
25302     
25303         return rangeStartRange.compareBoundaryPoints(
25304                  Range.START_TO_START, nodeEndRange) == -1 &&
25305                rangeEndRange.compareBoundaryPoints(
25306                  Range.START_TO_START, nodeStartRange) == 1;
25307         
25308          
25309     },
25310     rangeCompareNode : function(range, node)
25311     {
25312         var nodeRange = node.ownerDocument.createRange();
25313         try {
25314             nodeRange.selectNode(node);
25315         } catch (e) {
25316             nodeRange.selectNodeContents(node);
25317         }
25318         
25319         
25320         range.collapse(true);
25321     
25322         nodeRange.collapse(true);
25323      
25324         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25325         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25326          
25327         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25328         
25329         var nodeIsBefore   =  ss == 1;
25330         var nodeIsAfter    = ee == -1;
25331         
25332         if (nodeIsBefore && nodeIsAfter) {
25333             return 0; // outer
25334         }
25335         if (!nodeIsBefore && nodeIsAfter) {
25336             return 1; //right trailed.
25337         }
25338         
25339         if (nodeIsBefore && !nodeIsAfter) {
25340             return 2;  // left trailed.
25341         }
25342         // fully contined.
25343         return 3;
25344     },
25345
25346     // private? - in a new class?
25347     cleanUpPaste :  function()
25348     {
25349         // cleans up the whole document..
25350         Roo.log('cleanuppaste');
25351         
25352         this.cleanUpChildren(this.doc.body);
25353         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25354         if (clean != this.doc.body.innerHTML) {
25355             this.doc.body.innerHTML = clean;
25356         }
25357         
25358     },
25359     
25360     cleanWordChars : function(input) {// change the chars to hex code
25361         var he = Roo.HtmlEditorCore;
25362         
25363         var output = input;
25364         Roo.each(he.swapCodes, function(sw) { 
25365             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25366             
25367             output = output.replace(swapper, sw[1]);
25368         });
25369         
25370         return output;
25371     },
25372     
25373     
25374     cleanUpChildren : function (n)
25375     {
25376         if (!n.childNodes.length) {
25377             return;
25378         }
25379         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25380            this.cleanUpChild(n.childNodes[i]);
25381         }
25382     },
25383     
25384     
25385         
25386     
25387     cleanUpChild : function (node)
25388     {
25389         var ed = this;
25390         //console.log(node);
25391         if (node.nodeName == "#text") {
25392             // clean up silly Windows -- stuff?
25393             return; 
25394         }
25395         if (node.nodeName == "#comment") {
25396             node.parentNode.removeChild(node);
25397             // clean up silly Windows -- stuff?
25398             return; 
25399         }
25400         var lcname = node.tagName.toLowerCase();
25401         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25402         // whitelist of tags..
25403         
25404         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25405             // remove node.
25406             node.parentNode.removeChild(node);
25407             return;
25408             
25409         }
25410         
25411         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25412         
25413         // spans with no attributes - just remove them..
25414         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25415             remove_keep_children = true;
25416         }
25417         
25418         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25419         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25420         
25421         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25422         //    remove_keep_children = true;
25423         //}
25424         
25425         if (remove_keep_children) {
25426             this.cleanUpChildren(node);
25427             // inserts everything just before this node...
25428             while (node.childNodes.length) {
25429                 var cn = node.childNodes[0];
25430                 node.removeChild(cn);
25431                 node.parentNode.insertBefore(cn, node);
25432             }
25433             node.parentNode.removeChild(node);
25434             return;
25435         }
25436         
25437         if (!node.attributes || !node.attributes.length) {
25438             
25439           
25440             
25441             
25442             this.cleanUpChildren(node);
25443             return;
25444         }
25445         
25446         function cleanAttr(n,v)
25447         {
25448             
25449             if (v.match(/^\./) || v.match(/^\//)) {
25450                 return;
25451             }
25452             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25453                 return;
25454             }
25455             if (v.match(/^#/)) {
25456                 return;
25457             }
25458             if (v.match(/^\{/)) { // allow template editing.
25459                 return;
25460             }
25461 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25462             node.removeAttribute(n);
25463             
25464         }
25465         
25466         var cwhite = this.cwhite;
25467         var cblack = this.cblack;
25468             
25469         function cleanStyle(n,v)
25470         {
25471             if (v.match(/expression/)) { //XSS?? should we even bother..
25472                 node.removeAttribute(n);
25473                 return;
25474             }
25475             
25476             var parts = v.split(/;/);
25477             var clean = [];
25478             
25479             Roo.each(parts, function(p) {
25480                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25481                 if (!p.length) {
25482                     return true;
25483                 }
25484                 var l = p.split(':').shift().replace(/\s+/g,'');
25485                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25486                 
25487                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25488 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25489                     //node.removeAttribute(n);
25490                     return true;
25491                 }
25492                 //Roo.log()
25493                 // only allow 'c whitelisted system attributes'
25494                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25495 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25496                     //node.removeAttribute(n);
25497                     return true;
25498                 }
25499                 
25500                 
25501                  
25502                 
25503                 clean.push(p);
25504                 return true;
25505             });
25506             if (clean.length) { 
25507                 node.setAttribute(n, clean.join(';'));
25508             } else {
25509                 node.removeAttribute(n);
25510             }
25511             
25512         }
25513         
25514         
25515         for (var i = node.attributes.length-1; i > -1 ; i--) {
25516             var a = node.attributes[i];
25517             //console.log(a);
25518             
25519             if (a.name.toLowerCase().substr(0,2)=='on')  {
25520                 node.removeAttribute(a.name);
25521                 continue;
25522             }
25523             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25524                 node.removeAttribute(a.name);
25525                 continue;
25526             }
25527             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25528                 cleanAttr(a.name,a.value); // fixme..
25529                 continue;
25530             }
25531             if (a.name == 'style') {
25532                 cleanStyle(a.name,a.value);
25533                 continue;
25534             }
25535             /// clean up MS crap..
25536             // tecnically this should be a list of valid class'es..
25537             
25538             
25539             if (a.name == 'class') {
25540                 if (a.value.match(/^Mso/)) {
25541                     node.removeAttribute('class');
25542                 }
25543                 
25544                 if (a.value.match(/^body$/)) {
25545                     node.removeAttribute('class');
25546                 }
25547                 continue;
25548             }
25549             
25550             // style cleanup!?
25551             // class cleanup?
25552             
25553         }
25554         
25555         
25556         this.cleanUpChildren(node);
25557         
25558         
25559     },
25560     
25561     /**
25562      * Clean up MS wordisms...
25563      */
25564     cleanWord : function(node)
25565     {
25566         if (!node) {
25567             this.cleanWord(this.doc.body);
25568             return;
25569         }
25570         
25571         if(
25572                 node.nodeName == 'SPAN' &&
25573                 !node.hasAttributes() &&
25574                 node.childNodes.length == 1 &&
25575                 node.firstChild.nodeName == "#text"  
25576         ) {
25577             var textNode = node.firstChild;
25578             node.removeChild(textNode);
25579             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25580                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25581             }
25582             node.parentNode.insertBefore(textNode, node);
25583             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25584                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25585             }
25586             node.parentNode.removeChild(node);
25587         }
25588         
25589         if (node.nodeName == "#text") {
25590             // clean up silly Windows -- stuff?
25591             return; 
25592         }
25593         if (node.nodeName == "#comment") {
25594             node.parentNode.removeChild(node);
25595             // clean up silly Windows -- stuff?
25596             return; 
25597         }
25598         
25599         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25600             node.parentNode.removeChild(node);
25601             return;
25602         }
25603         //Roo.log(node.tagName);
25604         // remove - but keep children..
25605         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25606             //Roo.log('-- removed');
25607             while (node.childNodes.length) {
25608                 var cn = node.childNodes[0];
25609                 node.removeChild(cn);
25610                 node.parentNode.insertBefore(cn, node);
25611                 // move node to parent - and clean it..
25612                 this.cleanWord(cn);
25613             }
25614             node.parentNode.removeChild(node);
25615             /// no need to iterate chidlren = it's got none..
25616             //this.iterateChildren(node, this.cleanWord);
25617             return;
25618         }
25619         // clean styles
25620         if (node.className.length) {
25621             
25622             var cn = node.className.split(/\W+/);
25623             var cna = [];
25624             Roo.each(cn, function(cls) {
25625                 if (cls.match(/Mso[a-zA-Z]+/)) {
25626                     return;
25627                 }
25628                 cna.push(cls);
25629             });
25630             node.className = cna.length ? cna.join(' ') : '';
25631             if (!cna.length) {
25632                 node.removeAttribute("class");
25633             }
25634         }
25635         
25636         if (node.hasAttribute("lang")) {
25637             node.removeAttribute("lang");
25638         }
25639         
25640         if (node.hasAttribute("style")) {
25641             
25642             var styles = node.getAttribute("style").split(";");
25643             var nstyle = [];
25644             Roo.each(styles, function(s) {
25645                 if (!s.match(/:/)) {
25646                     return;
25647                 }
25648                 var kv = s.split(":");
25649                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25650                     return;
25651                 }
25652                 // what ever is left... we allow.
25653                 nstyle.push(s);
25654             });
25655             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25656             if (!nstyle.length) {
25657                 node.removeAttribute('style');
25658             }
25659         }
25660         this.iterateChildren(node, this.cleanWord);
25661         
25662         
25663         
25664     },
25665     /**
25666      * iterateChildren of a Node, calling fn each time, using this as the scole..
25667      * @param {DomNode} node node to iterate children of.
25668      * @param {Function} fn method of this class to call on each item.
25669      */
25670     iterateChildren : function(node, fn)
25671     {
25672         if (!node.childNodes.length) {
25673                 return;
25674         }
25675         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25676            fn.call(this, node.childNodes[i])
25677         }
25678     },
25679     
25680     
25681     /**
25682      * cleanTableWidths.
25683      *
25684      * Quite often pasting from word etc.. results in tables with column and widths.
25685      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25686      *
25687      */
25688     cleanTableWidths : function(node)
25689     {
25690          
25691          
25692         if (!node) {
25693             this.cleanTableWidths(this.doc.body);
25694             return;
25695         }
25696         
25697         // ignore list...
25698         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25699             return; 
25700         }
25701         Roo.log(node.tagName);
25702         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25703             this.iterateChildren(node, this.cleanTableWidths);
25704             return;
25705         }
25706         if (node.hasAttribute('width')) {
25707             node.removeAttribute('width');
25708         }
25709         
25710          
25711         if (node.hasAttribute("style")) {
25712             // pretty basic...
25713             
25714             var styles = node.getAttribute("style").split(";");
25715             var nstyle = [];
25716             Roo.each(styles, function(s) {
25717                 if (!s.match(/:/)) {
25718                     return;
25719                 }
25720                 var kv = s.split(":");
25721                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25722                     return;
25723                 }
25724                 // what ever is left... we allow.
25725                 nstyle.push(s);
25726             });
25727             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25728             if (!nstyle.length) {
25729                 node.removeAttribute('style');
25730             }
25731         }
25732         
25733         this.iterateChildren(node, this.cleanTableWidths);
25734         
25735         
25736     },
25737     
25738     
25739     
25740     
25741     domToHTML : function(currentElement, depth, nopadtext) {
25742         
25743         depth = depth || 0;
25744         nopadtext = nopadtext || false;
25745     
25746         if (!currentElement) {
25747             return this.domToHTML(this.doc.body);
25748         }
25749         
25750         //Roo.log(currentElement);
25751         var j;
25752         var allText = false;
25753         var nodeName = currentElement.nodeName;
25754         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25755         
25756         if  (nodeName == '#text') {
25757             
25758             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25759         }
25760         
25761         
25762         var ret = '';
25763         if (nodeName != 'BODY') {
25764              
25765             var i = 0;
25766             // Prints the node tagName, such as <A>, <IMG>, etc
25767             if (tagName) {
25768                 var attr = [];
25769                 for(i = 0; i < currentElement.attributes.length;i++) {
25770                     // quoting?
25771                     var aname = currentElement.attributes.item(i).name;
25772                     if (!currentElement.attributes.item(i).value.length) {
25773                         continue;
25774                     }
25775                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25776                 }
25777                 
25778                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25779             } 
25780             else {
25781                 
25782                 // eack
25783             }
25784         } else {
25785             tagName = false;
25786         }
25787         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25788             return ret;
25789         }
25790         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25791             nopadtext = true;
25792         }
25793         
25794         
25795         // Traverse the tree
25796         i = 0;
25797         var currentElementChild = currentElement.childNodes.item(i);
25798         var allText = true;
25799         var innerHTML  = '';
25800         lastnode = '';
25801         while (currentElementChild) {
25802             // Formatting code (indent the tree so it looks nice on the screen)
25803             var nopad = nopadtext;
25804             if (lastnode == 'SPAN') {
25805                 nopad  = true;
25806             }
25807             // text
25808             if  (currentElementChild.nodeName == '#text') {
25809                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25810                 toadd = nopadtext ? toadd : toadd.trim();
25811                 if (!nopad && toadd.length > 80) {
25812                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25813                 }
25814                 innerHTML  += toadd;
25815                 
25816                 i++;
25817                 currentElementChild = currentElement.childNodes.item(i);
25818                 lastNode = '';
25819                 continue;
25820             }
25821             allText = false;
25822             
25823             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25824                 
25825             // Recursively traverse the tree structure of the child node
25826             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25827             lastnode = currentElementChild.nodeName;
25828             i++;
25829             currentElementChild=currentElement.childNodes.item(i);
25830         }
25831         
25832         ret += innerHTML;
25833         
25834         if (!allText) {
25835                 // The remaining code is mostly for formatting the tree
25836             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25837         }
25838         
25839         
25840         if (tagName) {
25841             ret+= "</"+tagName+">";
25842         }
25843         return ret;
25844         
25845     },
25846         
25847     applyBlacklists : function()
25848     {
25849         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25850         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25851         
25852         this.white = [];
25853         this.black = [];
25854         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25855             if (b.indexOf(tag) > -1) {
25856                 return;
25857             }
25858             this.white.push(tag);
25859             
25860         }, this);
25861         
25862         Roo.each(w, function(tag) {
25863             if (b.indexOf(tag) > -1) {
25864                 return;
25865             }
25866             if (this.white.indexOf(tag) > -1) {
25867                 return;
25868             }
25869             this.white.push(tag);
25870             
25871         }, this);
25872         
25873         
25874         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25875             if (w.indexOf(tag) > -1) {
25876                 return;
25877             }
25878             this.black.push(tag);
25879             
25880         }, this);
25881         
25882         Roo.each(b, function(tag) {
25883             if (w.indexOf(tag) > -1) {
25884                 return;
25885             }
25886             if (this.black.indexOf(tag) > -1) {
25887                 return;
25888             }
25889             this.black.push(tag);
25890             
25891         }, this);
25892         
25893         
25894         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25895         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25896         
25897         this.cwhite = [];
25898         this.cblack = [];
25899         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25900             if (b.indexOf(tag) > -1) {
25901                 return;
25902             }
25903             this.cwhite.push(tag);
25904             
25905         }, this);
25906         
25907         Roo.each(w, function(tag) {
25908             if (b.indexOf(tag) > -1) {
25909                 return;
25910             }
25911             if (this.cwhite.indexOf(tag) > -1) {
25912                 return;
25913             }
25914             this.cwhite.push(tag);
25915             
25916         }, this);
25917         
25918         
25919         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25920             if (w.indexOf(tag) > -1) {
25921                 return;
25922             }
25923             this.cblack.push(tag);
25924             
25925         }, this);
25926         
25927         Roo.each(b, function(tag) {
25928             if (w.indexOf(tag) > -1) {
25929                 return;
25930             }
25931             if (this.cblack.indexOf(tag) > -1) {
25932                 return;
25933             }
25934             this.cblack.push(tag);
25935             
25936         }, this);
25937     },
25938     
25939     setStylesheets : function(stylesheets)
25940     {
25941         if(typeof(stylesheets) == 'string'){
25942             Roo.get(this.iframe.contentDocument.head).createChild({
25943                 tag : 'link',
25944                 rel : 'stylesheet',
25945                 type : 'text/css',
25946                 href : stylesheets
25947             });
25948             
25949             return;
25950         }
25951         var _this = this;
25952      
25953         Roo.each(stylesheets, function(s) {
25954             if(!s.length){
25955                 return;
25956             }
25957             
25958             Roo.get(_this.iframe.contentDocument.head).createChild({
25959                 tag : 'link',
25960                 rel : 'stylesheet',
25961                 type : 'text/css',
25962                 href : s
25963             });
25964         });
25965
25966         
25967     },
25968     
25969     removeStylesheets : function()
25970     {
25971         var _this = this;
25972         
25973         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25974             s.remove();
25975         });
25976     },
25977     
25978     setStyle : function(style)
25979     {
25980         Roo.get(this.iframe.contentDocument.head).createChild({
25981             tag : 'style',
25982             type : 'text/css',
25983             html : style
25984         });
25985
25986         return;
25987     }
25988     
25989     // hide stuff that is not compatible
25990     /**
25991      * @event blur
25992      * @hide
25993      */
25994     /**
25995      * @event change
25996      * @hide
25997      */
25998     /**
25999      * @event focus
26000      * @hide
26001      */
26002     /**
26003      * @event specialkey
26004      * @hide
26005      */
26006     /**
26007      * @cfg {String} fieldClass @hide
26008      */
26009     /**
26010      * @cfg {String} focusClass @hide
26011      */
26012     /**
26013      * @cfg {String} autoCreate @hide
26014      */
26015     /**
26016      * @cfg {String} inputType @hide
26017      */
26018     /**
26019      * @cfg {String} invalidClass @hide
26020      */
26021     /**
26022      * @cfg {String} invalidText @hide
26023      */
26024     /**
26025      * @cfg {String} msgFx @hide
26026      */
26027     /**
26028      * @cfg {String} validateOnBlur @hide
26029      */
26030 });
26031
26032 Roo.HtmlEditorCore.white = [
26033         'area', 'br', 'img', 'input', 'hr', 'wbr',
26034         
26035        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26036        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26037        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26038        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26039        'table',   'ul',         'xmp', 
26040        
26041        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26042       'thead',   'tr', 
26043      
26044       'dir', 'menu', 'ol', 'ul', 'dl',
26045        
26046       'embed',  'object'
26047 ];
26048
26049
26050 Roo.HtmlEditorCore.black = [
26051     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26052         'applet', // 
26053         'base',   'basefont', 'bgsound', 'blink',  'body', 
26054         'frame',  'frameset', 'head',    'html',   'ilayer', 
26055         'iframe', 'layer',  'link',     'meta',    'object',   
26056         'script', 'style' ,'title',  'xml' // clean later..
26057 ];
26058 Roo.HtmlEditorCore.clean = [
26059     'script', 'style', 'title', 'xml'
26060 ];
26061 Roo.HtmlEditorCore.remove = [
26062     'font'
26063 ];
26064 // attributes..
26065
26066 Roo.HtmlEditorCore.ablack = [
26067     'on'
26068 ];
26069     
26070 Roo.HtmlEditorCore.aclean = [ 
26071     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26072 ];
26073
26074 // protocols..
26075 Roo.HtmlEditorCore.pwhite= [
26076         'http',  'https',  'mailto'
26077 ];
26078
26079 // white listed style attributes.
26080 Roo.HtmlEditorCore.cwhite= [
26081       //  'text-align', /// default is to allow most things..
26082       
26083          
26084 //        'font-size'//??
26085 ];
26086
26087 // black listed style attributes.
26088 Roo.HtmlEditorCore.cblack= [
26089       //  'font-size' -- this can be set by the project 
26090 ];
26091
26092
26093 Roo.HtmlEditorCore.swapCodes   =[ 
26094     [    8211, "&#8211;" ], 
26095     [    8212, "&#8212;" ], 
26096     [    8216,  "'" ],  
26097     [    8217, "'" ],  
26098     [    8220, '"' ],  
26099     [    8221, '"' ],  
26100     [    8226, "*" ],  
26101     [    8230, "..." ]
26102 ]; 
26103
26104     /*
26105  * - LGPL
26106  *
26107  * HtmlEditor
26108  * 
26109  */
26110
26111 /**
26112  * @class Roo.bootstrap.HtmlEditor
26113  * @extends Roo.bootstrap.TextArea
26114  * Bootstrap HtmlEditor class
26115
26116  * @constructor
26117  * Create a new HtmlEditor
26118  * @param {Object} config The config object
26119  */
26120
26121 Roo.bootstrap.HtmlEditor = function(config){
26122     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26123     if (!this.toolbars) {
26124         this.toolbars = [];
26125     }
26126     
26127     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26128     this.addEvents({
26129             /**
26130              * @event initialize
26131              * Fires when the editor is fully initialized (including the iframe)
26132              * @param {HtmlEditor} this
26133              */
26134             initialize: true,
26135             /**
26136              * @event activate
26137              * Fires when the editor is first receives the focus. Any insertion must wait
26138              * until after this event.
26139              * @param {HtmlEditor} this
26140              */
26141             activate: true,
26142              /**
26143              * @event beforesync
26144              * Fires before the textarea is updated with content from the editor iframe. Return false
26145              * to cancel the sync.
26146              * @param {HtmlEditor} this
26147              * @param {String} html
26148              */
26149             beforesync: true,
26150              /**
26151              * @event beforepush
26152              * Fires before the iframe editor is updated with content from the textarea. Return false
26153              * to cancel the push.
26154              * @param {HtmlEditor} this
26155              * @param {String} html
26156              */
26157             beforepush: true,
26158              /**
26159              * @event sync
26160              * Fires when the textarea is updated with content from the editor iframe.
26161              * @param {HtmlEditor} this
26162              * @param {String} html
26163              */
26164             sync: true,
26165              /**
26166              * @event push
26167              * Fires when the iframe editor is updated with content from the textarea.
26168              * @param {HtmlEditor} this
26169              * @param {String} html
26170              */
26171             push: true,
26172              /**
26173              * @event editmodechange
26174              * Fires when the editor switches edit modes
26175              * @param {HtmlEditor} this
26176              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26177              */
26178             editmodechange: true,
26179             /**
26180              * @event editorevent
26181              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26182              * @param {HtmlEditor} this
26183              */
26184             editorevent: true,
26185             /**
26186              * @event firstfocus
26187              * Fires when on first focus - needed by toolbars..
26188              * @param {HtmlEditor} this
26189              */
26190             firstfocus: true,
26191             /**
26192              * @event autosave
26193              * Auto save the htmlEditor value as a file into Events
26194              * @param {HtmlEditor} this
26195              */
26196             autosave: true,
26197             /**
26198              * @event savedpreview
26199              * preview the saved version of htmlEditor
26200              * @param {HtmlEditor} this
26201              */
26202             savedpreview: true
26203         });
26204 };
26205
26206
26207 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26208     
26209     
26210       /**
26211      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26212      */
26213     toolbars : false,
26214     
26215      /**
26216     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26217     */
26218     btns : [],
26219    
26220      /**
26221      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26222      *                        Roo.resizable.
26223      */
26224     resizable : false,
26225      /**
26226      * @cfg {Number} height (in pixels)
26227      */   
26228     height: 300,
26229    /**
26230      * @cfg {Number} width (in pixels)
26231      */   
26232     width: false,
26233     
26234     /**
26235      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26236      * 
26237      */
26238     stylesheets: false,
26239     
26240     // id of frame..
26241     frameId: false,
26242     
26243     // private properties
26244     validationEvent : false,
26245     deferHeight: true,
26246     initialized : false,
26247     activated : false,
26248     
26249     onFocus : Roo.emptyFn,
26250     iframePad:3,
26251     hideMode:'offsets',
26252     
26253     tbContainer : false,
26254     
26255     bodyCls : '',
26256     
26257     toolbarContainer :function() {
26258         return this.wrap.select('.x-html-editor-tb',true).first();
26259     },
26260
26261     /**
26262      * Protected method that will not generally be called directly. It
26263      * is called when the editor creates its toolbar. Override this method if you need to
26264      * add custom toolbar buttons.
26265      * @param {HtmlEditor} editor
26266      */
26267     createToolbar : function(){
26268         Roo.log('renewing');
26269         Roo.log("create toolbars");
26270         
26271         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26272         this.toolbars[0].render(this.toolbarContainer());
26273         
26274         return;
26275         
26276 //        if (!editor.toolbars || !editor.toolbars.length) {
26277 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26278 //        }
26279 //        
26280 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26281 //            editor.toolbars[i] = Roo.factory(
26282 //                    typeof(editor.toolbars[i]) == 'string' ?
26283 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26284 //                Roo.bootstrap.HtmlEditor);
26285 //            editor.toolbars[i].init(editor);
26286 //        }
26287     },
26288
26289      
26290     // private
26291     onRender : function(ct, position)
26292     {
26293        // Roo.log("Call onRender: " + this.xtype);
26294         var _t = this;
26295         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26296       
26297         this.wrap = this.inputEl().wrap({
26298             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26299         });
26300         
26301         this.editorcore.onRender(ct, position);
26302          
26303         if (this.resizable) {
26304             this.resizeEl = new Roo.Resizable(this.wrap, {
26305                 pinned : true,
26306                 wrap: true,
26307                 dynamic : true,
26308                 minHeight : this.height,
26309                 height: this.height,
26310                 handles : this.resizable,
26311                 width: this.width,
26312                 listeners : {
26313                     resize : function(r, w, h) {
26314                         _t.onResize(w,h); // -something
26315                     }
26316                 }
26317             });
26318             
26319         }
26320         this.createToolbar(this);
26321        
26322         
26323         if(!this.width && this.resizable){
26324             this.setSize(this.wrap.getSize());
26325         }
26326         if (this.resizeEl) {
26327             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26328             // should trigger onReize..
26329         }
26330         
26331     },
26332
26333     // private
26334     onResize : function(w, h)
26335     {
26336         Roo.log('resize: ' +w + ',' + h );
26337         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26338         var ew = false;
26339         var eh = false;
26340         
26341         if(this.inputEl() ){
26342             if(typeof w == 'number'){
26343                 var aw = w - this.wrap.getFrameWidth('lr');
26344                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26345                 ew = aw;
26346             }
26347             if(typeof h == 'number'){
26348                  var tbh = -11;  // fixme it needs to tool bar size!
26349                 for (var i =0; i < this.toolbars.length;i++) {
26350                     // fixme - ask toolbars for heights?
26351                     tbh += this.toolbars[i].el.getHeight();
26352                     //if (this.toolbars[i].footer) {
26353                     //    tbh += this.toolbars[i].footer.el.getHeight();
26354                     //}
26355                 }
26356               
26357                 
26358                 
26359                 
26360                 
26361                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26362                 ah -= 5; // knock a few pixes off for look..
26363                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26364                 var eh = ah;
26365             }
26366         }
26367         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26368         this.editorcore.onResize(ew,eh);
26369         
26370     },
26371
26372     /**
26373      * Toggles the editor between standard and source edit mode.
26374      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26375      */
26376     toggleSourceEdit : function(sourceEditMode)
26377     {
26378         this.editorcore.toggleSourceEdit(sourceEditMode);
26379         
26380         if(this.editorcore.sourceEditMode){
26381             Roo.log('editor - showing textarea');
26382             
26383 //            Roo.log('in');
26384 //            Roo.log(this.syncValue());
26385             this.syncValue();
26386             this.inputEl().removeClass(['hide', 'x-hidden']);
26387             this.inputEl().dom.removeAttribute('tabIndex');
26388             this.inputEl().focus();
26389         }else{
26390             Roo.log('editor - hiding textarea');
26391 //            Roo.log('out')
26392 //            Roo.log(this.pushValue()); 
26393             this.pushValue();
26394             
26395             this.inputEl().addClass(['hide', 'x-hidden']);
26396             this.inputEl().dom.setAttribute('tabIndex', -1);
26397             //this.deferFocus();
26398         }
26399          
26400         if(this.resizable){
26401             this.setSize(this.wrap.getSize());
26402         }
26403         
26404         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26405     },
26406  
26407     // private (for BoxComponent)
26408     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26409
26410     // private (for BoxComponent)
26411     getResizeEl : function(){
26412         return this.wrap;
26413     },
26414
26415     // private (for BoxComponent)
26416     getPositionEl : function(){
26417         return this.wrap;
26418     },
26419
26420     // private
26421     initEvents : function(){
26422         this.originalValue = this.getValue();
26423     },
26424
26425 //    /**
26426 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26427 //     * @method
26428 //     */
26429 //    markInvalid : Roo.emptyFn,
26430 //    /**
26431 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26432 //     * @method
26433 //     */
26434 //    clearInvalid : Roo.emptyFn,
26435
26436     setValue : function(v){
26437         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26438         this.editorcore.pushValue();
26439     },
26440
26441      
26442     // private
26443     deferFocus : function(){
26444         this.focus.defer(10, this);
26445     },
26446
26447     // doc'ed in Field
26448     focus : function(){
26449         this.editorcore.focus();
26450         
26451     },
26452       
26453
26454     // private
26455     onDestroy : function(){
26456         
26457         
26458         
26459         if(this.rendered){
26460             
26461             for (var i =0; i < this.toolbars.length;i++) {
26462                 // fixme - ask toolbars for heights?
26463                 this.toolbars[i].onDestroy();
26464             }
26465             
26466             this.wrap.dom.innerHTML = '';
26467             this.wrap.remove();
26468         }
26469     },
26470
26471     // private
26472     onFirstFocus : function(){
26473         //Roo.log("onFirstFocus");
26474         this.editorcore.onFirstFocus();
26475          for (var i =0; i < this.toolbars.length;i++) {
26476             this.toolbars[i].onFirstFocus();
26477         }
26478         
26479     },
26480     
26481     // private
26482     syncValue : function()
26483     {   
26484         this.editorcore.syncValue();
26485     },
26486     
26487     pushValue : function()
26488     {   
26489         this.editorcore.pushValue();
26490     }
26491      
26492     
26493     // hide stuff that is not compatible
26494     /**
26495      * @event blur
26496      * @hide
26497      */
26498     /**
26499      * @event change
26500      * @hide
26501      */
26502     /**
26503      * @event focus
26504      * @hide
26505      */
26506     /**
26507      * @event specialkey
26508      * @hide
26509      */
26510     /**
26511      * @cfg {String} fieldClass @hide
26512      */
26513     /**
26514      * @cfg {String} focusClass @hide
26515      */
26516     /**
26517      * @cfg {String} autoCreate @hide
26518      */
26519     /**
26520      * @cfg {String} inputType @hide
26521      */
26522      
26523     /**
26524      * @cfg {String} invalidText @hide
26525      */
26526     /**
26527      * @cfg {String} msgFx @hide
26528      */
26529     /**
26530      * @cfg {String} validateOnBlur @hide
26531      */
26532 });
26533  
26534     
26535    
26536    
26537    
26538       
26539 Roo.namespace('Roo.bootstrap.htmleditor');
26540 /**
26541  * @class Roo.bootstrap.HtmlEditorToolbar1
26542  * Basic Toolbar
26543  * 
26544  * @example
26545  * Usage:
26546  *
26547  new Roo.bootstrap.HtmlEditor({
26548     ....
26549     toolbars : [
26550         new Roo.bootstrap.HtmlEditorToolbar1({
26551             disable : { fonts: 1 , format: 1, ..., ... , ...],
26552             btns : [ .... ]
26553         })
26554     }
26555      
26556  * 
26557  * @cfg {Object} disable List of elements to disable..
26558  * @cfg {Array} btns List of additional buttons.
26559  * 
26560  * 
26561  * NEEDS Extra CSS? 
26562  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26563  */
26564  
26565 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26566 {
26567     
26568     Roo.apply(this, config);
26569     
26570     // default disabled, based on 'good practice'..
26571     this.disable = this.disable || {};
26572     Roo.applyIf(this.disable, {
26573         fontSize : true,
26574         colors : true,
26575         specialElements : true
26576     });
26577     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26578     
26579     this.editor = config.editor;
26580     this.editorcore = config.editor.editorcore;
26581     
26582     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26583     
26584     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26585     // dont call parent... till later.
26586 }
26587 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26588      
26589     bar : true,
26590     
26591     editor : false,
26592     editorcore : false,
26593     
26594     
26595     formats : [
26596         "p" ,  
26597         "h1","h2","h3","h4","h5","h6", 
26598         "pre", "code", 
26599         "abbr", "acronym", "address", "cite", "samp", "var",
26600         'div','span'
26601     ],
26602     
26603     onRender : function(ct, position)
26604     {
26605        // Roo.log("Call onRender: " + this.xtype);
26606         
26607        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26608        Roo.log(this.el);
26609        this.el.dom.style.marginBottom = '0';
26610        var _this = this;
26611        var editorcore = this.editorcore;
26612        var editor= this.editor;
26613        
26614        var children = [];
26615        var btn = function(id,cmd , toggle, handler, html){
26616        
26617             var  event = toggle ? 'toggle' : 'click';
26618        
26619             var a = {
26620                 size : 'sm',
26621                 xtype: 'Button',
26622                 xns: Roo.bootstrap,
26623                 //glyphicon : id,
26624                 fa: id,
26625                 cmd : id || cmd,
26626                 enableToggle:toggle !== false,
26627                 html : html || '',
26628                 pressed : toggle ? false : null,
26629                 listeners : {}
26630             };
26631             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26632                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26633             };
26634             children.push(a);
26635             return a;
26636        }
26637        
26638     //    var cb_box = function...
26639         
26640         var style = {
26641                 xtype: 'Button',
26642                 size : 'sm',
26643                 xns: Roo.bootstrap,
26644                 fa : 'font',
26645                 //html : 'submit'
26646                 menu : {
26647                     xtype: 'Menu',
26648                     xns: Roo.bootstrap,
26649                     items:  []
26650                 }
26651         };
26652         Roo.each(this.formats, function(f) {
26653             style.menu.items.push({
26654                 xtype :'MenuItem',
26655                 xns: Roo.bootstrap,
26656                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26657                 tagname : f,
26658                 listeners : {
26659                     click : function()
26660                     {
26661                         editorcore.insertTag(this.tagname);
26662                         editor.focus();
26663                     }
26664                 }
26665                 
26666             });
26667         });
26668         children.push(style);   
26669         
26670         btn('bold',false,true);
26671         btn('italic',false,true);
26672         btn('align-left', 'justifyleft',true);
26673         btn('align-center', 'justifycenter',true);
26674         btn('align-right' , 'justifyright',true);
26675         btn('link', false, false, function(btn) {
26676             //Roo.log("create link?");
26677             var url = prompt(this.createLinkText, this.defaultLinkValue);
26678             if(url && url != 'http:/'+'/'){
26679                 this.editorcore.relayCmd('createlink', url);
26680             }
26681         }),
26682         btn('list','insertunorderedlist',true);
26683         btn('pencil', false,true, function(btn){
26684                 Roo.log(this);
26685                 this.toggleSourceEdit(btn.pressed);
26686         });
26687         
26688         if (this.editor.btns.length > 0) {
26689             for (var i = 0; i<this.editor.btns.length; i++) {
26690                 children.push(this.editor.btns[i]);
26691             }
26692         }
26693         
26694         /*
26695         var cog = {
26696                 xtype: 'Button',
26697                 size : 'sm',
26698                 xns: Roo.bootstrap,
26699                 glyphicon : 'cog',
26700                 //html : 'submit'
26701                 menu : {
26702                     xtype: 'Menu',
26703                     xns: Roo.bootstrap,
26704                     items:  []
26705                 }
26706         };
26707         
26708         cog.menu.items.push({
26709             xtype :'MenuItem',
26710             xns: Roo.bootstrap,
26711             html : Clean styles,
26712             tagname : f,
26713             listeners : {
26714                 click : function()
26715                 {
26716                     editorcore.insertTag(this.tagname);
26717                     editor.focus();
26718                 }
26719             }
26720             
26721         });
26722        */
26723         
26724          
26725        this.xtype = 'NavSimplebar';
26726         
26727         for(var i=0;i< children.length;i++) {
26728             
26729             this.buttons.add(this.addxtypeChild(children[i]));
26730             
26731         }
26732         
26733         editor.on('editorevent', this.updateToolbar, this);
26734     },
26735     onBtnClick : function(id)
26736     {
26737        this.editorcore.relayCmd(id);
26738        this.editorcore.focus();
26739     },
26740     
26741     /**
26742      * Protected method that will not generally be called directly. It triggers
26743      * a toolbar update by reading the markup state of the current selection in the editor.
26744      */
26745     updateToolbar: function(){
26746
26747         if(!this.editorcore.activated){
26748             this.editor.onFirstFocus(); // is this neeed?
26749             return;
26750         }
26751
26752         var btns = this.buttons; 
26753         var doc = this.editorcore.doc;
26754         btns.get('bold').setActive(doc.queryCommandState('bold'));
26755         btns.get('italic').setActive(doc.queryCommandState('italic'));
26756         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26757         
26758         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26759         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26760         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26761         
26762         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26763         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26764          /*
26765         
26766         var ans = this.editorcore.getAllAncestors();
26767         if (this.formatCombo) {
26768             
26769             
26770             var store = this.formatCombo.store;
26771             this.formatCombo.setValue("");
26772             for (var i =0; i < ans.length;i++) {
26773                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26774                     // select it..
26775                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26776                     break;
26777                 }
26778             }
26779         }
26780         
26781         
26782         
26783         // hides menus... - so this cant be on a menu...
26784         Roo.bootstrap.MenuMgr.hideAll();
26785         */
26786         Roo.bootstrap.MenuMgr.hideAll();
26787         //this.editorsyncValue();
26788     },
26789     onFirstFocus: function() {
26790         this.buttons.each(function(item){
26791            item.enable();
26792         });
26793     },
26794     toggleSourceEdit : function(sourceEditMode){
26795         
26796           
26797         if(sourceEditMode){
26798             Roo.log("disabling buttons");
26799            this.buttons.each( function(item){
26800                 if(item.cmd != 'pencil'){
26801                     item.disable();
26802                 }
26803             });
26804           
26805         }else{
26806             Roo.log("enabling buttons");
26807             if(this.editorcore.initialized){
26808                 this.buttons.each( function(item){
26809                     item.enable();
26810                 });
26811             }
26812             
26813         }
26814         Roo.log("calling toggole on editor");
26815         // tell the editor that it's been pressed..
26816         this.editor.toggleSourceEdit(sourceEditMode);
26817        
26818     }
26819 });
26820
26821
26822
26823
26824  
26825 /*
26826  * - LGPL
26827  */
26828
26829 /**
26830  * @class Roo.bootstrap.Markdown
26831  * @extends Roo.bootstrap.TextArea
26832  * Bootstrap Showdown editable area
26833  * @cfg {string} content
26834  * 
26835  * @constructor
26836  * Create a new Showdown
26837  */
26838
26839 Roo.bootstrap.Markdown = function(config){
26840     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26841    
26842 };
26843
26844 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26845     
26846     editing :false,
26847     
26848     initEvents : function()
26849     {
26850         
26851         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26852         this.markdownEl = this.el.createChild({
26853             cls : 'roo-markdown-area'
26854         });
26855         this.inputEl().addClass('d-none');
26856         if (this.getValue() == '') {
26857             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26858             
26859         } else {
26860             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26861         }
26862         this.markdownEl.on('click', this.toggleTextEdit, this);
26863         this.on('blur', this.toggleTextEdit, this);
26864         this.on('specialkey', this.resizeTextArea, this);
26865     },
26866     
26867     toggleTextEdit : function()
26868     {
26869         var sh = this.markdownEl.getHeight();
26870         this.inputEl().addClass('d-none');
26871         this.markdownEl.addClass('d-none');
26872         if (!this.editing) {
26873             // show editor?
26874             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26875             this.inputEl().removeClass('d-none');
26876             this.inputEl().focus();
26877             this.editing = true;
26878             return;
26879         }
26880         // show showdown...
26881         this.updateMarkdown();
26882         this.markdownEl.removeClass('d-none');
26883         this.editing = false;
26884         return;
26885     },
26886     updateMarkdown : function()
26887     {
26888         if (this.getValue() == '') {
26889             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26890             return;
26891         }
26892  
26893         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26894     },
26895     
26896     resizeTextArea: function () {
26897         
26898         var sh = 100;
26899         Roo.log([sh, this.getValue().split("\n").length * 30]);
26900         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26901     },
26902     setValue : function(val)
26903     {
26904         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26905         if (!this.editing) {
26906             this.updateMarkdown();
26907         }
26908         
26909     },
26910     focus : function()
26911     {
26912         if (!this.editing) {
26913             this.toggleTextEdit();
26914         }
26915         
26916     }
26917
26918
26919 });
26920 /**
26921  * @class Roo.bootstrap.Table.AbstractSelectionModel
26922  * @extends Roo.util.Observable
26923  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26924  * implemented by descendant classes.  This class should not be directly instantiated.
26925  * @constructor
26926  */
26927 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26928     this.locked = false;
26929     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26930 };
26931
26932
26933 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26934     /** @ignore Called by the grid automatically. Do not call directly. */
26935     init : function(grid){
26936         this.grid = grid;
26937         this.initEvents();
26938     },
26939
26940     /**
26941      * Locks the selections.
26942      */
26943     lock : function(){
26944         this.locked = true;
26945     },
26946
26947     /**
26948      * Unlocks the selections.
26949      */
26950     unlock : function(){
26951         this.locked = false;
26952     },
26953
26954     /**
26955      * Returns true if the selections are locked.
26956      * @return {Boolean}
26957      */
26958     isLocked : function(){
26959         return this.locked;
26960     },
26961     
26962     
26963     initEvents : function ()
26964     {
26965         
26966     }
26967 });
26968 /**
26969  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26970  * @class Roo.bootstrap.Table.RowSelectionModel
26971  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26972  * It supports multiple selections and keyboard selection/navigation. 
26973  * @constructor
26974  * @param {Object} config
26975  */
26976
26977 Roo.bootstrap.Table.RowSelectionModel = function(config){
26978     Roo.apply(this, config);
26979     this.selections = new Roo.util.MixedCollection(false, function(o){
26980         return o.id;
26981     });
26982
26983     this.last = false;
26984     this.lastActive = false;
26985
26986     this.addEvents({
26987         /**
26988              * @event selectionchange
26989              * Fires when the selection changes
26990              * @param {SelectionModel} this
26991              */
26992             "selectionchange" : true,
26993         /**
26994              * @event afterselectionchange
26995              * Fires after the selection changes (eg. by key press or clicking)
26996              * @param {SelectionModel} this
26997              */
26998             "afterselectionchange" : true,
26999         /**
27000              * @event beforerowselect
27001              * Fires when a row is selected being selected, return false to cancel.
27002              * @param {SelectionModel} this
27003              * @param {Number} rowIndex The selected index
27004              * @param {Boolean} keepExisting False if other selections will be cleared
27005              */
27006             "beforerowselect" : true,
27007         /**
27008              * @event rowselect
27009              * Fires when a row is selected.
27010              * @param {SelectionModel} this
27011              * @param {Number} rowIndex The selected index
27012              * @param {Roo.data.Record} r The record
27013              */
27014             "rowselect" : true,
27015         /**
27016              * @event rowdeselect
27017              * Fires when a row is deselected.
27018              * @param {SelectionModel} this
27019              * @param {Number} rowIndex The selected index
27020              */
27021         "rowdeselect" : true
27022     });
27023     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27024     this.locked = false;
27025  };
27026
27027 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27028     /**
27029      * @cfg {Boolean} singleSelect
27030      * True to allow selection of only one row at a time (defaults to false)
27031      */
27032     singleSelect : false,
27033
27034     // private
27035     initEvents : function()
27036     {
27037
27038         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27039         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27040         //}else{ // allow click to work like normal
27041          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27042         //}
27043         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27044         this.grid.on("rowclick", this.handleMouseDown, this);
27045         
27046         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27047             "up" : function(e){
27048                 if(!e.shiftKey){
27049                     this.selectPrevious(e.shiftKey);
27050                 }else if(this.last !== false && this.lastActive !== false){
27051                     var last = this.last;
27052                     this.selectRange(this.last,  this.lastActive-1);
27053                     this.grid.getView().focusRow(this.lastActive);
27054                     if(last !== false){
27055                         this.last = last;
27056                     }
27057                 }else{
27058                     this.selectFirstRow();
27059                 }
27060                 this.fireEvent("afterselectionchange", this);
27061             },
27062             "down" : function(e){
27063                 if(!e.shiftKey){
27064                     this.selectNext(e.shiftKey);
27065                 }else if(this.last !== false && this.lastActive !== false){
27066                     var last = this.last;
27067                     this.selectRange(this.last,  this.lastActive+1);
27068                     this.grid.getView().focusRow(this.lastActive);
27069                     if(last !== false){
27070                         this.last = last;
27071                     }
27072                 }else{
27073                     this.selectFirstRow();
27074                 }
27075                 this.fireEvent("afterselectionchange", this);
27076             },
27077             scope: this
27078         });
27079         this.grid.store.on('load', function(){
27080             this.selections.clear();
27081         },this);
27082         /*
27083         var view = this.grid.view;
27084         view.on("refresh", this.onRefresh, this);
27085         view.on("rowupdated", this.onRowUpdated, this);
27086         view.on("rowremoved", this.onRemove, this);
27087         */
27088     },
27089
27090     // private
27091     onRefresh : function()
27092     {
27093         var ds = this.grid.store, i, v = this.grid.view;
27094         var s = this.selections;
27095         s.each(function(r){
27096             if((i = ds.indexOfId(r.id)) != -1){
27097                 v.onRowSelect(i);
27098             }else{
27099                 s.remove(r);
27100             }
27101         });
27102     },
27103
27104     // private
27105     onRemove : function(v, index, r){
27106         this.selections.remove(r);
27107     },
27108
27109     // private
27110     onRowUpdated : function(v, index, r){
27111         if(this.isSelected(r)){
27112             v.onRowSelect(index);
27113         }
27114     },
27115
27116     /**
27117      * Select records.
27118      * @param {Array} records The records to select
27119      * @param {Boolean} keepExisting (optional) True to keep existing selections
27120      */
27121     selectRecords : function(records, keepExisting)
27122     {
27123         if(!keepExisting){
27124             this.clearSelections();
27125         }
27126             var ds = this.grid.store;
27127         for(var i = 0, len = records.length; i < len; i++){
27128             this.selectRow(ds.indexOf(records[i]), true);
27129         }
27130     },
27131
27132     /**
27133      * Gets the number of selected rows.
27134      * @return {Number}
27135      */
27136     getCount : function(){
27137         return this.selections.length;
27138     },
27139
27140     /**
27141      * Selects the first row in the grid.
27142      */
27143     selectFirstRow : function(){
27144         this.selectRow(0);
27145     },
27146
27147     /**
27148      * Select the last row.
27149      * @param {Boolean} keepExisting (optional) True to keep existing selections
27150      */
27151     selectLastRow : function(keepExisting){
27152         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27153         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27154     },
27155
27156     /**
27157      * Selects the row immediately following the last selected row.
27158      * @param {Boolean} keepExisting (optional) True to keep existing selections
27159      */
27160     selectNext : function(keepExisting)
27161     {
27162             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27163             this.selectRow(this.last+1, keepExisting);
27164             this.grid.getView().focusRow(this.last);
27165         }
27166     },
27167
27168     /**
27169      * Selects the row that precedes the last selected row.
27170      * @param {Boolean} keepExisting (optional) True to keep existing selections
27171      */
27172     selectPrevious : function(keepExisting){
27173         if(this.last){
27174             this.selectRow(this.last-1, keepExisting);
27175             this.grid.getView().focusRow(this.last);
27176         }
27177     },
27178
27179     /**
27180      * Returns the selected records
27181      * @return {Array} Array of selected records
27182      */
27183     getSelections : function(){
27184         return [].concat(this.selections.items);
27185     },
27186
27187     /**
27188      * Returns the first selected record.
27189      * @return {Record}
27190      */
27191     getSelected : function(){
27192         return this.selections.itemAt(0);
27193     },
27194
27195
27196     /**
27197      * Clears all selections.
27198      */
27199     clearSelections : function(fast)
27200     {
27201         if(this.locked) {
27202             return;
27203         }
27204         if(fast !== true){
27205                 var ds = this.grid.store;
27206             var s = this.selections;
27207             s.each(function(r){
27208                 this.deselectRow(ds.indexOfId(r.id));
27209             }, this);
27210             s.clear();
27211         }else{
27212             this.selections.clear();
27213         }
27214         this.last = false;
27215     },
27216
27217
27218     /**
27219      * Selects all rows.
27220      */
27221     selectAll : function(){
27222         if(this.locked) {
27223             return;
27224         }
27225         this.selections.clear();
27226         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27227             this.selectRow(i, true);
27228         }
27229     },
27230
27231     /**
27232      * Returns True if there is a selection.
27233      * @return {Boolean}
27234      */
27235     hasSelection : function(){
27236         return this.selections.length > 0;
27237     },
27238
27239     /**
27240      * Returns True if the specified row is selected.
27241      * @param {Number/Record} record The record or index of the record to check
27242      * @return {Boolean}
27243      */
27244     isSelected : function(index){
27245             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27246         return (r && this.selections.key(r.id) ? true : false);
27247     },
27248
27249     /**
27250      * Returns True if the specified record id is selected.
27251      * @param {String} id The id of record to check
27252      * @return {Boolean}
27253      */
27254     isIdSelected : function(id){
27255         return (this.selections.key(id) ? true : false);
27256     },
27257
27258
27259     // private
27260     handleMouseDBClick : function(e, t){
27261         
27262     },
27263     // private
27264     handleMouseDown : function(e, t)
27265     {
27266             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27267         if(this.isLocked() || rowIndex < 0 ){
27268             return;
27269         };
27270         if(e.shiftKey && this.last !== false){
27271             var last = this.last;
27272             this.selectRange(last, rowIndex, e.ctrlKey);
27273             this.last = last; // reset the last
27274             t.focus();
27275     
27276         }else{
27277             var isSelected = this.isSelected(rowIndex);
27278             //Roo.log("select row:" + rowIndex);
27279             if(isSelected){
27280                 this.deselectRow(rowIndex);
27281             } else {
27282                         this.selectRow(rowIndex, true);
27283             }
27284     
27285             /*
27286                 if(e.button !== 0 && isSelected){
27287                 alert('rowIndex 2: ' + rowIndex);
27288                     view.focusRow(rowIndex);
27289                 }else if(e.ctrlKey && isSelected){
27290                     this.deselectRow(rowIndex);
27291                 }else if(!isSelected){
27292                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27293                     view.focusRow(rowIndex);
27294                 }
27295             */
27296         }
27297         this.fireEvent("afterselectionchange", this);
27298     },
27299     // private
27300     handleDragableRowClick :  function(grid, rowIndex, e) 
27301     {
27302         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27303             this.selectRow(rowIndex, false);
27304             grid.view.focusRow(rowIndex);
27305              this.fireEvent("afterselectionchange", this);
27306         }
27307     },
27308     
27309     /**
27310      * Selects multiple rows.
27311      * @param {Array} rows Array of the indexes of the row to select
27312      * @param {Boolean} keepExisting (optional) True to keep existing selections
27313      */
27314     selectRows : function(rows, keepExisting){
27315         if(!keepExisting){
27316             this.clearSelections();
27317         }
27318         for(var i = 0, len = rows.length; i < len; i++){
27319             this.selectRow(rows[i], true);
27320         }
27321     },
27322
27323     /**
27324      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27325      * @param {Number} startRow The index of the first row in the range
27326      * @param {Number} endRow The index of the last row in the range
27327      * @param {Boolean} keepExisting (optional) True to retain existing selections
27328      */
27329     selectRange : function(startRow, endRow, keepExisting){
27330         if(this.locked) {
27331             return;
27332         }
27333         if(!keepExisting){
27334             this.clearSelections();
27335         }
27336         if(startRow <= endRow){
27337             for(var i = startRow; i <= endRow; i++){
27338                 this.selectRow(i, true);
27339             }
27340         }else{
27341             for(var i = startRow; i >= endRow; i--){
27342                 this.selectRow(i, true);
27343             }
27344         }
27345     },
27346
27347     /**
27348      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27349      * @param {Number} startRow The index of the first row in the range
27350      * @param {Number} endRow The index of the last row in the range
27351      */
27352     deselectRange : function(startRow, endRow, preventViewNotify){
27353         if(this.locked) {
27354             return;
27355         }
27356         for(var i = startRow; i <= endRow; i++){
27357             this.deselectRow(i, preventViewNotify);
27358         }
27359     },
27360
27361     /**
27362      * Selects a row.
27363      * @param {Number} row The index of the row to select
27364      * @param {Boolean} keepExisting (optional) True to keep existing selections
27365      */
27366     selectRow : function(index, keepExisting, preventViewNotify)
27367     {
27368             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27369             return;
27370         }
27371         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27372             if(!keepExisting || this.singleSelect){
27373                 this.clearSelections();
27374             }
27375             
27376             var r = this.grid.store.getAt(index);
27377             //console.log('selectRow - record id :' + r.id);
27378             
27379             this.selections.add(r);
27380             this.last = this.lastActive = index;
27381             if(!preventViewNotify){
27382                 var proxy = new Roo.Element(
27383                                 this.grid.getRowDom(index)
27384                 );
27385                 proxy.addClass('bg-info info');
27386             }
27387             this.fireEvent("rowselect", this, index, r);
27388             this.fireEvent("selectionchange", this);
27389         }
27390     },
27391
27392     /**
27393      * Deselects a row.
27394      * @param {Number} row The index of the row to deselect
27395      */
27396     deselectRow : function(index, preventViewNotify)
27397     {
27398         if(this.locked) {
27399             return;
27400         }
27401         if(this.last == index){
27402             this.last = false;
27403         }
27404         if(this.lastActive == index){
27405             this.lastActive = false;
27406         }
27407         
27408         var r = this.grid.store.getAt(index);
27409         if (!r) {
27410             return;
27411         }
27412         
27413         this.selections.remove(r);
27414         //.console.log('deselectRow - record id :' + r.id);
27415         if(!preventViewNotify){
27416         
27417             var proxy = new Roo.Element(
27418                 this.grid.getRowDom(index)
27419             );
27420             proxy.removeClass('bg-info info');
27421         }
27422         this.fireEvent("rowdeselect", this, index);
27423         this.fireEvent("selectionchange", this);
27424     },
27425
27426     // private
27427     restoreLast : function(){
27428         if(this._last){
27429             this.last = this._last;
27430         }
27431     },
27432
27433     // private
27434     acceptsNav : function(row, col, cm){
27435         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27436     },
27437
27438     // private
27439     onEditorKey : function(field, e){
27440         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27441         if(k == e.TAB){
27442             e.stopEvent();
27443             ed.completeEdit();
27444             if(e.shiftKey){
27445                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27446             }else{
27447                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27448             }
27449         }else if(k == e.ENTER && !e.ctrlKey){
27450             e.stopEvent();
27451             ed.completeEdit();
27452             if(e.shiftKey){
27453                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27454             }else{
27455                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27456             }
27457         }else if(k == e.ESC){
27458             ed.cancelEdit();
27459         }
27460         if(newCell){
27461             g.startEditing(newCell[0], newCell[1]);
27462         }
27463     }
27464 });
27465 /*
27466  * Based on:
27467  * Ext JS Library 1.1.1
27468  * Copyright(c) 2006-2007, Ext JS, LLC.
27469  *
27470  * Originally Released Under LGPL - original licence link has changed is not relivant.
27471  *
27472  * Fork - LGPL
27473  * <script type="text/javascript">
27474  */
27475  
27476 /**
27477  * @class Roo.bootstrap.PagingToolbar
27478  * @extends Roo.bootstrap.NavSimplebar
27479  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27480  * @constructor
27481  * Create a new PagingToolbar
27482  * @param {Object} config The config object
27483  * @param {Roo.data.Store} store
27484  */
27485 Roo.bootstrap.PagingToolbar = function(config)
27486 {
27487     // old args format still supported... - xtype is prefered..
27488         // created from xtype...
27489     
27490     this.ds = config.dataSource;
27491     
27492     if (config.store && !this.ds) {
27493         this.store= Roo.factory(config.store, Roo.data);
27494         this.ds = this.store;
27495         this.ds.xmodule = this.xmodule || false;
27496     }
27497     
27498     this.toolbarItems = [];
27499     if (config.items) {
27500         this.toolbarItems = config.items;
27501     }
27502     
27503     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27504     
27505     this.cursor = 0;
27506     
27507     if (this.ds) { 
27508         this.bind(this.ds);
27509     }
27510     
27511     if (Roo.bootstrap.version == 4) {
27512         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27513     } else {
27514         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27515     }
27516     
27517 };
27518
27519 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27520     /**
27521      * @cfg {Roo.data.Store} dataSource
27522      * The underlying data store providing the paged data
27523      */
27524     /**
27525      * @cfg {String/HTMLElement/Element} container
27526      * container The id or element that will contain the toolbar
27527      */
27528     /**
27529      * @cfg {Boolean} displayInfo
27530      * True to display the displayMsg (defaults to false)
27531      */
27532     /**
27533      * @cfg {Number} pageSize
27534      * The number of records to display per page (defaults to 20)
27535      */
27536     pageSize: 20,
27537     /**
27538      * @cfg {String} displayMsg
27539      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27540      */
27541     displayMsg : 'Displaying {0} - {1} of {2}',
27542     /**
27543      * @cfg {String} emptyMsg
27544      * The message to display when no records are found (defaults to "No data to display")
27545      */
27546     emptyMsg : 'No data to display',
27547     /**
27548      * Customizable piece of the default paging text (defaults to "Page")
27549      * @type String
27550      */
27551     beforePageText : "Page",
27552     /**
27553      * Customizable piece of the default paging text (defaults to "of %0")
27554      * @type String
27555      */
27556     afterPageText : "of {0}",
27557     /**
27558      * Customizable piece of the default paging text (defaults to "First Page")
27559      * @type String
27560      */
27561     firstText : "First Page",
27562     /**
27563      * Customizable piece of the default paging text (defaults to "Previous Page")
27564      * @type String
27565      */
27566     prevText : "Previous Page",
27567     /**
27568      * Customizable piece of the default paging text (defaults to "Next Page")
27569      * @type String
27570      */
27571     nextText : "Next Page",
27572     /**
27573      * Customizable piece of the default paging text (defaults to "Last Page")
27574      * @type String
27575      */
27576     lastText : "Last Page",
27577     /**
27578      * Customizable piece of the default paging text (defaults to "Refresh")
27579      * @type String
27580      */
27581     refreshText : "Refresh",
27582
27583     buttons : false,
27584     // private
27585     onRender : function(ct, position) 
27586     {
27587         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27588         this.navgroup.parentId = this.id;
27589         this.navgroup.onRender(this.el, null);
27590         // add the buttons to the navgroup
27591         
27592         if(this.displayInfo){
27593             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27594             this.displayEl = this.el.select('.x-paging-info', true).first();
27595 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27596 //            this.displayEl = navel.el.select('span',true).first();
27597         }
27598         
27599         var _this = this;
27600         
27601         if(this.buttons){
27602             Roo.each(_this.buttons, function(e){ // this might need to use render????
27603                Roo.factory(e).render(_this.el);
27604             });
27605         }
27606             
27607         Roo.each(_this.toolbarItems, function(e) {
27608             _this.navgroup.addItem(e);
27609         });
27610         
27611         
27612         this.first = this.navgroup.addItem({
27613             tooltip: this.firstText,
27614             cls: "prev btn-outline-secondary",
27615             html : ' <i class="fa fa-step-backward"></i>',
27616             disabled: true,
27617             preventDefault: true,
27618             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27619         });
27620         
27621         this.prev =  this.navgroup.addItem({
27622             tooltip: this.prevText,
27623             cls: "prev btn-outline-secondary",
27624             html : ' <i class="fa fa-backward"></i>',
27625             disabled: true,
27626             preventDefault: true,
27627             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27628         });
27629     //this.addSeparator();
27630         
27631         
27632         var field = this.navgroup.addItem( {
27633             tagtype : 'span',
27634             cls : 'x-paging-position  btn-outline-secondary',
27635              disabled: true,
27636             html : this.beforePageText  +
27637                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27638                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27639          } ); //?? escaped?
27640         
27641         this.field = field.el.select('input', true).first();
27642         this.field.on("keydown", this.onPagingKeydown, this);
27643         this.field.on("focus", function(){this.dom.select();});
27644     
27645     
27646         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27647         //this.field.setHeight(18);
27648         //this.addSeparator();
27649         this.next = this.navgroup.addItem({
27650             tooltip: this.nextText,
27651             cls: "next btn-outline-secondary",
27652             html : ' <i class="fa fa-forward"></i>',
27653             disabled: true,
27654             preventDefault: true,
27655             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27656         });
27657         this.last = this.navgroup.addItem({
27658             tooltip: this.lastText,
27659             html : ' <i class="fa fa-step-forward"></i>',
27660             cls: "next btn-outline-secondary",
27661             disabled: true,
27662             preventDefault: true,
27663             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27664         });
27665     //this.addSeparator();
27666         this.loading = this.navgroup.addItem({
27667             tooltip: this.refreshText,
27668             cls: "btn-outline-secondary",
27669             html : ' <i class="fa fa-refresh"></i>',
27670             preventDefault: true,
27671             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27672         });
27673         
27674     },
27675
27676     // private
27677     updateInfo : function(){
27678         if(this.displayEl){
27679             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27680             var msg = count == 0 ?
27681                 this.emptyMsg :
27682                 String.format(
27683                     this.displayMsg,
27684                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27685                 );
27686             this.displayEl.update(msg);
27687         }
27688     },
27689
27690     // private
27691     onLoad : function(ds, r, o)
27692     {
27693         this.cursor = o.params && o.params.start ? o.params.start : 0;
27694         
27695         var d = this.getPageData(),
27696             ap = d.activePage,
27697             ps = d.pages;
27698         
27699         
27700         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27701         this.field.dom.value = ap;
27702         this.first.setDisabled(ap == 1);
27703         this.prev.setDisabled(ap == 1);
27704         this.next.setDisabled(ap == ps);
27705         this.last.setDisabled(ap == ps);
27706         this.loading.enable();
27707         this.updateInfo();
27708     },
27709
27710     // private
27711     getPageData : function(){
27712         var total = this.ds.getTotalCount();
27713         return {
27714             total : total,
27715             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27716             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27717         };
27718     },
27719
27720     // private
27721     onLoadError : function(){
27722         this.loading.enable();
27723     },
27724
27725     // private
27726     onPagingKeydown : function(e){
27727         var k = e.getKey();
27728         var d = this.getPageData();
27729         if(k == e.RETURN){
27730             var v = this.field.dom.value, pageNum;
27731             if(!v || isNaN(pageNum = parseInt(v, 10))){
27732                 this.field.dom.value = d.activePage;
27733                 return;
27734             }
27735             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27736             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27737             e.stopEvent();
27738         }
27739         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))
27740         {
27741           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27742           this.field.dom.value = pageNum;
27743           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27744           e.stopEvent();
27745         }
27746         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27747         {
27748           var v = this.field.dom.value, pageNum; 
27749           var increment = (e.shiftKey) ? 10 : 1;
27750           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27751                 increment *= -1;
27752           }
27753           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27754             this.field.dom.value = d.activePage;
27755             return;
27756           }
27757           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27758           {
27759             this.field.dom.value = parseInt(v, 10) + increment;
27760             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27761             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27762           }
27763           e.stopEvent();
27764         }
27765     },
27766
27767     // private
27768     beforeLoad : function(){
27769         if(this.loading){
27770             this.loading.disable();
27771         }
27772     },
27773
27774     // private
27775     onClick : function(which){
27776         
27777         var ds = this.ds;
27778         if (!ds) {
27779             return;
27780         }
27781         
27782         switch(which){
27783             case "first":
27784                 ds.load({params:{start: 0, limit: this.pageSize}});
27785             break;
27786             case "prev":
27787                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27788             break;
27789             case "next":
27790                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27791             break;
27792             case "last":
27793                 var total = ds.getTotalCount();
27794                 var extra = total % this.pageSize;
27795                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27796                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27797             break;
27798             case "refresh":
27799                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27800             break;
27801         }
27802     },
27803
27804     /**
27805      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27806      * @param {Roo.data.Store} store The data store to unbind
27807      */
27808     unbind : function(ds){
27809         ds.un("beforeload", this.beforeLoad, this);
27810         ds.un("load", this.onLoad, this);
27811         ds.un("loadexception", this.onLoadError, this);
27812         ds.un("remove", this.updateInfo, this);
27813         ds.un("add", this.updateInfo, this);
27814         this.ds = undefined;
27815     },
27816
27817     /**
27818      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27819      * @param {Roo.data.Store} store The data store to bind
27820      */
27821     bind : function(ds){
27822         ds.on("beforeload", this.beforeLoad, this);
27823         ds.on("load", this.onLoad, this);
27824         ds.on("loadexception", this.onLoadError, this);
27825         ds.on("remove", this.updateInfo, this);
27826         ds.on("add", this.updateInfo, this);
27827         this.ds = ds;
27828     }
27829 });/*
27830  * - LGPL
27831  *
27832  * element
27833  * 
27834  */
27835
27836 /**
27837  * @class Roo.bootstrap.MessageBar
27838  * @extends Roo.bootstrap.Component
27839  * Bootstrap MessageBar class
27840  * @cfg {String} html contents of the MessageBar
27841  * @cfg {String} weight (info | success | warning | danger) default info
27842  * @cfg {String} beforeClass insert the bar before the given class
27843  * @cfg {Boolean} closable (true | false) default false
27844  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27845  * 
27846  * @constructor
27847  * Create a new Element
27848  * @param {Object} config The config object
27849  */
27850
27851 Roo.bootstrap.MessageBar = function(config){
27852     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27853 };
27854
27855 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27856     
27857     html: '',
27858     weight: 'info',
27859     closable: false,
27860     fixed: false,
27861     beforeClass: 'bootstrap-sticky-wrap',
27862     
27863     getAutoCreate : function(){
27864         
27865         var cfg = {
27866             tag: 'div',
27867             cls: 'alert alert-dismissable alert-' + this.weight,
27868             cn: [
27869                 {
27870                     tag: 'span',
27871                     cls: 'message',
27872                     html: this.html || ''
27873                 }
27874             ]
27875         };
27876         
27877         if(this.fixed){
27878             cfg.cls += ' alert-messages-fixed';
27879         }
27880         
27881         if(this.closable){
27882             cfg.cn.push({
27883                 tag: 'button',
27884                 cls: 'close',
27885                 html: 'x'
27886             });
27887         }
27888         
27889         return cfg;
27890     },
27891     
27892     onRender : function(ct, position)
27893     {
27894         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27895         
27896         if(!this.el){
27897             var cfg = Roo.apply({},  this.getAutoCreate());
27898             cfg.id = Roo.id();
27899             
27900             if (this.cls) {
27901                 cfg.cls += ' ' + this.cls;
27902             }
27903             if (this.style) {
27904                 cfg.style = this.style;
27905             }
27906             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27907             
27908             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27909         }
27910         
27911         this.el.select('>button.close').on('click', this.hide, this);
27912         
27913     },
27914     
27915     show : function()
27916     {
27917         if (!this.rendered) {
27918             this.render();
27919         }
27920         
27921         this.el.show();
27922         
27923         this.fireEvent('show', this);
27924         
27925     },
27926     
27927     hide : function()
27928     {
27929         if (!this.rendered) {
27930             this.render();
27931         }
27932         
27933         this.el.hide();
27934         
27935         this.fireEvent('hide', this);
27936     },
27937     
27938     update : function()
27939     {
27940 //        var e = this.el.dom.firstChild;
27941 //        
27942 //        if(this.closable){
27943 //            e = e.nextSibling;
27944 //        }
27945 //        
27946 //        e.data = this.html || '';
27947
27948         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27949     }
27950    
27951 });
27952
27953  
27954
27955      /*
27956  * - LGPL
27957  *
27958  * Graph
27959  * 
27960  */
27961
27962
27963 /**
27964  * @class Roo.bootstrap.Graph
27965  * @extends Roo.bootstrap.Component
27966  * Bootstrap Graph class
27967 > Prameters
27968  -sm {number} sm 4
27969  -md {number} md 5
27970  @cfg {String} graphtype  bar | vbar | pie
27971  @cfg {number} g_x coodinator | centre x (pie)
27972  @cfg {number} g_y coodinator | centre y (pie)
27973  @cfg {number} g_r radius (pie)
27974  @cfg {number} g_height height of the chart (respected by all elements in the set)
27975  @cfg {number} g_width width of the chart (respected by all elements in the set)
27976  @cfg {Object} title The title of the chart
27977     
27978  -{Array}  values
27979  -opts (object) options for the chart 
27980      o {
27981      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27982      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27983      o vgutter (number)
27984      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.
27985      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27986      o to
27987      o stretch (boolean)
27988      o }
27989  -opts (object) options for the pie
27990      o{
27991      o cut
27992      o startAngle (number)
27993      o endAngle (number)
27994      } 
27995  *
27996  * @constructor
27997  * Create a new Input
27998  * @param {Object} config The config object
27999  */
28000
28001 Roo.bootstrap.Graph = function(config){
28002     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28003     
28004     this.addEvents({
28005         // img events
28006         /**
28007          * @event click
28008          * The img click event for the img.
28009          * @param {Roo.EventObject} e
28010          */
28011         "click" : true
28012     });
28013 };
28014
28015 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28016     
28017     sm: 4,
28018     md: 5,
28019     graphtype: 'bar',
28020     g_height: 250,
28021     g_width: 400,
28022     g_x: 50,
28023     g_y: 50,
28024     g_r: 30,
28025     opts:{
28026         //g_colors: this.colors,
28027         g_type: 'soft',
28028         g_gutter: '20%'
28029
28030     },
28031     title : false,
28032
28033     getAutoCreate : function(){
28034         
28035         var cfg = {
28036             tag: 'div',
28037             html : null
28038         };
28039         
28040         
28041         return  cfg;
28042     },
28043
28044     onRender : function(ct,position){
28045         
28046         
28047         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28048         
28049         if (typeof(Raphael) == 'undefined') {
28050             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28051             return;
28052         }
28053         
28054         this.raphael = Raphael(this.el.dom);
28055         
28056                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28057                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28058                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28059                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28060                 /*
28061                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28062                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28063                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28064                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28065                 
28066                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28067                 r.barchart(330, 10, 300, 220, data1);
28068                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28069                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28070                 */
28071                 
28072                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28073                 // r.barchart(30, 30, 560, 250,  xdata, {
28074                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28075                 //     axis : "0 0 1 1",
28076                 //     axisxlabels :  xdata
28077                 //     //yvalues : cols,
28078                    
28079                 // });
28080 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28081 //        
28082 //        this.load(null,xdata,{
28083 //                axis : "0 0 1 1",
28084 //                axisxlabels :  xdata
28085 //                });
28086
28087     },
28088
28089     load : function(graphtype,xdata,opts)
28090     {
28091         this.raphael.clear();
28092         if(!graphtype) {
28093             graphtype = this.graphtype;
28094         }
28095         if(!opts){
28096             opts = this.opts;
28097         }
28098         var r = this.raphael,
28099             fin = function () {
28100                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28101             },
28102             fout = function () {
28103                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28104             },
28105             pfin = function() {
28106                 this.sector.stop();
28107                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28108
28109                 if (this.label) {
28110                     this.label[0].stop();
28111                     this.label[0].attr({ r: 7.5 });
28112                     this.label[1].attr({ "font-weight": 800 });
28113                 }
28114             },
28115             pfout = function() {
28116                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28117
28118                 if (this.label) {
28119                     this.label[0].animate({ r: 5 }, 500, "bounce");
28120                     this.label[1].attr({ "font-weight": 400 });
28121                 }
28122             };
28123
28124         switch(graphtype){
28125             case 'bar':
28126                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28127                 break;
28128             case 'hbar':
28129                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28130                 break;
28131             case 'pie':
28132 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28133 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28134 //            
28135                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28136                 
28137                 break;
28138
28139         }
28140         
28141         if(this.title){
28142             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28143         }
28144         
28145     },
28146     
28147     setTitle: function(o)
28148     {
28149         this.title = o;
28150     },
28151     
28152     initEvents: function() {
28153         
28154         if(!this.href){
28155             this.el.on('click', this.onClick, this);
28156         }
28157     },
28158     
28159     onClick : function(e)
28160     {
28161         Roo.log('img onclick');
28162         this.fireEvent('click', this, e);
28163     }
28164    
28165 });
28166
28167  
28168 /*
28169  * - LGPL
28170  *
28171  * numberBox
28172  * 
28173  */
28174 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28175
28176 /**
28177  * @class Roo.bootstrap.dash.NumberBox
28178  * @extends Roo.bootstrap.Component
28179  * Bootstrap NumberBox class
28180  * @cfg {String} headline Box headline
28181  * @cfg {String} content Box content
28182  * @cfg {String} icon Box icon
28183  * @cfg {String} footer Footer text
28184  * @cfg {String} fhref Footer href
28185  * 
28186  * @constructor
28187  * Create a new NumberBox
28188  * @param {Object} config The config object
28189  */
28190
28191
28192 Roo.bootstrap.dash.NumberBox = function(config){
28193     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28194     
28195 };
28196
28197 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28198     
28199     headline : '',
28200     content : '',
28201     icon : '',
28202     footer : '',
28203     fhref : '',
28204     ficon : '',
28205     
28206     getAutoCreate : function(){
28207         
28208         var cfg = {
28209             tag : 'div',
28210             cls : 'small-box ',
28211             cn : [
28212                 {
28213                     tag : 'div',
28214                     cls : 'inner',
28215                     cn :[
28216                         {
28217                             tag : 'h3',
28218                             cls : 'roo-headline',
28219                             html : this.headline
28220                         },
28221                         {
28222                             tag : 'p',
28223                             cls : 'roo-content',
28224                             html : this.content
28225                         }
28226                     ]
28227                 }
28228             ]
28229         };
28230         
28231         if(this.icon){
28232             cfg.cn.push({
28233                 tag : 'div',
28234                 cls : 'icon',
28235                 cn :[
28236                     {
28237                         tag : 'i',
28238                         cls : 'ion ' + this.icon
28239                     }
28240                 ]
28241             });
28242         }
28243         
28244         if(this.footer){
28245             var footer = {
28246                 tag : 'a',
28247                 cls : 'small-box-footer',
28248                 href : this.fhref || '#',
28249                 html : this.footer
28250             };
28251             
28252             cfg.cn.push(footer);
28253             
28254         }
28255         
28256         return  cfg;
28257     },
28258
28259     onRender : function(ct,position){
28260         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28261
28262
28263        
28264                 
28265     },
28266
28267     setHeadline: function (value)
28268     {
28269         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28270     },
28271     
28272     setFooter: function (value, href)
28273     {
28274         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28275         
28276         if(href){
28277             this.el.select('a.small-box-footer',true).first().attr('href', href);
28278         }
28279         
28280     },
28281
28282     setContent: function (value)
28283     {
28284         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28285     },
28286
28287     initEvents: function() 
28288     {   
28289         
28290     }
28291     
28292 });
28293
28294  
28295 /*
28296  * - LGPL
28297  *
28298  * TabBox
28299  * 
28300  */
28301 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28302
28303 /**
28304  * @class Roo.bootstrap.dash.TabBox
28305  * @extends Roo.bootstrap.Component
28306  * Bootstrap TabBox class
28307  * @cfg {String} title Title of the TabBox
28308  * @cfg {String} icon Icon of the TabBox
28309  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28310  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28311  * 
28312  * @constructor
28313  * Create a new TabBox
28314  * @param {Object} config The config object
28315  */
28316
28317
28318 Roo.bootstrap.dash.TabBox = function(config){
28319     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28320     this.addEvents({
28321         // raw events
28322         /**
28323          * @event addpane
28324          * When a pane is added
28325          * @param {Roo.bootstrap.dash.TabPane} pane
28326          */
28327         "addpane" : true,
28328         /**
28329          * @event activatepane
28330          * When a pane is activated
28331          * @param {Roo.bootstrap.dash.TabPane} pane
28332          */
28333         "activatepane" : true
28334         
28335          
28336     });
28337     
28338     this.panes = [];
28339 };
28340
28341 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28342
28343     title : '',
28344     icon : false,
28345     showtabs : true,
28346     tabScrollable : false,
28347     
28348     getChildContainer : function()
28349     {
28350         return this.el.select('.tab-content', true).first();
28351     },
28352     
28353     getAutoCreate : function(){
28354         
28355         var header = {
28356             tag: 'li',
28357             cls: 'pull-left header',
28358             html: this.title,
28359             cn : []
28360         };
28361         
28362         if(this.icon){
28363             header.cn.push({
28364                 tag: 'i',
28365                 cls: 'fa ' + this.icon
28366             });
28367         }
28368         
28369         var h = {
28370             tag: 'ul',
28371             cls: 'nav nav-tabs pull-right',
28372             cn: [
28373                 header
28374             ]
28375         };
28376         
28377         if(this.tabScrollable){
28378             h = {
28379                 tag: 'div',
28380                 cls: 'tab-header',
28381                 cn: [
28382                     {
28383                         tag: 'ul',
28384                         cls: 'nav nav-tabs pull-right',
28385                         cn: [
28386                             header
28387                         ]
28388                     }
28389                 ]
28390             };
28391         }
28392         
28393         var cfg = {
28394             tag: 'div',
28395             cls: 'nav-tabs-custom',
28396             cn: [
28397                 h,
28398                 {
28399                     tag: 'div',
28400                     cls: 'tab-content no-padding',
28401                     cn: []
28402                 }
28403             ]
28404         };
28405
28406         return  cfg;
28407     },
28408     initEvents : function()
28409     {
28410         //Roo.log('add add pane handler');
28411         this.on('addpane', this.onAddPane, this);
28412     },
28413      /**
28414      * Updates the box title
28415      * @param {String} html to set the title to.
28416      */
28417     setTitle : function(value)
28418     {
28419         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28420     },
28421     onAddPane : function(pane)
28422     {
28423         this.panes.push(pane);
28424         //Roo.log('addpane');
28425         //Roo.log(pane);
28426         // tabs are rendere left to right..
28427         if(!this.showtabs){
28428             return;
28429         }
28430         
28431         var ctr = this.el.select('.nav-tabs', true).first();
28432          
28433          
28434         var existing = ctr.select('.nav-tab',true);
28435         var qty = existing.getCount();;
28436         
28437         
28438         var tab = ctr.createChild({
28439             tag : 'li',
28440             cls : 'nav-tab' + (qty ? '' : ' active'),
28441             cn : [
28442                 {
28443                     tag : 'a',
28444                     href:'#',
28445                     html : pane.title
28446                 }
28447             ]
28448         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28449         pane.tab = tab;
28450         
28451         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28452         if (!qty) {
28453             pane.el.addClass('active');
28454         }
28455         
28456                 
28457     },
28458     onTabClick : function(ev,un,ob,pane)
28459     {
28460         //Roo.log('tab - prev default');
28461         ev.preventDefault();
28462         
28463         
28464         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28465         pane.tab.addClass('active');
28466         //Roo.log(pane.title);
28467         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28468         // technically we should have a deactivate event.. but maybe add later.
28469         // and it should not de-activate the selected tab...
28470         this.fireEvent('activatepane', pane);
28471         pane.el.addClass('active');
28472         pane.fireEvent('activate');
28473         
28474         
28475     },
28476     
28477     getActivePane : function()
28478     {
28479         var r = false;
28480         Roo.each(this.panes, function(p) {
28481             if(p.el.hasClass('active')){
28482                 r = p;
28483                 return false;
28484             }
28485             
28486             return;
28487         });
28488         
28489         return r;
28490     }
28491     
28492     
28493 });
28494
28495  
28496 /*
28497  * - LGPL
28498  *
28499  * Tab pane
28500  * 
28501  */
28502 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28503 /**
28504  * @class Roo.bootstrap.TabPane
28505  * @extends Roo.bootstrap.Component
28506  * Bootstrap TabPane class
28507  * @cfg {Boolean} active (false | true) Default false
28508  * @cfg {String} title title of panel
28509
28510  * 
28511  * @constructor
28512  * Create a new TabPane
28513  * @param {Object} config The config object
28514  */
28515
28516 Roo.bootstrap.dash.TabPane = function(config){
28517     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28518     
28519     this.addEvents({
28520         // raw events
28521         /**
28522          * @event activate
28523          * When a pane is activated
28524          * @param {Roo.bootstrap.dash.TabPane} pane
28525          */
28526         "activate" : true
28527          
28528     });
28529 };
28530
28531 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28532     
28533     active : false,
28534     title : '',
28535     
28536     // the tabBox that this is attached to.
28537     tab : false,
28538      
28539     getAutoCreate : function() 
28540     {
28541         var cfg = {
28542             tag: 'div',
28543             cls: 'tab-pane'
28544         };
28545         
28546         if(this.active){
28547             cfg.cls += ' active';
28548         }
28549         
28550         return cfg;
28551     },
28552     initEvents  : function()
28553     {
28554         //Roo.log('trigger add pane handler');
28555         this.parent().fireEvent('addpane', this)
28556     },
28557     
28558      /**
28559      * Updates the tab title 
28560      * @param {String} html to set the title to.
28561      */
28562     setTitle: function(str)
28563     {
28564         if (!this.tab) {
28565             return;
28566         }
28567         this.title = str;
28568         this.tab.select('a', true).first().dom.innerHTML = str;
28569         
28570     }
28571     
28572     
28573     
28574 });
28575
28576  
28577
28578
28579  /*
28580  * - LGPL
28581  *
28582  * menu
28583  * 
28584  */
28585 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28586
28587 /**
28588  * @class Roo.bootstrap.menu.Menu
28589  * @extends Roo.bootstrap.Component
28590  * Bootstrap Menu class - container for Menu
28591  * @cfg {String} html Text of the menu
28592  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28593  * @cfg {String} icon Font awesome icon
28594  * @cfg {String} pos Menu align to (top | bottom) default bottom
28595  * 
28596  * 
28597  * @constructor
28598  * Create a new Menu
28599  * @param {Object} config The config object
28600  */
28601
28602
28603 Roo.bootstrap.menu.Menu = function(config){
28604     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28605     
28606     this.addEvents({
28607         /**
28608          * @event beforeshow
28609          * Fires before this menu is displayed
28610          * @param {Roo.bootstrap.menu.Menu} this
28611          */
28612         beforeshow : true,
28613         /**
28614          * @event beforehide
28615          * Fires before this menu is hidden
28616          * @param {Roo.bootstrap.menu.Menu} this
28617          */
28618         beforehide : true,
28619         /**
28620          * @event show
28621          * Fires after this menu is displayed
28622          * @param {Roo.bootstrap.menu.Menu} this
28623          */
28624         show : true,
28625         /**
28626          * @event hide
28627          * Fires after this menu is hidden
28628          * @param {Roo.bootstrap.menu.Menu} this
28629          */
28630         hide : true,
28631         /**
28632          * @event click
28633          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28634          * @param {Roo.bootstrap.menu.Menu} this
28635          * @param {Roo.EventObject} e
28636          */
28637         click : true
28638     });
28639     
28640 };
28641
28642 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28643     
28644     submenu : false,
28645     html : '',
28646     weight : 'default',
28647     icon : false,
28648     pos : 'bottom',
28649     
28650     
28651     getChildContainer : function() {
28652         if(this.isSubMenu){
28653             return this.el;
28654         }
28655         
28656         return this.el.select('ul.dropdown-menu', true).first();  
28657     },
28658     
28659     getAutoCreate : function()
28660     {
28661         var text = [
28662             {
28663                 tag : 'span',
28664                 cls : 'roo-menu-text',
28665                 html : this.html
28666             }
28667         ];
28668         
28669         if(this.icon){
28670             text.unshift({
28671                 tag : 'i',
28672                 cls : 'fa ' + this.icon
28673             })
28674         }
28675         
28676         
28677         var cfg = {
28678             tag : 'div',
28679             cls : 'btn-group',
28680             cn : [
28681                 {
28682                     tag : 'button',
28683                     cls : 'dropdown-button btn btn-' + this.weight,
28684                     cn : text
28685                 },
28686                 {
28687                     tag : 'button',
28688                     cls : 'dropdown-toggle btn btn-' + this.weight,
28689                     cn : [
28690                         {
28691                             tag : 'span',
28692                             cls : 'caret'
28693                         }
28694                     ]
28695                 },
28696                 {
28697                     tag : 'ul',
28698                     cls : 'dropdown-menu'
28699                 }
28700             ]
28701             
28702         };
28703         
28704         if(this.pos == 'top'){
28705             cfg.cls += ' dropup';
28706         }
28707         
28708         if(this.isSubMenu){
28709             cfg = {
28710                 tag : 'ul',
28711                 cls : 'dropdown-menu'
28712             }
28713         }
28714         
28715         return cfg;
28716     },
28717     
28718     onRender : function(ct, position)
28719     {
28720         this.isSubMenu = ct.hasClass('dropdown-submenu');
28721         
28722         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28723     },
28724     
28725     initEvents : function() 
28726     {
28727         if(this.isSubMenu){
28728             return;
28729         }
28730         
28731         this.hidden = true;
28732         
28733         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28734         this.triggerEl.on('click', this.onTriggerPress, this);
28735         
28736         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28737         this.buttonEl.on('click', this.onClick, this);
28738         
28739     },
28740     
28741     list : function()
28742     {
28743         if(this.isSubMenu){
28744             return this.el;
28745         }
28746         
28747         return this.el.select('ul.dropdown-menu', true).first();
28748     },
28749     
28750     onClick : function(e)
28751     {
28752         this.fireEvent("click", this, e);
28753     },
28754     
28755     onTriggerPress  : function(e)
28756     {   
28757         if (this.isVisible()) {
28758             this.hide();
28759         } else {
28760             this.show();
28761         }
28762     },
28763     
28764     isVisible : function(){
28765         return !this.hidden;
28766     },
28767     
28768     show : function()
28769     {
28770         this.fireEvent("beforeshow", this);
28771         
28772         this.hidden = false;
28773         this.el.addClass('open');
28774         
28775         Roo.get(document).on("mouseup", this.onMouseUp, this);
28776         
28777         this.fireEvent("show", this);
28778         
28779         
28780     },
28781     
28782     hide : function()
28783     {
28784         this.fireEvent("beforehide", this);
28785         
28786         this.hidden = true;
28787         this.el.removeClass('open');
28788         
28789         Roo.get(document).un("mouseup", this.onMouseUp);
28790         
28791         this.fireEvent("hide", this);
28792     },
28793     
28794     onMouseUp : function()
28795     {
28796         this.hide();
28797     }
28798     
28799 });
28800
28801  
28802  /*
28803  * - LGPL
28804  *
28805  * menu item
28806  * 
28807  */
28808 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28809
28810 /**
28811  * @class Roo.bootstrap.menu.Item
28812  * @extends Roo.bootstrap.Component
28813  * Bootstrap MenuItem class
28814  * @cfg {Boolean} submenu (true | false) default false
28815  * @cfg {String} html text of the item
28816  * @cfg {String} href the link
28817  * @cfg {Boolean} disable (true | false) default false
28818  * @cfg {Boolean} preventDefault (true | false) default true
28819  * @cfg {String} icon Font awesome icon
28820  * @cfg {String} pos Submenu align to (left | right) default right 
28821  * 
28822  * 
28823  * @constructor
28824  * Create a new Item
28825  * @param {Object} config The config object
28826  */
28827
28828
28829 Roo.bootstrap.menu.Item = function(config){
28830     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28831     this.addEvents({
28832         /**
28833          * @event mouseover
28834          * Fires when the mouse is hovering over this menu
28835          * @param {Roo.bootstrap.menu.Item} this
28836          * @param {Roo.EventObject} e
28837          */
28838         mouseover : true,
28839         /**
28840          * @event mouseout
28841          * Fires when the mouse exits this menu
28842          * @param {Roo.bootstrap.menu.Item} this
28843          * @param {Roo.EventObject} e
28844          */
28845         mouseout : true,
28846         // raw events
28847         /**
28848          * @event click
28849          * The raw click event for the entire grid.
28850          * @param {Roo.EventObject} e
28851          */
28852         click : true
28853     });
28854 };
28855
28856 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28857     
28858     submenu : false,
28859     href : '',
28860     html : '',
28861     preventDefault: true,
28862     disable : false,
28863     icon : false,
28864     pos : 'right',
28865     
28866     getAutoCreate : function()
28867     {
28868         var text = [
28869             {
28870                 tag : 'span',
28871                 cls : 'roo-menu-item-text',
28872                 html : this.html
28873             }
28874         ];
28875         
28876         if(this.icon){
28877             text.unshift({
28878                 tag : 'i',
28879                 cls : 'fa ' + this.icon
28880             })
28881         }
28882         
28883         var cfg = {
28884             tag : 'li',
28885             cn : [
28886                 {
28887                     tag : 'a',
28888                     href : this.href || '#',
28889                     cn : text
28890                 }
28891             ]
28892         };
28893         
28894         if(this.disable){
28895             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28896         }
28897         
28898         if(this.submenu){
28899             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28900             
28901             if(this.pos == 'left'){
28902                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28903             }
28904         }
28905         
28906         return cfg;
28907     },
28908     
28909     initEvents : function() 
28910     {
28911         this.el.on('mouseover', this.onMouseOver, this);
28912         this.el.on('mouseout', this.onMouseOut, this);
28913         
28914         this.el.select('a', true).first().on('click', this.onClick, this);
28915         
28916     },
28917     
28918     onClick : function(e)
28919     {
28920         if(this.preventDefault){
28921             e.preventDefault();
28922         }
28923         
28924         this.fireEvent("click", this, e);
28925     },
28926     
28927     onMouseOver : function(e)
28928     {
28929         if(this.submenu && this.pos == 'left'){
28930             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28931         }
28932         
28933         this.fireEvent("mouseover", this, e);
28934     },
28935     
28936     onMouseOut : function(e)
28937     {
28938         this.fireEvent("mouseout", this, e);
28939     }
28940 });
28941
28942  
28943
28944  /*
28945  * - LGPL
28946  *
28947  * menu separator
28948  * 
28949  */
28950 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28951
28952 /**
28953  * @class Roo.bootstrap.menu.Separator
28954  * @extends Roo.bootstrap.Component
28955  * Bootstrap Separator class
28956  * 
28957  * @constructor
28958  * Create a new Separator
28959  * @param {Object} config The config object
28960  */
28961
28962
28963 Roo.bootstrap.menu.Separator = function(config){
28964     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28965 };
28966
28967 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28968     
28969     getAutoCreate : function(){
28970         var cfg = {
28971             tag : 'li',
28972             cls: 'dropdown-divider divider'
28973         };
28974         
28975         return cfg;
28976     }
28977    
28978 });
28979
28980  
28981
28982  /*
28983  * - LGPL
28984  *
28985  * Tooltip
28986  * 
28987  */
28988
28989 /**
28990  * @class Roo.bootstrap.Tooltip
28991  * Bootstrap Tooltip class
28992  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28993  * to determine which dom element triggers the tooltip.
28994  * 
28995  * It needs to add support for additional attributes like tooltip-position
28996  * 
28997  * @constructor
28998  * Create a new Toolti
28999  * @param {Object} config The config object
29000  */
29001
29002 Roo.bootstrap.Tooltip = function(config){
29003     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29004     
29005     this.alignment = Roo.bootstrap.Tooltip.alignment;
29006     
29007     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29008         this.alignment = config.alignment;
29009     }
29010     
29011 };
29012
29013 Roo.apply(Roo.bootstrap.Tooltip, {
29014     /**
29015      * @function init initialize tooltip monitoring.
29016      * @static
29017      */
29018     currentEl : false,
29019     currentTip : false,
29020     currentRegion : false,
29021     
29022     //  init : delay?
29023     
29024     init : function()
29025     {
29026         Roo.get(document).on('mouseover', this.enter ,this);
29027         Roo.get(document).on('mouseout', this.leave, this);
29028          
29029         
29030         this.currentTip = new Roo.bootstrap.Tooltip();
29031     },
29032     
29033     enter : function(ev)
29034     {
29035         var dom = ev.getTarget();
29036         
29037         //Roo.log(['enter',dom]);
29038         var el = Roo.fly(dom);
29039         if (this.currentEl) {
29040             //Roo.log(dom);
29041             //Roo.log(this.currentEl);
29042             //Roo.log(this.currentEl.contains(dom));
29043             if (this.currentEl == el) {
29044                 return;
29045             }
29046             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29047                 return;
29048             }
29049
29050         }
29051         
29052         if (this.currentTip.el) {
29053             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29054         }    
29055         //Roo.log(ev);
29056         
29057         if(!el || el.dom == document){
29058             return;
29059         }
29060         
29061         var bindEl = el;
29062         
29063         // you can not look for children, as if el is the body.. then everythign is the child..
29064         if (!el.attr('tooltip')) { //
29065             if (!el.select("[tooltip]").elements.length) {
29066                 return;
29067             }
29068             // is the mouse over this child...?
29069             bindEl = el.select("[tooltip]").first();
29070             var xy = ev.getXY();
29071             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29072                 //Roo.log("not in region.");
29073                 return;
29074             }
29075             //Roo.log("child element over..");
29076             
29077         }
29078         this.currentEl = bindEl;
29079         this.currentTip.bind(bindEl);
29080         this.currentRegion = Roo.lib.Region.getRegion(dom);
29081         this.currentTip.enter();
29082         
29083     },
29084     leave : function(ev)
29085     {
29086         var dom = ev.getTarget();
29087         //Roo.log(['leave',dom]);
29088         if (!this.currentEl) {
29089             return;
29090         }
29091         
29092         
29093         if (dom != this.currentEl.dom) {
29094             return;
29095         }
29096         var xy = ev.getXY();
29097         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29098             return;
29099         }
29100         // only activate leave if mouse cursor is outside... bounding box..
29101         
29102         
29103         
29104         
29105         if (this.currentTip) {
29106             this.currentTip.leave();
29107         }
29108         //Roo.log('clear currentEl');
29109         this.currentEl = false;
29110         
29111         
29112     },
29113     alignment : {
29114         'left' : ['r-l', [-2,0], 'right'],
29115         'right' : ['l-r', [2,0], 'left'],
29116         'bottom' : ['t-b', [0,2], 'top'],
29117         'top' : [ 'b-t', [0,-2], 'bottom']
29118     }
29119     
29120 });
29121
29122
29123 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29124     
29125     
29126     bindEl : false,
29127     
29128     delay : null, // can be { show : 300 , hide: 500}
29129     
29130     timeout : null,
29131     
29132     hoverState : null, //???
29133     
29134     placement : 'bottom', 
29135     
29136     alignment : false,
29137     
29138     getAutoCreate : function(){
29139     
29140         var cfg = {
29141            cls : 'tooltip',   
29142            role : 'tooltip',
29143            cn : [
29144                 {
29145                     cls : 'tooltip-arrow arrow'
29146                 },
29147                 {
29148                     cls : 'tooltip-inner'
29149                 }
29150            ]
29151         };
29152         
29153         return cfg;
29154     },
29155     bind : function(el)
29156     {
29157         this.bindEl = el;
29158     },
29159     
29160     initEvents : function()
29161     {
29162         this.arrowEl = this.el.select('.arrow', true).first();
29163         this.innerEl = this.el.select('.tooltip-inner', true).first();
29164     },
29165     
29166     enter : function () {
29167        
29168         if (this.timeout != null) {
29169             clearTimeout(this.timeout);
29170         }
29171         
29172         this.hoverState = 'in';
29173          //Roo.log("enter - show");
29174         if (!this.delay || !this.delay.show) {
29175             this.show();
29176             return;
29177         }
29178         var _t = this;
29179         this.timeout = setTimeout(function () {
29180             if (_t.hoverState == 'in') {
29181                 _t.show();
29182             }
29183         }, this.delay.show);
29184     },
29185     leave : function()
29186     {
29187         clearTimeout(this.timeout);
29188     
29189         this.hoverState = 'out';
29190          if (!this.delay || !this.delay.hide) {
29191             this.hide();
29192             return;
29193         }
29194        
29195         var _t = this;
29196         this.timeout = setTimeout(function () {
29197             //Roo.log("leave - timeout");
29198             
29199             if (_t.hoverState == 'out') {
29200                 _t.hide();
29201                 Roo.bootstrap.Tooltip.currentEl = false;
29202             }
29203         }, delay);
29204     },
29205     
29206     show : function (msg)
29207     {
29208         if (!this.el) {
29209             this.render(document.body);
29210         }
29211         // set content.
29212         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29213         
29214         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29215         
29216         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29217         
29218         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29219                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29220         
29221         var placement = typeof this.placement == 'function' ?
29222             this.placement.call(this, this.el, on_el) :
29223             this.placement;
29224             
29225         var autoToken = /\s?auto?\s?/i;
29226         var autoPlace = autoToken.test(placement);
29227         if (autoPlace) {
29228             placement = placement.replace(autoToken, '') || 'top';
29229         }
29230         
29231         //this.el.detach()
29232         //this.el.setXY([0,0]);
29233         this.el.show();
29234         //this.el.dom.style.display='block';
29235         
29236         //this.el.appendTo(on_el);
29237         
29238         var p = this.getPosition();
29239         var box = this.el.getBox();
29240         
29241         if (autoPlace) {
29242             // fixme..
29243         }
29244         
29245         var align = this.alignment[placement];
29246         
29247         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29248         
29249         if(placement == 'top' || placement == 'bottom'){
29250             if(xy[0] < 0){
29251                 placement = 'right';
29252             }
29253             
29254             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29255                 placement = 'left';
29256             }
29257             
29258             var scroll = Roo.select('body', true).first().getScroll();
29259             
29260             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29261                 placement = 'top';
29262             }
29263             
29264             align = this.alignment[placement];
29265             
29266             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29267             
29268         }
29269         
29270         this.el.alignTo(this.bindEl, align[0],align[1]);
29271         //var arrow = this.el.select('.arrow',true).first();
29272         //arrow.set(align[2], 
29273         
29274         this.el.addClass(placement);
29275         this.el.addClass("bs-tooltip-"+ placement);
29276         
29277         this.el.addClass('in fade show');
29278         
29279         this.hoverState = null;
29280         
29281         if (this.el.hasClass('fade')) {
29282             // fade it?
29283         }
29284         
29285         
29286         
29287         
29288         
29289     },
29290     hide : function()
29291     {
29292          
29293         if (!this.el) {
29294             return;
29295         }
29296         //this.el.setXY([0,0]);
29297         this.el.removeClass(['show', 'in']);
29298         //this.el.hide();
29299         
29300     }
29301     
29302 });
29303  
29304
29305  /*
29306  * - LGPL
29307  *
29308  * Location Picker
29309  * 
29310  */
29311
29312 /**
29313  * @class Roo.bootstrap.LocationPicker
29314  * @extends Roo.bootstrap.Component
29315  * Bootstrap LocationPicker class
29316  * @cfg {Number} latitude Position when init default 0
29317  * @cfg {Number} longitude Position when init default 0
29318  * @cfg {Number} zoom default 15
29319  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29320  * @cfg {Boolean} mapTypeControl default false
29321  * @cfg {Boolean} disableDoubleClickZoom default false
29322  * @cfg {Boolean} scrollwheel default true
29323  * @cfg {Boolean} streetViewControl default false
29324  * @cfg {Number} radius default 0
29325  * @cfg {String} locationName
29326  * @cfg {Boolean} draggable default true
29327  * @cfg {Boolean} enableAutocomplete default false
29328  * @cfg {Boolean} enableReverseGeocode default true
29329  * @cfg {String} markerTitle
29330  * 
29331  * @constructor
29332  * Create a new LocationPicker
29333  * @param {Object} config The config object
29334  */
29335
29336
29337 Roo.bootstrap.LocationPicker = function(config){
29338     
29339     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29340     
29341     this.addEvents({
29342         /**
29343          * @event initial
29344          * Fires when the picker initialized.
29345          * @param {Roo.bootstrap.LocationPicker} this
29346          * @param {Google Location} location
29347          */
29348         initial : true,
29349         /**
29350          * @event positionchanged
29351          * Fires when the picker position changed.
29352          * @param {Roo.bootstrap.LocationPicker} this
29353          * @param {Google Location} location
29354          */
29355         positionchanged : true,
29356         /**
29357          * @event resize
29358          * Fires when the map resize.
29359          * @param {Roo.bootstrap.LocationPicker} this
29360          */
29361         resize : true,
29362         /**
29363          * @event show
29364          * Fires when the map show.
29365          * @param {Roo.bootstrap.LocationPicker} this
29366          */
29367         show : true,
29368         /**
29369          * @event hide
29370          * Fires when the map hide.
29371          * @param {Roo.bootstrap.LocationPicker} this
29372          */
29373         hide : true,
29374         /**
29375          * @event mapClick
29376          * Fires when click the map.
29377          * @param {Roo.bootstrap.LocationPicker} this
29378          * @param {Map event} e
29379          */
29380         mapClick : true,
29381         /**
29382          * @event mapRightClick
29383          * Fires when right click the map.
29384          * @param {Roo.bootstrap.LocationPicker} this
29385          * @param {Map event} e
29386          */
29387         mapRightClick : true,
29388         /**
29389          * @event markerClick
29390          * Fires when click the marker.
29391          * @param {Roo.bootstrap.LocationPicker} this
29392          * @param {Map event} e
29393          */
29394         markerClick : true,
29395         /**
29396          * @event markerRightClick
29397          * Fires when right click the marker.
29398          * @param {Roo.bootstrap.LocationPicker} this
29399          * @param {Map event} e
29400          */
29401         markerRightClick : true,
29402         /**
29403          * @event OverlayViewDraw
29404          * Fires when OverlayView Draw
29405          * @param {Roo.bootstrap.LocationPicker} this
29406          */
29407         OverlayViewDraw : true,
29408         /**
29409          * @event OverlayViewOnAdd
29410          * Fires when OverlayView Draw
29411          * @param {Roo.bootstrap.LocationPicker} this
29412          */
29413         OverlayViewOnAdd : true,
29414         /**
29415          * @event OverlayViewOnRemove
29416          * Fires when OverlayView Draw
29417          * @param {Roo.bootstrap.LocationPicker} this
29418          */
29419         OverlayViewOnRemove : true,
29420         /**
29421          * @event OverlayViewShow
29422          * Fires when OverlayView Draw
29423          * @param {Roo.bootstrap.LocationPicker} this
29424          * @param {Pixel} cpx
29425          */
29426         OverlayViewShow : true,
29427         /**
29428          * @event OverlayViewHide
29429          * Fires when OverlayView Draw
29430          * @param {Roo.bootstrap.LocationPicker} this
29431          */
29432         OverlayViewHide : true,
29433         /**
29434          * @event loadexception
29435          * Fires when load google lib failed.
29436          * @param {Roo.bootstrap.LocationPicker} this
29437          */
29438         loadexception : true
29439     });
29440         
29441 };
29442
29443 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29444     
29445     gMapContext: false,
29446     
29447     latitude: 0,
29448     longitude: 0,
29449     zoom: 15,
29450     mapTypeId: false,
29451     mapTypeControl: false,
29452     disableDoubleClickZoom: false,
29453     scrollwheel: true,
29454     streetViewControl: false,
29455     radius: 0,
29456     locationName: '',
29457     draggable: true,
29458     enableAutocomplete: false,
29459     enableReverseGeocode: true,
29460     markerTitle: '',
29461     
29462     getAutoCreate: function()
29463     {
29464
29465         var cfg = {
29466             tag: 'div',
29467             cls: 'roo-location-picker'
29468         };
29469         
29470         return cfg
29471     },
29472     
29473     initEvents: function(ct, position)
29474     {       
29475         if(!this.el.getWidth() || this.isApplied()){
29476             return;
29477         }
29478         
29479         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29480         
29481         this.initial();
29482     },
29483     
29484     initial: function()
29485     {
29486         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29487             this.fireEvent('loadexception', this);
29488             return;
29489         }
29490         
29491         if(!this.mapTypeId){
29492             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29493         }
29494         
29495         this.gMapContext = this.GMapContext();
29496         
29497         this.initOverlayView();
29498         
29499         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29500         
29501         var _this = this;
29502                 
29503         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29504             _this.setPosition(_this.gMapContext.marker.position);
29505         });
29506         
29507         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29508             _this.fireEvent('mapClick', this, event);
29509             
29510         });
29511
29512         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29513             _this.fireEvent('mapRightClick', this, event);
29514             
29515         });
29516         
29517         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29518             _this.fireEvent('markerClick', this, event);
29519             
29520         });
29521
29522         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29523             _this.fireEvent('markerRightClick', this, event);
29524             
29525         });
29526         
29527         this.setPosition(this.gMapContext.location);
29528         
29529         this.fireEvent('initial', this, this.gMapContext.location);
29530     },
29531     
29532     initOverlayView: function()
29533     {
29534         var _this = this;
29535         
29536         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29537             
29538             draw: function()
29539             {
29540                 _this.fireEvent('OverlayViewDraw', _this);
29541             },
29542             
29543             onAdd: function()
29544             {
29545                 _this.fireEvent('OverlayViewOnAdd', _this);
29546             },
29547             
29548             onRemove: function()
29549             {
29550                 _this.fireEvent('OverlayViewOnRemove', _this);
29551             },
29552             
29553             show: function(cpx)
29554             {
29555                 _this.fireEvent('OverlayViewShow', _this, cpx);
29556             },
29557             
29558             hide: function()
29559             {
29560                 _this.fireEvent('OverlayViewHide', _this);
29561             }
29562             
29563         });
29564     },
29565     
29566     fromLatLngToContainerPixel: function(event)
29567     {
29568         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29569     },
29570     
29571     isApplied: function() 
29572     {
29573         return this.getGmapContext() == false ? false : true;
29574     },
29575     
29576     getGmapContext: function() 
29577     {
29578         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29579     },
29580     
29581     GMapContext: function() 
29582     {
29583         var position = new google.maps.LatLng(this.latitude, this.longitude);
29584         
29585         var _map = new google.maps.Map(this.el.dom, {
29586             center: position,
29587             zoom: this.zoom,
29588             mapTypeId: this.mapTypeId,
29589             mapTypeControl: this.mapTypeControl,
29590             disableDoubleClickZoom: this.disableDoubleClickZoom,
29591             scrollwheel: this.scrollwheel,
29592             streetViewControl: this.streetViewControl,
29593             locationName: this.locationName,
29594             draggable: this.draggable,
29595             enableAutocomplete: this.enableAutocomplete,
29596             enableReverseGeocode: this.enableReverseGeocode
29597         });
29598         
29599         var _marker = new google.maps.Marker({
29600             position: position,
29601             map: _map,
29602             title: this.markerTitle,
29603             draggable: this.draggable
29604         });
29605         
29606         return {
29607             map: _map,
29608             marker: _marker,
29609             circle: null,
29610             location: position,
29611             radius: this.radius,
29612             locationName: this.locationName,
29613             addressComponents: {
29614                 formatted_address: null,
29615                 addressLine1: null,
29616                 addressLine2: null,
29617                 streetName: null,
29618                 streetNumber: null,
29619                 city: null,
29620                 district: null,
29621                 state: null,
29622                 stateOrProvince: null
29623             },
29624             settings: this,
29625             domContainer: this.el.dom,
29626             geodecoder: new google.maps.Geocoder()
29627         };
29628     },
29629     
29630     drawCircle: function(center, radius, options) 
29631     {
29632         if (this.gMapContext.circle != null) {
29633             this.gMapContext.circle.setMap(null);
29634         }
29635         if (radius > 0) {
29636             radius *= 1;
29637             options = Roo.apply({}, options, {
29638                 strokeColor: "#0000FF",
29639                 strokeOpacity: .35,
29640                 strokeWeight: 2,
29641                 fillColor: "#0000FF",
29642                 fillOpacity: .2
29643             });
29644             
29645             options.map = this.gMapContext.map;
29646             options.radius = radius;
29647             options.center = center;
29648             this.gMapContext.circle = new google.maps.Circle(options);
29649             return this.gMapContext.circle;
29650         }
29651         
29652         return null;
29653     },
29654     
29655     setPosition: function(location) 
29656     {
29657         this.gMapContext.location = location;
29658         this.gMapContext.marker.setPosition(location);
29659         this.gMapContext.map.panTo(location);
29660         this.drawCircle(location, this.gMapContext.radius, {});
29661         
29662         var _this = this;
29663         
29664         if (this.gMapContext.settings.enableReverseGeocode) {
29665             this.gMapContext.geodecoder.geocode({
29666                 latLng: this.gMapContext.location
29667             }, function(results, status) {
29668                 
29669                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29670                     _this.gMapContext.locationName = results[0].formatted_address;
29671                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29672                     
29673                     _this.fireEvent('positionchanged', this, location);
29674                 }
29675             });
29676             
29677             return;
29678         }
29679         
29680         this.fireEvent('positionchanged', this, location);
29681     },
29682     
29683     resize: function()
29684     {
29685         google.maps.event.trigger(this.gMapContext.map, "resize");
29686         
29687         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29688         
29689         this.fireEvent('resize', this);
29690     },
29691     
29692     setPositionByLatLng: function(latitude, longitude)
29693     {
29694         this.setPosition(new google.maps.LatLng(latitude, longitude));
29695     },
29696     
29697     getCurrentPosition: function() 
29698     {
29699         return {
29700             latitude: this.gMapContext.location.lat(),
29701             longitude: this.gMapContext.location.lng()
29702         };
29703     },
29704     
29705     getAddressName: function() 
29706     {
29707         return this.gMapContext.locationName;
29708     },
29709     
29710     getAddressComponents: function() 
29711     {
29712         return this.gMapContext.addressComponents;
29713     },
29714     
29715     address_component_from_google_geocode: function(address_components) 
29716     {
29717         var result = {};
29718         
29719         for (var i = 0; i < address_components.length; i++) {
29720             var component = address_components[i];
29721             if (component.types.indexOf("postal_code") >= 0) {
29722                 result.postalCode = component.short_name;
29723             } else if (component.types.indexOf("street_number") >= 0) {
29724                 result.streetNumber = component.short_name;
29725             } else if (component.types.indexOf("route") >= 0) {
29726                 result.streetName = component.short_name;
29727             } else if (component.types.indexOf("neighborhood") >= 0) {
29728                 result.city = component.short_name;
29729             } else if (component.types.indexOf("locality") >= 0) {
29730                 result.city = component.short_name;
29731             } else if (component.types.indexOf("sublocality") >= 0) {
29732                 result.district = component.short_name;
29733             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29734                 result.stateOrProvince = component.short_name;
29735             } else if (component.types.indexOf("country") >= 0) {
29736                 result.country = component.short_name;
29737             }
29738         }
29739         
29740         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29741         result.addressLine2 = "";
29742         return result;
29743     },
29744     
29745     setZoomLevel: function(zoom)
29746     {
29747         this.gMapContext.map.setZoom(zoom);
29748     },
29749     
29750     show: function()
29751     {
29752         if(!this.el){
29753             return;
29754         }
29755         
29756         this.el.show();
29757         
29758         this.resize();
29759         
29760         this.fireEvent('show', this);
29761     },
29762     
29763     hide: function()
29764     {
29765         if(!this.el){
29766             return;
29767         }
29768         
29769         this.el.hide();
29770         
29771         this.fireEvent('hide', this);
29772     }
29773     
29774 });
29775
29776 Roo.apply(Roo.bootstrap.LocationPicker, {
29777     
29778     OverlayView : function(map, options)
29779     {
29780         options = options || {};
29781         
29782         this.setMap(map);
29783     }
29784     
29785     
29786 });/**
29787  * @class Roo.bootstrap.Alert
29788  * @extends Roo.bootstrap.Component
29789  * Bootstrap Alert class - shows an alert area box
29790  * eg
29791  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29792   Enter a valid email address
29793 </div>
29794  * @licence LGPL
29795  * @cfg {String} title The title of alert
29796  * @cfg {String} html The content of alert
29797  * @cfg {String} weight (  success | info | warning | danger )
29798  * @cfg {String} faicon font-awesomeicon
29799  * 
29800  * @constructor
29801  * Create a new alert
29802  * @param {Object} config The config object
29803  */
29804
29805
29806 Roo.bootstrap.Alert = function(config){
29807     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29808     
29809 };
29810
29811 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29812     
29813     title: '',
29814     html: '',
29815     weight: false,
29816     faicon: false,
29817     
29818     getAutoCreate : function()
29819     {
29820         
29821         var cfg = {
29822             tag : 'div',
29823             cls : 'alert',
29824             cn : [
29825                 {
29826                     tag : 'i',
29827                     cls : 'roo-alert-icon'
29828                     
29829                 },
29830                 {
29831                     tag : 'b',
29832                     cls : 'roo-alert-title',
29833                     html : this.title
29834                 },
29835                 {
29836                     tag : 'span',
29837                     cls : 'roo-alert-text',
29838                     html : this.html
29839                 }
29840             ]
29841         };
29842         
29843         if(this.faicon){
29844             cfg.cn[0].cls += ' fa ' + this.faicon;
29845         }
29846         
29847         if(this.weight){
29848             cfg.cls += ' alert-' + this.weight;
29849         }
29850         
29851         return cfg;
29852     },
29853     
29854     initEvents: function() 
29855     {
29856         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29857     },
29858     
29859     setTitle : function(str)
29860     {
29861         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29862     },
29863     
29864     setText : function(str)
29865     {
29866         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29867     },
29868     
29869     setWeight : function(weight)
29870     {
29871         if(this.weight){
29872             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29873         }
29874         
29875         this.weight = weight;
29876         
29877         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29878     },
29879     
29880     setIcon : function(icon)
29881     {
29882         if(this.faicon){
29883             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29884         }
29885         
29886         this.faicon = icon;
29887         
29888         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29889     },
29890     
29891     hide: function() 
29892     {
29893         this.el.hide();   
29894     },
29895     
29896     show: function() 
29897     {  
29898         this.el.show();   
29899     }
29900     
29901 });
29902
29903  
29904 /*
29905 * Licence: LGPL
29906 */
29907
29908 /**
29909  * @class Roo.bootstrap.UploadCropbox
29910  * @extends Roo.bootstrap.Component
29911  * Bootstrap UploadCropbox class
29912  * @cfg {String} emptyText show when image has been loaded
29913  * @cfg {String} rotateNotify show when image too small to rotate
29914  * @cfg {Number} errorTimeout default 3000
29915  * @cfg {Number} minWidth default 300
29916  * @cfg {Number} minHeight default 300
29917  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29918  * @cfg {Boolean} isDocument (true|false) default false
29919  * @cfg {String} url action url
29920  * @cfg {String} paramName default 'imageUpload'
29921  * @cfg {String} method default POST
29922  * @cfg {Boolean} loadMask (true|false) default true
29923  * @cfg {Boolean} loadingText default 'Loading...'
29924  * 
29925  * @constructor
29926  * Create a new UploadCropbox
29927  * @param {Object} config The config object
29928  */
29929
29930 Roo.bootstrap.UploadCropbox = function(config){
29931     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29932     
29933     this.addEvents({
29934         /**
29935          * @event beforeselectfile
29936          * Fire before select file
29937          * @param {Roo.bootstrap.UploadCropbox} this
29938          */
29939         "beforeselectfile" : true,
29940         /**
29941          * @event initial
29942          * Fire after initEvent
29943          * @param {Roo.bootstrap.UploadCropbox} this
29944          */
29945         "initial" : true,
29946         /**
29947          * @event crop
29948          * Fire after initEvent
29949          * @param {Roo.bootstrap.UploadCropbox} this
29950          * @param {String} data
29951          */
29952         "crop" : true,
29953         /**
29954          * @event prepare
29955          * Fire when preparing the file data
29956          * @param {Roo.bootstrap.UploadCropbox} this
29957          * @param {Object} file
29958          */
29959         "prepare" : true,
29960         /**
29961          * @event exception
29962          * Fire when get exception
29963          * @param {Roo.bootstrap.UploadCropbox} this
29964          * @param {XMLHttpRequest} xhr
29965          */
29966         "exception" : true,
29967         /**
29968          * @event beforeloadcanvas
29969          * Fire before load the canvas
29970          * @param {Roo.bootstrap.UploadCropbox} this
29971          * @param {String} src
29972          */
29973         "beforeloadcanvas" : true,
29974         /**
29975          * @event trash
29976          * Fire when trash image
29977          * @param {Roo.bootstrap.UploadCropbox} this
29978          */
29979         "trash" : true,
29980         /**
29981          * @event download
29982          * Fire when download the image
29983          * @param {Roo.bootstrap.UploadCropbox} this
29984          */
29985         "download" : true,
29986         /**
29987          * @event footerbuttonclick
29988          * Fire when footerbuttonclick
29989          * @param {Roo.bootstrap.UploadCropbox} this
29990          * @param {String} type
29991          */
29992         "footerbuttonclick" : true,
29993         /**
29994          * @event resize
29995          * Fire when resize
29996          * @param {Roo.bootstrap.UploadCropbox} this
29997          */
29998         "resize" : true,
29999         /**
30000          * @event rotate
30001          * Fire when rotate the image
30002          * @param {Roo.bootstrap.UploadCropbox} this
30003          * @param {String} pos
30004          */
30005         "rotate" : true,
30006         /**
30007          * @event inspect
30008          * Fire when inspect the file
30009          * @param {Roo.bootstrap.UploadCropbox} this
30010          * @param {Object} file
30011          */
30012         "inspect" : true,
30013         /**
30014          * @event upload
30015          * Fire when xhr upload the file
30016          * @param {Roo.bootstrap.UploadCropbox} this
30017          * @param {Object} data
30018          */
30019         "upload" : true,
30020         /**
30021          * @event arrange
30022          * Fire when arrange the file data
30023          * @param {Roo.bootstrap.UploadCropbox} this
30024          * @param {Object} formData
30025          */
30026         "arrange" : true
30027     });
30028     
30029     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30030 };
30031
30032 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30033     
30034     emptyText : 'Click to upload image',
30035     rotateNotify : 'Image is too small to rotate',
30036     errorTimeout : 3000,
30037     scale : 0,
30038     baseScale : 1,
30039     rotate : 0,
30040     dragable : false,
30041     pinching : false,
30042     mouseX : 0,
30043     mouseY : 0,
30044     cropData : false,
30045     minWidth : 300,
30046     minHeight : 300,
30047     file : false,
30048     exif : {},
30049     baseRotate : 1,
30050     cropType : 'image/jpeg',
30051     buttons : false,
30052     canvasLoaded : false,
30053     isDocument : false,
30054     method : 'POST',
30055     paramName : 'imageUpload',
30056     loadMask : true,
30057     loadingText : 'Loading...',
30058     maskEl : false,
30059     
30060     getAutoCreate : function()
30061     {
30062         var cfg = {
30063             tag : 'div',
30064             cls : 'roo-upload-cropbox',
30065             cn : [
30066                 {
30067                     tag : 'input',
30068                     cls : 'roo-upload-cropbox-selector',
30069                     type : 'file'
30070                 },
30071                 {
30072                     tag : 'div',
30073                     cls : 'roo-upload-cropbox-body',
30074                     style : 'cursor:pointer',
30075                     cn : [
30076                         {
30077                             tag : 'div',
30078                             cls : 'roo-upload-cropbox-preview'
30079                         },
30080                         {
30081                             tag : 'div',
30082                             cls : 'roo-upload-cropbox-thumb'
30083                         },
30084                         {
30085                             tag : 'div',
30086                             cls : 'roo-upload-cropbox-empty-notify',
30087                             html : this.emptyText
30088                         },
30089                         {
30090                             tag : 'div',
30091                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30092                             html : this.rotateNotify
30093                         }
30094                     ]
30095                 },
30096                 {
30097                     tag : 'div',
30098                     cls : 'roo-upload-cropbox-footer',
30099                     cn : {
30100                         tag : 'div',
30101                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30102                         cn : []
30103                     }
30104                 }
30105             ]
30106         };
30107         
30108         return cfg;
30109     },
30110     
30111     onRender : function(ct, position)
30112     {
30113         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30114         
30115         if (this.buttons.length) {
30116             
30117             Roo.each(this.buttons, function(bb) {
30118                 
30119                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30120                 
30121                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30122                 
30123             }, this);
30124         }
30125         
30126         if(this.loadMask){
30127             this.maskEl = this.el;
30128         }
30129     },
30130     
30131     initEvents : function()
30132     {
30133         this.urlAPI = (window.createObjectURL && window) || 
30134                                 (window.URL && URL.revokeObjectURL && URL) || 
30135                                 (window.webkitURL && webkitURL);
30136                         
30137         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30138         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30139         
30140         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30141         this.selectorEl.hide();
30142         
30143         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30144         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30145         
30146         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30147         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30148         this.thumbEl.hide();
30149         
30150         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30151         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30152         
30153         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30154         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30155         this.errorEl.hide();
30156         
30157         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30158         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30159         this.footerEl.hide();
30160         
30161         this.setThumbBoxSize();
30162         
30163         this.bind();
30164         
30165         this.resize();
30166         
30167         this.fireEvent('initial', this);
30168     },
30169
30170     bind : function()
30171     {
30172         var _this = this;
30173         
30174         window.addEventListener("resize", function() { _this.resize(); } );
30175         
30176         this.bodyEl.on('click', this.beforeSelectFile, this);
30177         
30178         if(Roo.isTouch){
30179             this.bodyEl.on('touchstart', this.onTouchStart, this);
30180             this.bodyEl.on('touchmove', this.onTouchMove, this);
30181             this.bodyEl.on('touchend', this.onTouchEnd, this);
30182         }
30183         
30184         if(!Roo.isTouch){
30185             this.bodyEl.on('mousedown', this.onMouseDown, this);
30186             this.bodyEl.on('mousemove', this.onMouseMove, this);
30187             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30188             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30189             Roo.get(document).on('mouseup', this.onMouseUp, this);
30190         }
30191         
30192         this.selectorEl.on('change', this.onFileSelected, this);
30193     },
30194     
30195     reset : function()
30196     {    
30197         this.scale = 0;
30198         this.baseScale = 1;
30199         this.rotate = 0;
30200         this.baseRotate = 1;
30201         this.dragable = false;
30202         this.pinching = false;
30203         this.mouseX = 0;
30204         this.mouseY = 0;
30205         this.cropData = false;
30206         this.notifyEl.dom.innerHTML = this.emptyText;
30207         
30208         this.selectorEl.dom.value = '';
30209         
30210     },
30211     
30212     resize : function()
30213     {
30214         if(this.fireEvent('resize', this) != false){
30215             this.setThumbBoxPosition();
30216             this.setCanvasPosition();
30217         }
30218     },
30219     
30220     onFooterButtonClick : function(e, el, o, type)
30221     {
30222         switch (type) {
30223             case 'rotate-left' :
30224                 this.onRotateLeft(e);
30225                 break;
30226             case 'rotate-right' :
30227                 this.onRotateRight(e);
30228                 break;
30229             case 'picture' :
30230                 this.beforeSelectFile(e);
30231                 break;
30232             case 'trash' :
30233                 this.trash(e);
30234                 break;
30235             case 'crop' :
30236                 this.crop(e);
30237                 break;
30238             case 'download' :
30239                 this.download(e);
30240                 break;
30241             default :
30242                 break;
30243         }
30244         
30245         this.fireEvent('footerbuttonclick', this, type);
30246     },
30247     
30248     beforeSelectFile : function(e)
30249     {
30250         e.preventDefault();
30251         
30252         if(this.fireEvent('beforeselectfile', this) != false){
30253             this.selectorEl.dom.click();
30254         }
30255     },
30256     
30257     onFileSelected : function(e)
30258     {
30259         e.preventDefault();
30260         
30261         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30262             return;
30263         }
30264         
30265         var file = this.selectorEl.dom.files[0];
30266         
30267         if(this.fireEvent('inspect', this, file) != false){
30268             this.prepare(file);
30269         }
30270         
30271     },
30272     
30273     trash : function(e)
30274     {
30275         this.fireEvent('trash', this);
30276     },
30277     
30278     download : function(e)
30279     {
30280         this.fireEvent('download', this);
30281     },
30282     
30283     loadCanvas : function(src)
30284     {   
30285         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30286             
30287             this.reset();
30288             
30289             this.imageEl = document.createElement('img');
30290             
30291             var _this = this;
30292             
30293             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30294             
30295             this.imageEl.src = src;
30296         }
30297     },
30298     
30299     onLoadCanvas : function()
30300     {   
30301         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30302         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30303         
30304         this.bodyEl.un('click', this.beforeSelectFile, this);
30305         
30306         this.notifyEl.hide();
30307         this.thumbEl.show();
30308         this.footerEl.show();
30309         
30310         this.baseRotateLevel();
30311         
30312         if(this.isDocument){
30313             this.setThumbBoxSize();
30314         }
30315         
30316         this.setThumbBoxPosition();
30317         
30318         this.baseScaleLevel();
30319         
30320         this.draw();
30321         
30322         this.resize();
30323         
30324         this.canvasLoaded = true;
30325         
30326         if(this.loadMask){
30327             this.maskEl.unmask();
30328         }
30329         
30330     },
30331     
30332     setCanvasPosition : function()
30333     {   
30334         if(!this.canvasEl){
30335             return;
30336         }
30337         
30338         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30339         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30340         
30341         this.previewEl.setLeft(pw);
30342         this.previewEl.setTop(ph);
30343         
30344     },
30345     
30346     onMouseDown : function(e)
30347     {   
30348         e.stopEvent();
30349         
30350         this.dragable = true;
30351         this.pinching = false;
30352         
30353         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30354             this.dragable = false;
30355             return;
30356         }
30357         
30358         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30359         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30360         
30361     },
30362     
30363     onMouseMove : function(e)
30364     {   
30365         e.stopEvent();
30366         
30367         if(!this.canvasLoaded){
30368             return;
30369         }
30370         
30371         if (!this.dragable){
30372             return;
30373         }
30374         
30375         var minX = Math.ceil(this.thumbEl.getLeft(true));
30376         var minY = Math.ceil(this.thumbEl.getTop(true));
30377         
30378         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30379         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30380         
30381         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30382         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30383         
30384         x = x - this.mouseX;
30385         y = y - this.mouseY;
30386         
30387         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30388         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30389         
30390         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30391         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30392         
30393         this.previewEl.setLeft(bgX);
30394         this.previewEl.setTop(bgY);
30395         
30396         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30397         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30398     },
30399     
30400     onMouseUp : function(e)
30401     {   
30402         e.stopEvent();
30403         
30404         this.dragable = false;
30405     },
30406     
30407     onMouseWheel : function(e)
30408     {   
30409         e.stopEvent();
30410         
30411         this.startScale = this.scale;
30412         
30413         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30414         
30415         if(!this.zoomable()){
30416             this.scale = this.startScale;
30417             return;
30418         }
30419         
30420         this.draw();
30421         
30422         return;
30423     },
30424     
30425     zoomable : function()
30426     {
30427         var minScale = this.thumbEl.getWidth() / this.minWidth;
30428         
30429         if(this.minWidth < this.minHeight){
30430             minScale = this.thumbEl.getHeight() / this.minHeight;
30431         }
30432         
30433         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30434         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30435         
30436         if(
30437                 this.isDocument &&
30438                 (this.rotate == 0 || this.rotate == 180) && 
30439                 (
30440                     width > this.imageEl.OriginWidth || 
30441                     height > this.imageEl.OriginHeight ||
30442                     (width < this.minWidth && height < this.minHeight)
30443                 )
30444         ){
30445             return false;
30446         }
30447         
30448         if(
30449                 this.isDocument &&
30450                 (this.rotate == 90 || this.rotate == 270) && 
30451                 (
30452                     width > this.imageEl.OriginWidth || 
30453                     height > this.imageEl.OriginHeight ||
30454                     (width < this.minHeight && height < this.minWidth)
30455                 )
30456         ){
30457             return false;
30458         }
30459         
30460         if(
30461                 !this.isDocument &&
30462                 (this.rotate == 0 || this.rotate == 180) && 
30463                 (
30464                     width < this.minWidth || 
30465                     width > this.imageEl.OriginWidth || 
30466                     height < this.minHeight || 
30467                     height > this.imageEl.OriginHeight
30468                 )
30469         ){
30470             return false;
30471         }
30472         
30473         if(
30474                 !this.isDocument &&
30475                 (this.rotate == 90 || this.rotate == 270) && 
30476                 (
30477                     width < this.minHeight || 
30478                     width > this.imageEl.OriginWidth || 
30479                     height < this.minWidth || 
30480                     height > this.imageEl.OriginHeight
30481                 )
30482         ){
30483             return false;
30484         }
30485         
30486         return true;
30487         
30488     },
30489     
30490     onRotateLeft : function(e)
30491     {   
30492         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30493             
30494             var minScale = this.thumbEl.getWidth() / this.minWidth;
30495             
30496             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30497             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30498             
30499             this.startScale = this.scale;
30500             
30501             while (this.getScaleLevel() < minScale){
30502             
30503                 this.scale = this.scale + 1;
30504                 
30505                 if(!this.zoomable()){
30506                     break;
30507                 }
30508                 
30509                 if(
30510                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30511                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30512                 ){
30513                     continue;
30514                 }
30515                 
30516                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30517
30518                 this.draw();
30519                 
30520                 return;
30521             }
30522             
30523             this.scale = this.startScale;
30524             
30525             this.onRotateFail();
30526             
30527             return false;
30528         }
30529         
30530         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30531
30532         if(this.isDocument){
30533             this.setThumbBoxSize();
30534             this.setThumbBoxPosition();
30535             this.setCanvasPosition();
30536         }
30537         
30538         this.draw();
30539         
30540         this.fireEvent('rotate', this, 'left');
30541         
30542     },
30543     
30544     onRotateRight : function(e)
30545     {
30546         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30547             
30548             var minScale = this.thumbEl.getWidth() / this.minWidth;
30549         
30550             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30551             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30552             
30553             this.startScale = this.scale;
30554             
30555             while (this.getScaleLevel() < minScale){
30556             
30557                 this.scale = this.scale + 1;
30558                 
30559                 if(!this.zoomable()){
30560                     break;
30561                 }
30562                 
30563                 if(
30564                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30565                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30566                 ){
30567                     continue;
30568                 }
30569                 
30570                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30571
30572                 this.draw();
30573                 
30574                 return;
30575             }
30576             
30577             this.scale = this.startScale;
30578             
30579             this.onRotateFail();
30580             
30581             return false;
30582         }
30583         
30584         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30585
30586         if(this.isDocument){
30587             this.setThumbBoxSize();
30588             this.setThumbBoxPosition();
30589             this.setCanvasPosition();
30590         }
30591         
30592         this.draw();
30593         
30594         this.fireEvent('rotate', this, 'right');
30595     },
30596     
30597     onRotateFail : function()
30598     {
30599         this.errorEl.show(true);
30600         
30601         var _this = this;
30602         
30603         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30604     },
30605     
30606     draw : function()
30607     {
30608         this.previewEl.dom.innerHTML = '';
30609         
30610         var canvasEl = document.createElement("canvas");
30611         
30612         var contextEl = canvasEl.getContext("2d");
30613         
30614         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30615         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30616         var center = this.imageEl.OriginWidth / 2;
30617         
30618         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30619             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30620             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30621             center = this.imageEl.OriginHeight / 2;
30622         }
30623         
30624         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30625         
30626         contextEl.translate(center, center);
30627         contextEl.rotate(this.rotate * Math.PI / 180);
30628
30629         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30630         
30631         this.canvasEl = document.createElement("canvas");
30632         
30633         this.contextEl = this.canvasEl.getContext("2d");
30634         
30635         switch (this.rotate) {
30636             case 0 :
30637                 
30638                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30639                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30640                 
30641                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30642                 
30643                 break;
30644             case 90 : 
30645                 
30646                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30647                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30648                 
30649                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30650                     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);
30651                     break;
30652                 }
30653                 
30654                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30655                 
30656                 break;
30657             case 180 :
30658                 
30659                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30660                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30661                 
30662                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30663                     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);
30664                     break;
30665                 }
30666                 
30667                 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);
30668                 
30669                 break;
30670             case 270 :
30671                 
30672                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30673                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30674         
30675                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30676                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30677                     break;
30678                 }
30679                 
30680                 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);
30681                 
30682                 break;
30683             default : 
30684                 break;
30685         }
30686         
30687         this.previewEl.appendChild(this.canvasEl);
30688         
30689         this.setCanvasPosition();
30690     },
30691     
30692     crop : function()
30693     {
30694         if(!this.canvasLoaded){
30695             return;
30696         }
30697         
30698         var imageCanvas = document.createElement("canvas");
30699         
30700         var imageContext = imageCanvas.getContext("2d");
30701         
30702         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30703         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30704         
30705         var center = imageCanvas.width / 2;
30706         
30707         imageContext.translate(center, center);
30708         
30709         imageContext.rotate(this.rotate * Math.PI / 180);
30710         
30711         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30712         
30713         var canvas = document.createElement("canvas");
30714         
30715         var context = canvas.getContext("2d");
30716                 
30717         canvas.width = this.minWidth;
30718         canvas.height = this.minHeight;
30719
30720         switch (this.rotate) {
30721             case 0 :
30722                 
30723                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30724                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30725                 
30726                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30727                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30728                 
30729                 var targetWidth = this.minWidth - 2 * x;
30730                 var targetHeight = this.minHeight - 2 * y;
30731                 
30732                 var scale = 1;
30733                 
30734                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30735                     scale = targetWidth / width;
30736                 }
30737                 
30738                 if(x > 0 && y == 0){
30739                     scale = targetHeight / height;
30740                 }
30741                 
30742                 if(x > 0 && y > 0){
30743                     scale = targetWidth / width;
30744                     
30745                     if(width < height){
30746                         scale = targetHeight / height;
30747                     }
30748                 }
30749                 
30750                 context.scale(scale, scale);
30751                 
30752                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30753                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30754
30755                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30756                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30757
30758                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30759                 
30760                 break;
30761             case 90 : 
30762                 
30763                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30764                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30765                 
30766                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30767                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30768                 
30769                 var targetWidth = this.minWidth - 2 * x;
30770                 var targetHeight = this.minHeight - 2 * y;
30771                 
30772                 var scale = 1;
30773                 
30774                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30775                     scale = targetWidth / width;
30776                 }
30777                 
30778                 if(x > 0 && y == 0){
30779                     scale = targetHeight / height;
30780                 }
30781                 
30782                 if(x > 0 && y > 0){
30783                     scale = targetWidth / width;
30784                     
30785                     if(width < height){
30786                         scale = targetHeight / height;
30787                     }
30788                 }
30789                 
30790                 context.scale(scale, scale);
30791                 
30792                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30793                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30794
30795                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30796                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30797                 
30798                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30799                 
30800                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30801                 
30802                 break;
30803             case 180 :
30804                 
30805                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30806                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30807                 
30808                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30809                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30810                 
30811                 var targetWidth = this.minWidth - 2 * x;
30812                 var targetHeight = this.minHeight - 2 * y;
30813                 
30814                 var scale = 1;
30815                 
30816                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30817                     scale = targetWidth / width;
30818                 }
30819                 
30820                 if(x > 0 && y == 0){
30821                     scale = targetHeight / height;
30822                 }
30823                 
30824                 if(x > 0 && y > 0){
30825                     scale = targetWidth / width;
30826                     
30827                     if(width < height){
30828                         scale = targetHeight / height;
30829                     }
30830                 }
30831                 
30832                 context.scale(scale, scale);
30833                 
30834                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30835                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30836
30837                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30838                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30839
30840                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30841                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30842                 
30843                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30844                 
30845                 break;
30846             case 270 :
30847                 
30848                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30849                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30850                 
30851                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30852                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30853                 
30854                 var targetWidth = this.minWidth - 2 * x;
30855                 var targetHeight = this.minHeight - 2 * y;
30856                 
30857                 var scale = 1;
30858                 
30859                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30860                     scale = targetWidth / width;
30861                 }
30862                 
30863                 if(x > 0 && y == 0){
30864                     scale = targetHeight / height;
30865                 }
30866                 
30867                 if(x > 0 && y > 0){
30868                     scale = targetWidth / width;
30869                     
30870                     if(width < height){
30871                         scale = targetHeight / height;
30872                     }
30873                 }
30874                 
30875                 context.scale(scale, scale);
30876                 
30877                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30878                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30879
30880                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30881                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30882                 
30883                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30884                 
30885                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30886                 
30887                 break;
30888             default : 
30889                 break;
30890         }
30891         
30892         this.cropData = canvas.toDataURL(this.cropType);
30893         
30894         if(this.fireEvent('crop', this, this.cropData) !== false){
30895             this.process(this.file, this.cropData);
30896         }
30897         
30898         return;
30899         
30900     },
30901     
30902     setThumbBoxSize : function()
30903     {
30904         var width, height;
30905         
30906         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30907             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30908             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30909             
30910             this.minWidth = width;
30911             this.minHeight = height;
30912             
30913             if(this.rotate == 90 || this.rotate == 270){
30914                 this.minWidth = height;
30915                 this.minHeight = width;
30916             }
30917         }
30918         
30919         height = 300;
30920         width = Math.ceil(this.minWidth * height / this.minHeight);
30921         
30922         if(this.minWidth > this.minHeight){
30923             width = 300;
30924             height = Math.ceil(this.minHeight * width / this.minWidth);
30925         }
30926         
30927         this.thumbEl.setStyle({
30928             width : width + 'px',
30929             height : height + 'px'
30930         });
30931
30932         return;
30933             
30934     },
30935     
30936     setThumbBoxPosition : function()
30937     {
30938         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30939         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30940         
30941         this.thumbEl.setLeft(x);
30942         this.thumbEl.setTop(y);
30943         
30944     },
30945     
30946     baseRotateLevel : function()
30947     {
30948         this.baseRotate = 1;
30949         
30950         if(
30951                 typeof(this.exif) != 'undefined' &&
30952                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30953                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30954         ){
30955             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30956         }
30957         
30958         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30959         
30960     },
30961     
30962     baseScaleLevel : function()
30963     {
30964         var width, height;
30965         
30966         if(this.isDocument){
30967             
30968             if(this.baseRotate == 6 || this.baseRotate == 8){
30969             
30970                 height = this.thumbEl.getHeight();
30971                 this.baseScale = height / this.imageEl.OriginWidth;
30972
30973                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30974                     width = this.thumbEl.getWidth();
30975                     this.baseScale = width / this.imageEl.OriginHeight;
30976                 }
30977
30978                 return;
30979             }
30980
30981             height = this.thumbEl.getHeight();
30982             this.baseScale = height / this.imageEl.OriginHeight;
30983
30984             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30985                 width = this.thumbEl.getWidth();
30986                 this.baseScale = width / this.imageEl.OriginWidth;
30987             }
30988
30989             return;
30990         }
30991         
30992         if(this.baseRotate == 6 || this.baseRotate == 8){
30993             
30994             width = this.thumbEl.getHeight();
30995             this.baseScale = width / this.imageEl.OriginHeight;
30996             
30997             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30998                 height = this.thumbEl.getWidth();
30999                 this.baseScale = height / this.imageEl.OriginHeight;
31000             }
31001             
31002             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31003                 height = this.thumbEl.getWidth();
31004                 this.baseScale = height / this.imageEl.OriginHeight;
31005                 
31006                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31007                     width = this.thumbEl.getHeight();
31008                     this.baseScale = width / this.imageEl.OriginWidth;
31009                 }
31010             }
31011             
31012             return;
31013         }
31014         
31015         width = this.thumbEl.getWidth();
31016         this.baseScale = width / this.imageEl.OriginWidth;
31017         
31018         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31019             height = this.thumbEl.getHeight();
31020             this.baseScale = height / this.imageEl.OriginHeight;
31021         }
31022         
31023         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31024             
31025             height = this.thumbEl.getHeight();
31026             this.baseScale = height / this.imageEl.OriginHeight;
31027             
31028             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31029                 width = this.thumbEl.getWidth();
31030                 this.baseScale = width / this.imageEl.OriginWidth;
31031             }
31032             
31033         }
31034         
31035         return;
31036     },
31037     
31038     getScaleLevel : function()
31039     {
31040         return this.baseScale * Math.pow(1.1, this.scale);
31041     },
31042     
31043     onTouchStart : function(e)
31044     {
31045         if(!this.canvasLoaded){
31046             this.beforeSelectFile(e);
31047             return;
31048         }
31049         
31050         var touches = e.browserEvent.touches;
31051         
31052         if(!touches){
31053             return;
31054         }
31055         
31056         if(touches.length == 1){
31057             this.onMouseDown(e);
31058             return;
31059         }
31060         
31061         if(touches.length != 2){
31062             return;
31063         }
31064         
31065         var coords = [];
31066         
31067         for(var i = 0, finger; finger = touches[i]; i++){
31068             coords.push(finger.pageX, finger.pageY);
31069         }
31070         
31071         var x = Math.pow(coords[0] - coords[2], 2);
31072         var y = Math.pow(coords[1] - coords[3], 2);
31073         
31074         this.startDistance = Math.sqrt(x + y);
31075         
31076         this.startScale = this.scale;
31077         
31078         this.pinching = true;
31079         this.dragable = false;
31080         
31081     },
31082     
31083     onTouchMove : function(e)
31084     {
31085         if(!this.pinching && !this.dragable){
31086             return;
31087         }
31088         
31089         var touches = e.browserEvent.touches;
31090         
31091         if(!touches){
31092             return;
31093         }
31094         
31095         if(this.dragable){
31096             this.onMouseMove(e);
31097             return;
31098         }
31099         
31100         var coords = [];
31101         
31102         for(var i = 0, finger; finger = touches[i]; i++){
31103             coords.push(finger.pageX, finger.pageY);
31104         }
31105         
31106         var x = Math.pow(coords[0] - coords[2], 2);
31107         var y = Math.pow(coords[1] - coords[3], 2);
31108         
31109         this.endDistance = Math.sqrt(x + y);
31110         
31111         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31112         
31113         if(!this.zoomable()){
31114             this.scale = this.startScale;
31115             return;
31116         }
31117         
31118         this.draw();
31119         
31120     },
31121     
31122     onTouchEnd : function(e)
31123     {
31124         this.pinching = false;
31125         this.dragable = false;
31126         
31127     },
31128     
31129     process : function(file, crop)
31130     {
31131         if(this.loadMask){
31132             this.maskEl.mask(this.loadingText);
31133         }
31134         
31135         this.xhr = new XMLHttpRequest();
31136         
31137         file.xhr = this.xhr;
31138
31139         this.xhr.open(this.method, this.url, true);
31140         
31141         var headers = {
31142             "Accept": "application/json",
31143             "Cache-Control": "no-cache",
31144             "X-Requested-With": "XMLHttpRequest"
31145         };
31146         
31147         for (var headerName in headers) {
31148             var headerValue = headers[headerName];
31149             if (headerValue) {
31150                 this.xhr.setRequestHeader(headerName, headerValue);
31151             }
31152         }
31153         
31154         var _this = this;
31155         
31156         this.xhr.onload = function()
31157         {
31158             _this.xhrOnLoad(_this.xhr);
31159         }
31160         
31161         this.xhr.onerror = function()
31162         {
31163             _this.xhrOnError(_this.xhr);
31164         }
31165         
31166         var formData = new FormData();
31167
31168         formData.append('returnHTML', 'NO');
31169         
31170         if(crop){
31171             formData.append('crop', crop);
31172         }
31173         
31174         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31175             formData.append(this.paramName, file, file.name);
31176         }
31177         
31178         if(typeof(file.filename) != 'undefined'){
31179             formData.append('filename', file.filename);
31180         }
31181         
31182         if(typeof(file.mimetype) != 'undefined'){
31183             formData.append('mimetype', file.mimetype);
31184         }
31185         
31186         if(this.fireEvent('arrange', this, formData) != false){
31187             this.xhr.send(formData);
31188         };
31189     },
31190     
31191     xhrOnLoad : function(xhr)
31192     {
31193         if(this.loadMask){
31194             this.maskEl.unmask();
31195         }
31196         
31197         if (xhr.readyState !== 4) {
31198             this.fireEvent('exception', this, xhr);
31199             return;
31200         }
31201
31202         var response = Roo.decode(xhr.responseText);
31203         
31204         if(!response.success){
31205             this.fireEvent('exception', this, xhr);
31206             return;
31207         }
31208         
31209         var response = Roo.decode(xhr.responseText);
31210         
31211         this.fireEvent('upload', this, response);
31212         
31213     },
31214     
31215     xhrOnError : function()
31216     {
31217         if(this.loadMask){
31218             this.maskEl.unmask();
31219         }
31220         
31221         Roo.log('xhr on error');
31222         
31223         var response = Roo.decode(xhr.responseText);
31224           
31225         Roo.log(response);
31226         
31227     },
31228     
31229     prepare : function(file)
31230     {   
31231         if(this.loadMask){
31232             this.maskEl.mask(this.loadingText);
31233         }
31234         
31235         this.file = false;
31236         this.exif = {};
31237         
31238         if(typeof(file) === 'string'){
31239             this.loadCanvas(file);
31240             return;
31241         }
31242         
31243         if(!file || !this.urlAPI){
31244             return;
31245         }
31246         
31247         this.file = file;
31248         this.cropType = file.type;
31249         
31250         var _this = this;
31251         
31252         if(this.fireEvent('prepare', this, this.file) != false){
31253             
31254             var reader = new FileReader();
31255             
31256             reader.onload = function (e) {
31257                 if (e.target.error) {
31258                     Roo.log(e.target.error);
31259                     return;
31260                 }
31261                 
31262                 var buffer = e.target.result,
31263                     dataView = new DataView(buffer),
31264                     offset = 2,
31265                     maxOffset = dataView.byteLength - 4,
31266                     markerBytes,
31267                     markerLength;
31268                 
31269                 if (dataView.getUint16(0) === 0xffd8) {
31270                     while (offset < maxOffset) {
31271                         markerBytes = dataView.getUint16(offset);
31272                         
31273                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31274                             markerLength = dataView.getUint16(offset + 2) + 2;
31275                             if (offset + markerLength > dataView.byteLength) {
31276                                 Roo.log('Invalid meta data: Invalid segment size.');
31277                                 break;
31278                             }
31279                             
31280                             if(markerBytes == 0xffe1){
31281                                 _this.parseExifData(
31282                                     dataView,
31283                                     offset,
31284                                     markerLength
31285                                 );
31286                             }
31287                             
31288                             offset += markerLength;
31289                             
31290                             continue;
31291                         }
31292                         
31293                         break;
31294                     }
31295                     
31296                 }
31297                 
31298                 var url = _this.urlAPI.createObjectURL(_this.file);
31299                 
31300                 _this.loadCanvas(url);
31301                 
31302                 return;
31303             }
31304             
31305             reader.readAsArrayBuffer(this.file);
31306             
31307         }
31308         
31309     },
31310     
31311     parseExifData : function(dataView, offset, length)
31312     {
31313         var tiffOffset = offset + 10,
31314             littleEndian,
31315             dirOffset;
31316     
31317         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31318             // No Exif data, might be XMP data instead
31319             return;
31320         }
31321         
31322         // Check for the ASCII code for "Exif" (0x45786966):
31323         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31324             // No Exif data, might be XMP data instead
31325             return;
31326         }
31327         if (tiffOffset + 8 > dataView.byteLength) {
31328             Roo.log('Invalid Exif data: Invalid segment size.');
31329             return;
31330         }
31331         // Check for the two null bytes:
31332         if (dataView.getUint16(offset + 8) !== 0x0000) {
31333             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31334             return;
31335         }
31336         // Check the byte alignment:
31337         switch (dataView.getUint16(tiffOffset)) {
31338         case 0x4949:
31339             littleEndian = true;
31340             break;
31341         case 0x4D4D:
31342             littleEndian = false;
31343             break;
31344         default:
31345             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31346             return;
31347         }
31348         // Check for the TIFF tag marker (0x002A):
31349         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31350             Roo.log('Invalid Exif data: Missing TIFF marker.');
31351             return;
31352         }
31353         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31354         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31355         
31356         this.parseExifTags(
31357             dataView,
31358             tiffOffset,
31359             tiffOffset + dirOffset,
31360             littleEndian
31361         );
31362     },
31363     
31364     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31365     {
31366         var tagsNumber,
31367             dirEndOffset,
31368             i;
31369         if (dirOffset + 6 > dataView.byteLength) {
31370             Roo.log('Invalid Exif data: Invalid directory offset.');
31371             return;
31372         }
31373         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31374         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31375         if (dirEndOffset + 4 > dataView.byteLength) {
31376             Roo.log('Invalid Exif data: Invalid directory size.');
31377             return;
31378         }
31379         for (i = 0; i < tagsNumber; i += 1) {
31380             this.parseExifTag(
31381                 dataView,
31382                 tiffOffset,
31383                 dirOffset + 2 + 12 * i, // tag offset
31384                 littleEndian
31385             );
31386         }
31387         // Return the offset to the next directory:
31388         return dataView.getUint32(dirEndOffset, littleEndian);
31389     },
31390     
31391     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31392     {
31393         var tag = dataView.getUint16(offset, littleEndian);
31394         
31395         this.exif[tag] = this.getExifValue(
31396             dataView,
31397             tiffOffset,
31398             offset,
31399             dataView.getUint16(offset + 2, littleEndian), // tag type
31400             dataView.getUint32(offset + 4, littleEndian), // tag length
31401             littleEndian
31402         );
31403     },
31404     
31405     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31406     {
31407         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31408             tagSize,
31409             dataOffset,
31410             values,
31411             i,
31412             str,
31413             c;
31414     
31415         if (!tagType) {
31416             Roo.log('Invalid Exif data: Invalid tag type.');
31417             return;
31418         }
31419         
31420         tagSize = tagType.size * length;
31421         // Determine if the value is contained in the dataOffset bytes,
31422         // or if the value at the dataOffset is a pointer to the actual data:
31423         dataOffset = tagSize > 4 ?
31424                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31425         if (dataOffset + tagSize > dataView.byteLength) {
31426             Roo.log('Invalid Exif data: Invalid data offset.');
31427             return;
31428         }
31429         if (length === 1) {
31430             return tagType.getValue(dataView, dataOffset, littleEndian);
31431         }
31432         values = [];
31433         for (i = 0; i < length; i += 1) {
31434             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31435         }
31436         
31437         if (tagType.ascii) {
31438             str = '';
31439             // Concatenate the chars:
31440             for (i = 0; i < values.length; i += 1) {
31441                 c = values[i];
31442                 // Ignore the terminating NULL byte(s):
31443                 if (c === '\u0000') {
31444                     break;
31445                 }
31446                 str += c;
31447             }
31448             return str;
31449         }
31450         return values;
31451     }
31452     
31453 });
31454
31455 Roo.apply(Roo.bootstrap.UploadCropbox, {
31456     tags : {
31457         'Orientation': 0x0112
31458     },
31459     
31460     Orientation: {
31461             1: 0, //'top-left',
31462 //            2: 'top-right',
31463             3: 180, //'bottom-right',
31464 //            4: 'bottom-left',
31465 //            5: 'left-top',
31466             6: 90, //'right-top',
31467 //            7: 'right-bottom',
31468             8: 270 //'left-bottom'
31469     },
31470     
31471     exifTagTypes : {
31472         // byte, 8-bit unsigned int:
31473         1: {
31474             getValue: function (dataView, dataOffset) {
31475                 return dataView.getUint8(dataOffset);
31476             },
31477             size: 1
31478         },
31479         // ascii, 8-bit byte:
31480         2: {
31481             getValue: function (dataView, dataOffset) {
31482                 return String.fromCharCode(dataView.getUint8(dataOffset));
31483             },
31484             size: 1,
31485             ascii: true
31486         },
31487         // short, 16 bit int:
31488         3: {
31489             getValue: function (dataView, dataOffset, littleEndian) {
31490                 return dataView.getUint16(dataOffset, littleEndian);
31491             },
31492             size: 2
31493         },
31494         // long, 32 bit int:
31495         4: {
31496             getValue: function (dataView, dataOffset, littleEndian) {
31497                 return dataView.getUint32(dataOffset, littleEndian);
31498             },
31499             size: 4
31500         },
31501         // rational = two long values, first is numerator, second is denominator:
31502         5: {
31503             getValue: function (dataView, dataOffset, littleEndian) {
31504                 return dataView.getUint32(dataOffset, littleEndian) /
31505                     dataView.getUint32(dataOffset + 4, littleEndian);
31506             },
31507             size: 8
31508         },
31509         // slong, 32 bit signed int:
31510         9: {
31511             getValue: function (dataView, dataOffset, littleEndian) {
31512                 return dataView.getInt32(dataOffset, littleEndian);
31513             },
31514             size: 4
31515         },
31516         // srational, two slongs, first is numerator, second is denominator:
31517         10: {
31518             getValue: function (dataView, dataOffset, littleEndian) {
31519                 return dataView.getInt32(dataOffset, littleEndian) /
31520                     dataView.getInt32(dataOffset + 4, littleEndian);
31521             },
31522             size: 8
31523         }
31524     },
31525     
31526     footer : {
31527         STANDARD : [
31528             {
31529                 tag : 'div',
31530                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31531                 action : 'rotate-left',
31532                 cn : [
31533                     {
31534                         tag : 'button',
31535                         cls : 'btn btn-default',
31536                         html : '<i class="fa fa-undo"></i>'
31537                     }
31538                 ]
31539             },
31540             {
31541                 tag : 'div',
31542                 cls : 'btn-group roo-upload-cropbox-picture',
31543                 action : 'picture',
31544                 cn : [
31545                     {
31546                         tag : 'button',
31547                         cls : 'btn btn-default',
31548                         html : '<i class="fa fa-picture-o"></i>'
31549                     }
31550                 ]
31551             },
31552             {
31553                 tag : 'div',
31554                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31555                 action : 'rotate-right',
31556                 cn : [
31557                     {
31558                         tag : 'button',
31559                         cls : 'btn btn-default',
31560                         html : '<i class="fa fa-repeat"></i>'
31561                     }
31562                 ]
31563             }
31564         ],
31565         DOCUMENT : [
31566             {
31567                 tag : 'div',
31568                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31569                 action : 'rotate-left',
31570                 cn : [
31571                     {
31572                         tag : 'button',
31573                         cls : 'btn btn-default',
31574                         html : '<i class="fa fa-undo"></i>'
31575                     }
31576                 ]
31577             },
31578             {
31579                 tag : 'div',
31580                 cls : 'btn-group roo-upload-cropbox-download',
31581                 action : 'download',
31582                 cn : [
31583                     {
31584                         tag : 'button',
31585                         cls : 'btn btn-default',
31586                         html : '<i class="fa fa-download"></i>'
31587                     }
31588                 ]
31589             },
31590             {
31591                 tag : 'div',
31592                 cls : 'btn-group roo-upload-cropbox-crop',
31593                 action : 'crop',
31594                 cn : [
31595                     {
31596                         tag : 'button',
31597                         cls : 'btn btn-default',
31598                         html : '<i class="fa fa-crop"></i>'
31599                     }
31600                 ]
31601             },
31602             {
31603                 tag : 'div',
31604                 cls : 'btn-group roo-upload-cropbox-trash',
31605                 action : 'trash',
31606                 cn : [
31607                     {
31608                         tag : 'button',
31609                         cls : 'btn btn-default',
31610                         html : '<i class="fa fa-trash"></i>'
31611                     }
31612                 ]
31613             },
31614             {
31615                 tag : 'div',
31616                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31617                 action : 'rotate-right',
31618                 cn : [
31619                     {
31620                         tag : 'button',
31621                         cls : 'btn btn-default',
31622                         html : '<i class="fa fa-repeat"></i>'
31623                     }
31624                 ]
31625             }
31626         ],
31627         ROTATOR : [
31628             {
31629                 tag : 'div',
31630                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31631                 action : 'rotate-left',
31632                 cn : [
31633                     {
31634                         tag : 'button',
31635                         cls : 'btn btn-default',
31636                         html : '<i class="fa fa-undo"></i>'
31637                     }
31638                 ]
31639             },
31640             {
31641                 tag : 'div',
31642                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31643                 action : 'rotate-right',
31644                 cn : [
31645                     {
31646                         tag : 'button',
31647                         cls : 'btn btn-default',
31648                         html : '<i class="fa fa-repeat"></i>'
31649                     }
31650                 ]
31651             }
31652         ]
31653     }
31654 });
31655
31656 /*
31657 * Licence: LGPL
31658 */
31659
31660 /**
31661  * @class Roo.bootstrap.DocumentManager
31662  * @extends Roo.bootstrap.Component
31663  * Bootstrap DocumentManager class
31664  * @cfg {String} paramName default 'imageUpload'
31665  * @cfg {String} toolTipName default 'filename'
31666  * @cfg {String} method default POST
31667  * @cfg {String} url action url
31668  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31669  * @cfg {Boolean} multiple multiple upload default true
31670  * @cfg {Number} thumbSize default 300
31671  * @cfg {String} fieldLabel
31672  * @cfg {Number} labelWidth default 4
31673  * @cfg {String} labelAlign (left|top) default left
31674  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31675 * @cfg {Number} labellg set the width of label (1-12)
31676  * @cfg {Number} labelmd set the width of label (1-12)
31677  * @cfg {Number} labelsm set the width of label (1-12)
31678  * @cfg {Number} labelxs set the width of label (1-12)
31679  * 
31680  * @constructor
31681  * Create a new DocumentManager
31682  * @param {Object} config The config object
31683  */
31684
31685 Roo.bootstrap.DocumentManager = function(config){
31686     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31687     
31688     this.files = [];
31689     this.delegates = [];
31690     
31691     this.addEvents({
31692         /**
31693          * @event initial
31694          * Fire when initial the DocumentManager
31695          * @param {Roo.bootstrap.DocumentManager} this
31696          */
31697         "initial" : true,
31698         /**
31699          * @event inspect
31700          * inspect selected file
31701          * @param {Roo.bootstrap.DocumentManager} this
31702          * @param {File} file
31703          */
31704         "inspect" : true,
31705         /**
31706          * @event exception
31707          * Fire when xhr load exception
31708          * @param {Roo.bootstrap.DocumentManager} this
31709          * @param {XMLHttpRequest} xhr
31710          */
31711         "exception" : true,
31712         /**
31713          * @event afterupload
31714          * Fire when xhr load exception
31715          * @param {Roo.bootstrap.DocumentManager} this
31716          * @param {XMLHttpRequest} xhr
31717          */
31718         "afterupload" : true,
31719         /**
31720          * @event prepare
31721          * prepare the form data
31722          * @param {Roo.bootstrap.DocumentManager} this
31723          * @param {Object} formData
31724          */
31725         "prepare" : true,
31726         /**
31727          * @event remove
31728          * Fire when remove the file
31729          * @param {Roo.bootstrap.DocumentManager} this
31730          * @param {Object} file
31731          */
31732         "remove" : true,
31733         /**
31734          * @event refresh
31735          * Fire after refresh the file
31736          * @param {Roo.bootstrap.DocumentManager} this
31737          */
31738         "refresh" : true,
31739         /**
31740          * @event click
31741          * Fire after click the image
31742          * @param {Roo.bootstrap.DocumentManager} this
31743          * @param {Object} file
31744          */
31745         "click" : true,
31746         /**
31747          * @event edit
31748          * Fire when upload a image and editable set to true
31749          * @param {Roo.bootstrap.DocumentManager} this
31750          * @param {Object} file
31751          */
31752         "edit" : true,
31753         /**
31754          * @event beforeselectfile
31755          * Fire before select file
31756          * @param {Roo.bootstrap.DocumentManager} this
31757          */
31758         "beforeselectfile" : true,
31759         /**
31760          * @event process
31761          * Fire before process file
31762          * @param {Roo.bootstrap.DocumentManager} this
31763          * @param {Object} file
31764          */
31765         "process" : true,
31766         /**
31767          * @event previewrendered
31768          * Fire when preview rendered
31769          * @param {Roo.bootstrap.DocumentManager} this
31770          * @param {Object} file
31771          */
31772         "previewrendered" : true,
31773         /**
31774          */
31775         "previewResize" : true
31776         
31777     });
31778 };
31779
31780 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31781     
31782     boxes : 0,
31783     inputName : '',
31784     thumbSize : 300,
31785     multiple : true,
31786     files : false,
31787     method : 'POST',
31788     url : '',
31789     paramName : 'imageUpload',
31790     toolTipName : 'filename',
31791     fieldLabel : '',
31792     labelWidth : 4,
31793     labelAlign : 'left',
31794     editable : true,
31795     delegates : false,
31796     xhr : false, 
31797     
31798     labellg : 0,
31799     labelmd : 0,
31800     labelsm : 0,
31801     labelxs : 0,
31802     
31803     getAutoCreate : function()
31804     {   
31805         var managerWidget = {
31806             tag : 'div',
31807             cls : 'roo-document-manager',
31808             cn : [
31809                 {
31810                     tag : 'input',
31811                     cls : 'roo-document-manager-selector',
31812                     type : 'file'
31813                 },
31814                 {
31815                     tag : 'div',
31816                     cls : 'roo-document-manager-uploader',
31817                     cn : [
31818                         {
31819                             tag : 'div',
31820                             cls : 'roo-document-manager-upload-btn',
31821                             html : '<i class="fa fa-plus"></i>'
31822                         }
31823                     ]
31824                     
31825                 }
31826             ]
31827         };
31828         
31829         var content = [
31830             {
31831                 tag : 'div',
31832                 cls : 'column col-md-12',
31833                 cn : managerWidget
31834             }
31835         ];
31836         
31837         if(this.fieldLabel.length){
31838             
31839             content = [
31840                 {
31841                     tag : 'div',
31842                     cls : 'column col-md-12',
31843                     html : this.fieldLabel
31844                 },
31845                 {
31846                     tag : 'div',
31847                     cls : 'column col-md-12',
31848                     cn : managerWidget
31849                 }
31850             ];
31851
31852             if(this.labelAlign == 'left'){
31853                 content = [
31854                     {
31855                         tag : 'div',
31856                         cls : 'column',
31857                         html : this.fieldLabel
31858                     },
31859                     {
31860                         tag : 'div',
31861                         cls : 'column',
31862                         cn : managerWidget
31863                     }
31864                 ];
31865                 
31866                 if(this.labelWidth > 12){
31867                     content[0].style = "width: " + this.labelWidth + 'px';
31868                 }
31869
31870                 if(this.labelWidth < 13 && this.labelmd == 0){
31871                     this.labelmd = this.labelWidth;
31872                 }
31873
31874                 if(this.labellg > 0){
31875                     content[0].cls += ' col-lg-' + this.labellg;
31876                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31877                 }
31878
31879                 if(this.labelmd > 0){
31880                     content[0].cls += ' col-md-' + this.labelmd;
31881                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31882                 }
31883
31884                 if(this.labelsm > 0){
31885                     content[0].cls += ' col-sm-' + this.labelsm;
31886                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31887                 }
31888
31889                 if(this.labelxs > 0){
31890                     content[0].cls += ' col-xs-' + this.labelxs;
31891                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31892                 }
31893                 
31894             }
31895         }
31896         
31897         var cfg = {
31898             tag : 'div',
31899             cls : 'row clearfix',
31900             cn : content
31901         };
31902         
31903         return cfg;
31904         
31905     },
31906     
31907     initEvents : function()
31908     {
31909         this.managerEl = this.el.select('.roo-document-manager', true).first();
31910         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31911         
31912         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31913         this.selectorEl.hide();
31914         
31915         if(this.multiple){
31916             this.selectorEl.attr('multiple', 'multiple');
31917         }
31918         
31919         this.selectorEl.on('change', this.onFileSelected, this);
31920         
31921         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31922         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31923         
31924         this.uploader.on('click', this.onUploaderClick, this);
31925         
31926         this.renderProgressDialog();
31927         
31928         var _this = this;
31929         
31930         window.addEventListener("resize", function() { _this.refresh(); } );
31931         
31932         this.fireEvent('initial', this);
31933     },
31934     
31935     renderProgressDialog : function()
31936     {
31937         var _this = this;
31938         
31939         this.progressDialog = new Roo.bootstrap.Modal({
31940             cls : 'roo-document-manager-progress-dialog',
31941             allow_close : false,
31942             animate : false,
31943             title : '',
31944             buttons : [
31945                 {
31946                     name  :'cancel',
31947                     weight : 'danger',
31948                     html : 'Cancel'
31949                 }
31950             ], 
31951             listeners : { 
31952                 btnclick : function() {
31953                     _this.uploadCancel();
31954                     this.hide();
31955                 }
31956             }
31957         });
31958          
31959         this.progressDialog.render(Roo.get(document.body));
31960          
31961         this.progress = new Roo.bootstrap.Progress({
31962             cls : 'roo-document-manager-progress',
31963             active : true,
31964             striped : true
31965         });
31966         
31967         this.progress.render(this.progressDialog.getChildContainer());
31968         
31969         this.progressBar = new Roo.bootstrap.ProgressBar({
31970             cls : 'roo-document-manager-progress-bar',
31971             aria_valuenow : 0,
31972             aria_valuemin : 0,
31973             aria_valuemax : 12,
31974             panel : 'success'
31975         });
31976         
31977         this.progressBar.render(this.progress.getChildContainer());
31978     },
31979     
31980     onUploaderClick : function(e)
31981     {
31982         e.preventDefault();
31983      
31984         if(this.fireEvent('beforeselectfile', this) != false){
31985             this.selectorEl.dom.click();
31986         }
31987         
31988     },
31989     
31990     onFileSelected : function(e)
31991     {
31992         e.preventDefault();
31993         
31994         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31995             return;
31996         }
31997         
31998         Roo.each(this.selectorEl.dom.files, function(file){
31999             if(this.fireEvent('inspect', this, file) != false){
32000                 this.files.push(file);
32001             }
32002         }, this);
32003         
32004         this.queue();
32005         
32006     },
32007     
32008     queue : function()
32009     {
32010         this.selectorEl.dom.value = '';
32011         
32012         if(!this.files || !this.files.length){
32013             return;
32014         }
32015         
32016         if(this.boxes > 0 && this.files.length > this.boxes){
32017             this.files = this.files.slice(0, this.boxes);
32018         }
32019         
32020         this.uploader.show();
32021         
32022         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32023             this.uploader.hide();
32024         }
32025         
32026         var _this = this;
32027         
32028         var files = [];
32029         
32030         var docs = [];
32031         
32032         Roo.each(this.files, function(file){
32033             
32034             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32035                 var f = this.renderPreview(file);
32036                 files.push(f);
32037                 return;
32038             }
32039             
32040             if(file.type.indexOf('image') != -1){
32041                 this.delegates.push(
32042                     (function(){
32043                         _this.process(file);
32044                     }).createDelegate(this)
32045                 );
32046         
32047                 return;
32048             }
32049             
32050             docs.push(
32051                 (function(){
32052                     _this.process(file);
32053                 }).createDelegate(this)
32054             );
32055             
32056         }, this);
32057         
32058         this.files = files;
32059         
32060         this.delegates = this.delegates.concat(docs);
32061         
32062         if(!this.delegates.length){
32063             this.refresh();
32064             return;
32065         }
32066         
32067         this.progressBar.aria_valuemax = this.delegates.length;
32068         
32069         this.arrange();
32070         
32071         return;
32072     },
32073     
32074     arrange : function()
32075     {
32076         if(!this.delegates.length){
32077             this.progressDialog.hide();
32078             this.refresh();
32079             return;
32080         }
32081         
32082         var delegate = this.delegates.shift();
32083         
32084         this.progressDialog.show();
32085         
32086         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32087         
32088         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32089         
32090         delegate();
32091     },
32092     
32093     refresh : function()
32094     {
32095         this.uploader.show();
32096         
32097         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32098             this.uploader.hide();
32099         }
32100         
32101         Roo.isTouch ? this.closable(false) : this.closable(true);
32102         
32103         this.fireEvent('refresh', this);
32104     },
32105     
32106     onRemove : function(e, el, o)
32107     {
32108         e.preventDefault();
32109         
32110         this.fireEvent('remove', this, o);
32111         
32112     },
32113     
32114     remove : function(o)
32115     {
32116         var files = [];
32117         
32118         Roo.each(this.files, function(file){
32119             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32120                 files.push(file);
32121                 return;
32122             }
32123
32124             o.target.remove();
32125
32126         }, this);
32127         
32128         this.files = files;
32129         
32130         this.refresh();
32131     },
32132     
32133     clear : function()
32134     {
32135         Roo.each(this.files, function(file){
32136             if(!file.target){
32137                 return;
32138             }
32139             
32140             file.target.remove();
32141
32142         }, this);
32143         
32144         this.files = [];
32145         
32146         this.refresh();
32147     },
32148     
32149     onClick : function(e, el, o)
32150     {
32151         e.preventDefault();
32152         
32153         this.fireEvent('click', this, o);
32154         
32155     },
32156     
32157     closable : function(closable)
32158     {
32159         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32160             
32161             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32162             
32163             if(closable){
32164                 el.show();
32165                 return;
32166             }
32167             
32168             el.hide();
32169             
32170         }, this);
32171     },
32172     
32173     xhrOnLoad : function(xhr)
32174     {
32175         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32176             el.remove();
32177         }, this);
32178         
32179         if (xhr.readyState !== 4) {
32180             this.arrange();
32181             this.fireEvent('exception', this, xhr);
32182             return;
32183         }
32184
32185         var response = Roo.decode(xhr.responseText);
32186         
32187         if(!response.success){
32188             this.arrange();
32189             this.fireEvent('exception', this, xhr);
32190             return;
32191         }
32192         
32193         var file = this.renderPreview(response.data);
32194         
32195         this.files.push(file);
32196         
32197         this.arrange();
32198         
32199         this.fireEvent('afterupload', this, xhr);
32200         
32201     },
32202     
32203     xhrOnError : function(xhr)
32204     {
32205         Roo.log('xhr on error');
32206         
32207         var response = Roo.decode(xhr.responseText);
32208           
32209         Roo.log(response);
32210         
32211         this.arrange();
32212     },
32213     
32214     process : function(file)
32215     {
32216         if(this.fireEvent('process', this, file) !== false){
32217             if(this.editable && file.type.indexOf('image') != -1){
32218                 this.fireEvent('edit', this, file);
32219                 return;
32220             }
32221
32222             this.uploadStart(file, false);
32223
32224             return;
32225         }
32226         
32227     },
32228     
32229     uploadStart : function(file, crop)
32230     {
32231         this.xhr = new XMLHttpRequest();
32232         
32233         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32234             this.arrange();
32235             return;
32236         }
32237         
32238         file.xhr = this.xhr;
32239             
32240         this.managerEl.createChild({
32241             tag : 'div',
32242             cls : 'roo-document-manager-loading',
32243             cn : [
32244                 {
32245                     tag : 'div',
32246                     tooltip : file.name,
32247                     cls : 'roo-document-manager-thumb',
32248                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32249                 }
32250             ]
32251
32252         });
32253
32254         this.xhr.open(this.method, this.url, true);
32255         
32256         var headers = {
32257             "Accept": "application/json",
32258             "Cache-Control": "no-cache",
32259             "X-Requested-With": "XMLHttpRequest"
32260         };
32261         
32262         for (var headerName in headers) {
32263             var headerValue = headers[headerName];
32264             if (headerValue) {
32265                 this.xhr.setRequestHeader(headerName, headerValue);
32266             }
32267         }
32268         
32269         var _this = this;
32270         
32271         this.xhr.onload = function()
32272         {
32273             _this.xhrOnLoad(_this.xhr);
32274         }
32275         
32276         this.xhr.onerror = function()
32277         {
32278             _this.xhrOnError(_this.xhr);
32279         }
32280         
32281         var formData = new FormData();
32282
32283         formData.append('returnHTML', 'NO');
32284         
32285         if(crop){
32286             formData.append('crop', crop);
32287         }
32288         
32289         formData.append(this.paramName, file, file.name);
32290         
32291         var options = {
32292             file : file, 
32293             manually : false
32294         };
32295         
32296         if(this.fireEvent('prepare', this, formData, options) != false){
32297             
32298             if(options.manually){
32299                 return;
32300             }
32301             
32302             this.xhr.send(formData);
32303             return;
32304         };
32305         
32306         this.uploadCancel();
32307     },
32308     
32309     uploadCancel : function()
32310     {
32311         if (this.xhr) {
32312             this.xhr.abort();
32313         }
32314         
32315         this.delegates = [];
32316         
32317         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32318             el.remove();
32319         }, this);
32320         
32321         this.arrange();
32322     },
32323     
32324     renderPreview : function(file)
32325     {
32326         if(typeof(file.target) != 'undefined' && file.target){
32327             return file;
32328         }
32329         
32330         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32331         
32332         var previewEl = this.managerEl.createChild({
32333             tag : 'div',
32334             cls : 'roo-document-manager-preview',
32335             cn : [
32336                 {
32337                     tag : 'div',
32338                     tooltip : file[this.toolTipName],
32339                     cls : 'roo-document-manager-thumb',
32340                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32341                 },
32342                 {
32343                     tag : 'button',
32344                     cls : 'close',
32345                     html : '<i class="fa fa-times-circle"></i>'
32346                 }
32347             ]
32348         });
32349
32350         var close = previewEl.select('button.close', true).first();
32351
32352         close.on('click', this.onRemove, this, file);
32353
32354         file.target = previewEl;
32355
32356         var image = previewEl.select('img', true).first();
32357         
32358         var _this = this;
32359         
32360         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32361         
32362         image.on('click', this.onClick, this, file);
32363         
32364         this.fireEvent('previewrendered', this, file);
32365         
32366         return file;
32367         
32368     },
32369     
32370     onPreviewLoad : function(file, image)
32371     {
32372         if(typeof(file.target) == 'undefined' || !file.target){
32373             return;
32374         }
32375         
32376         var width = image.dom.naturalWidth || image.dom.width;
32377         var height = image.dom.naturalHeight || image.dom.height;
32378         
32379         if(!this.previewResize) {
32380             return;
32381         }
32382         
32383         if(width > height){
32384             file.target.addClass('wide');
32385             return;
32386         }
32387         
32388         file.target.addClass('tall');
32389         return;
32390         
32391     },
32392     
32393     uploadFromSource : function(file, crop)
32394     {
32395         this.xhr = new XMLHttpRequest();
32396         
32397         this.managerEl.createChild({
32398             tag : 'div',
32399             cls : 'roo-document-manager-loading',
32400             cn : [
32401                 {
32402                     tag : 'div',
32403                     tooltip : file.name,
32404                     cls : 'roo-document-manager-thumb',
32405                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32406                 }
32407             ]
32408
32409         });
32410
32411         this.xhr.open(this.method, this.url, true);
32412         
32413         var headers = {
32414             "Accept": "application/json",
32415             "Cache-Control": "no-cache",
32416             "X-Requested-With": "XMLHttpRequest"
32417         };
32418         
32419         for (var headerName in headers) {
32420             var headerValue = headers[headerName];
32421             if (headerValue) {
32422                 this.xhr.setRequestHeader(headerName, headerValue);
32423             }
32424         }
32425         
32426         var _this = this;
32427         
32428         this.xhr.onload = function()
32429         {
32430             _this.xhrOnLoad(_this.xhr);
32431         }
32432         
32433         this.xhr.onerror = function()
32434         {
32435             _this.xhrOnError(_this.xhr);
32436         }
32437         
32438         var formData = new FormData();
32439
32440         formData.append('returnHTML', 'NO');
32441         
32442         formData.append('crop', crop);
32443         
32444         if(typeof(file.filename) != 'undefined'){
32445             formData.append('filename', file.filename);
32446         }
32447         
32448         if(typeof(file.mimetype) != 'undefined'){
32449             formData.append('mimetype', file.mimetype);
32450         }
32451         
32452         Roo.log(formData);
32453         
32454         if(this.fireEvent('prepare', this, formData) != false){
32455             this.xhr.send(formData);
32456         };
32457     }
32458 });
32459
32460 /*
32461 * Licence: LGPL
32462 */
32463
32464 /**
32465  * @class Roo.bootstrap.DocumentViewer
32466  * @extends Roo.bootstrap.Component
32467  * Bootstrap DocumentViewer class
32468  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32469  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32470  * 
32471  * @constructor
32472  * Create a new DocumentViewer
32473  * @param {Object} config The config object
32474  */
32475
32476 Roo.bootstrap.DocumentViewer = function(config){
32477     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32478     
32479     this.addEvents({
32480         /**
32481          * @event initial
32482          * Fire after initEvent
32483          * @param {Roo.bootstrap.DocumentViewer} this
32484          */
32485         "initial" : true,
32486         /**
32487          * @event click
32488          * Fire after click
32489          * @param {Roo.bootstrap.DocumentViewer} this
32490          */
32491         "click" : true,
32492         /**
32493          * @event download
32494          * Fire after download button
32495          * @param {Roo.bootstrap.DocumentViewer} this
32496          */
32497         "download" : true,
32498         /**
32499          * @event trash
32500          * Fire after trash button
32501          * @param {Roo.bootstrap.DocumentViewer} this
32502          */
32503         "trash" : true
32504         
32505     });
32506 };
32507
32508 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32509     
32510     showDownload : true,
32511     
32512     showTrash : true,
32513     
32514     getAutoCreate : function()
32515     {
32516         var cfg = {
32517             tag : 'div',
32518             cls : 'roo-document-viewer',
32519             cn : [
32520                 {
32521                     tag : 'div',
32522                     cls : 'roo-document-viewer-body',
32523                     cn : [
32524                         {
32525                             tag : 'div',
32526                             cls : 'roo-document-viewer-thumb',
32527                             cn : [
32528                                 {
32529                                     tag : 'img',
32530                                     cls : 'roo-document-viewer-image'
32531                                 }
32532                             ]
32533                         }
32534                     ]
32535                 },
32536                 {
32537                     tag : 'div',
32538                     cls : 'roo-document-viewer-footer',
32539                     cn : {
32540                         tag : 'div',
32541                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32542                         cn : [
32543                             {
32544                                 tag : 'div',
32545                                 cls : 'btn-group roo-document-viewer-download',
32546                                 cn : [
32547                                     {
32548                                         tag : 'button',
32549                                         cls : 'btn btn-default',
32550                                         html : '<i class="fa fa-download"></i>'
32551                                     }
32552                                 ]
32553                             },
32554                             {
32555                                 tag : 'div',
32556                                 cls : 'btn-group roo-document-viewer-trash',
32557                                 cn : [
32558                                     {
32559                                         tag : 'button',
32560                                         cls : 'btn btn-default',
32561                                         html : '<i class="fa fa-trash"></i>'
32562                                     }
32563                                 ]
32564                             }
32565                         ]
32566                     }
32567                 }
32568             ]
32569         };
32570         
32571         return cfg;
32572     },
32573     
32574     initEvents : function()
32575     {
32576         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32577         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32578         
32579         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32580         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32581         
32582         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32583         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32584         
32585         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32586         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32587         
32588         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32589         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32590         
32591         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32592         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32593         
32594         this.bodyEl.on('click', this.onClick, this);
32595         this.downloadBtn.on('click', this.onDownload, this);
32596         this.trashBtn.on('click', this.onTrash, this);
32597         
32598         this.downloadBtn.hide();
32599         this.trashBtn.hide();
32600         
32601         if(this.showDownload){
32602             this.downloadBtn.show();
32603         }
32604         
32605         if(this.showTrash){
32606             this.trashBtn.show();
32607         }
32608         
32609         if(!this.showDownload && !this.showTrash) {
32610             this.footerEl.hide();
32611         }
32612         
32613     },
32614     
32615     initial : function()
32616     {
32617         this.fireEvent('initial', this);
32618         
32619     },
32620     
32621     onClick : function(e)
32622     {
32623         e.preventDefault();
32624         
32625         this.fireEvent('click', this);
32626     },
32627     
32628     onDownload : function(e)
32629     {
32630         e.preventDefault();
32631         
32632         this.fireEvent('download', this);
32633     },
32634     
32635     onTrash : function(e)
32636     {
32637         e.preventDefault();
32638         
32639         this.fireEvent('trash', this);
32640     }
32641     
32642 });
32643 /*
32644  * - LGPL
32645  *
32646  * nav progress bar
32647  * 
32648  */
32649
32650 /**
32651  * @class Roo.bootstrap.NavProgressBar
32652  * @extends Roo.bootstrap.Component
32653  * Bootstrap NavProgressBar class
32654  * 
32655  * @constructor
32656  * Create a new nav progress bar
32657  * @param {Object} config The config object
32658  */
32659
32660 Roo.bootstrap.NavProgressBar = function(config){
32661     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32662
32663     this.bullets = this.bullets || [];
32664    
32665 //    Roo.bootstrap.NavProgressBar.register(this);
32666      this.addEvents({
32667         /**
32668              * @event changed
32669              * Fires when the active item changes
32670              * @param {Roo.bootstrap.NavProgressBar} this
32671              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32672              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32673          */
32674         'changed': true
32675      });
32676     
32677 };
32678
32679 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32680     
32681     bullets : [],
32682     barItems : [],
32683     
32684     getAutoCreate : function()
32685     {
32686         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32687         
32688         cfg = {
32689             tag : 'div',
32690             cls : 'roo-navigation-bar-group',
32691             cn : [
32692                 {
32693                     tag : 'div',
32694                     cls : 'roo-navigation-top-bar'
32695                 },
32696                 {
32697                     tag : 'div',
32698                     cls : 'roo-navigation-bullets-bar',
32699                     cn : [
32700                         {
32701                             tag : 'ul',
32702                             cls : 'roo-navigation-bar'
32703                         }
32704                     ]
32705                 },
32706                 
32707                 {
32708                     tag : 'div',
32709                     cls : 'roo-navigation-bottom-bar'
32710                 }
32711             ]
32712             
32713         };
32714         
32715         return cfg;
32716         
32717     },
32718     
32719     initEvents: function() 
32720     {
32721         
32722     },
32723     
32724     onRender : function(ct, position) 
32725     {
32726         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32727         
32728         if(this.bullets.length){
32729             Roo.each(this.bullets, function(b){
32730                this.addItem(b);
32731             }, this);
32732         }
32733         
32734         this.format();
32735         
32736     },
32737     
32738     addItem : function(cfg)
32739     {
32740         var item = new Roo.bootstrap.NavProgressItem(cfg);
32741         
32742         item.parentId = this.id;
32743         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32744         
32745         if(cfg.html){
32746             var top = new Roo.bootstrap.Element({
32747                 tag : 'div',
32748                 cls : 'roo-navigation-bar-text'
32749             });
32750             
32751             var bottom = new Roo.bootstrap.Element({
32752                 tag : 'div',
32753                 cls : 'roo-navigation-bar-text'
32754             });
32755             
32756             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32757             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32758             
32759             var topText = new Roo.bootstrap.Element({
32760                 tag : 'span',
32761                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32762             });
32763             
32764             var bottomText = new Roo.bootstrap.Element({
32765                 tag : 'span',
32766                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32767             });
32768             
32769             topText.onRender(top.el, null);
32770             bottomText.onRender(bottom.el, null);
32771             
32772             item.topEl = top;
32773             item.bottomEl = bottom;
32774         }
32775         
32776         this.barItems.push(item);
32777         
32778         return item;
32779     },
32780     
32781     getActive : function()
32782     {
32783         var active = false;
32784         
32785         Roo.each(this.barItems, function(v){
32786             
32787             if (!v.isActive()) {
32788                 return;
32789             }
32790             
32791             active = v;
32792             return false;
32793             
32794         });
32795         
32796         return active;
32797     },
32798     
32799     setActiveItem : function(item)
32800     {
32801         var prev = false;
32802         
32803         Roo.each(this.barItems, function(v){
32804             if (v.rid == item.rid) {
32805                 return ;
32806             }
32807             
32808             if (v.isActive()) {
32809                 v.setActive(false);
32810                 prev = v;
32811             }
32812         });
32813
32814         item.setActive(true);
32815         
32816         this.fireEvent('changed', this, item, prev);
32817     },
32818     
32819     getBarItem: function(rid)
32820     {
32821         var ret = false;
32822         
32823         Roo.each(this.barItems, function(e) {
32824             if (e.rid != rid) {
32825                 return;
32826             }
32827             
32828             ret =  e;
32829             return false;
32830         });
32831         
32832         return ret;
32833     },
32834     
32835     indexOfItem : function(item)
32836     {
32837         var index = false;
32838         
32839         Roo.each(this.barItems, function(v, i){
32840             
32841             if (v.rid != item.rid) {
32842                 return;
32843             }
32844             
32845             index = i;
32846             return false
32847         });
32848         
32849         return index;
32850     },
32851     
32852     setActiveNext : function()
32853     {
32854         var i = this.indexOfItem(this.getActive());
32855         
32856         if (i > this.barItems.length) {
32857             return;
32858         }
32859         
32860         this.setActiveItem(this.barItems[i+1]);
32861     },
32862     
32863     setActivePrev : function()
32864     {
32865         var i = this.indexOfItem(this.getActive());
32866         
32867         if (i  < 1) {
32868             return;
32869         }
32870         
32871         this.setActiveItem(this.barItems[i-1]);
32872     },
32873     
32874     format : function()
32875     {
32876         if(!this.barItems.length){
32877             return;
32878         }
32879      
32880         var width = 100 / this.barItems.length;
32881         
32882         Roo.each(this.barItems, function(i){
32883             i.el.setStyle('width', width + '%');
32884             i.topEl.el.setStyle('width', width + '%');
32885             i.bottomEl.el.setStyle('width', width + '%');
32886         }, this);
32887         
32888     }
32889     
32890 });
32891 /*
32892  * - LGPL
32893  *
32894  * Nav Progress Item
32895  * 
32896  */
32897
32898 /**
32899  * @class Roo.bootstrap.NavProgressItem
32900  * @extends Roo.bootstrap.Component
32901  * Bootstrap NavProgressItem class
32902  * @cfg {String} rid the reference id
32903  * @cfg {Boolean} active (true|false) Is item active default false
32904  * @cfg {Boolean} disabled (true|false) Is item active default false
32905  * @cfg {String} html
32906  * @cfg {String} position (top|bottom) text position default bottom
32907  * @cfg {String} icon show icon instead of number
32908  * 
32909  * @constructor
32910  * Create a new NavProgressItem
32911  * @param {Object} config The config object
32912  */
32913 Roo.bootstrap.NavProgressItem = function(config){
32914     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32915     this.addEvents({
32916         // raw events
32917         /**
32918          * @event click
32919          * The raw click event for the entire grid.
32920          * @param {Roo.bootstrap.NavProgressItem} this
32921          * @param {Roo.EventObject} e
32922          */
32923         "click" : true
32924     });
32925    
32926 };
32927
32928 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32929     
32930     rid : '',
32931     active : false,
32932     disabled : false,
32933     html : '',
32934     position : 'bottom',
32935     icon : false,
32936     
32937     getAutoCreate : function()
32938     {
32939         var iconCls = 'roo-navigation-bar-item-icon';
32940         
32941         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32942         
32943         var cfg = {
32944             tag: 'li',
32945             cls: 'roo-navigation-bar-item',
32946             cn : [
32947                 {
32948                     tag : 'i',
32949                     cls : iconCls
32950                 }
32951             ]
32952         };
32953         
32954         if(this.active){
32955             cfg.cls += ' active';
32956         }
32957         if(this.disabled){
32958             cfg.cls += ' disabled';
32959         }
32960         
32961         return cfg;
32962     },
32963     
32964     disable : function()
32965     {
32966         this.setDisabled(true);
32967     },
32968     
32969     enable : function()
32970     {
32971         this.setDisabled(false);
32972     },
32973     
32974     initEvents: function() 
32975     {
32976         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32977         
32978         this.iconEl.on('click', this.onClick, this);
32979     },
32980     
32981     onClick : function(e)
32982     {
32983         e.preventDefault();
32984         
32985         if(this.disabled){
32986             return;
32987         }
32988         
32989         if(this.fireEvent('click', this, e) === false){
32990             return;
32991         };
32992         
32993         this.parent().setActiveItem(this);
32994     },
32995     
32996     isActive: function () 
32997     {
32998         return this.active;
32999     },
33000     
33001     setActive : function(state)
33002     {
33003         if(this.active == state){
33004             return;
33005         }
33006         
33007         this.active = state;
33008         
33009         if (state) {
33010             this.el.addClass('active');
33011             return;
33012         }
33013         
33014         this.el.removeClass('active');
33015         
33016         return;
33017     },
33018     
33019     setDisabled : function(state)
33020     {
33021         if(this.disabled == state){
33022             return;
33023         }
33024         
33025         this.disabled = state;
33026         
33027         if (state) {
33028             this.el.addClass('disabled');
33029             return;
33030         }
33031         
33032         this.el.removeClass('disabled');
33033     },
33034     
33035     tooltipEl : function()
33036     {
33037         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33038     }
33039 });
33040  
33041
33042  /*
33043  * - LGPL
33044  *
33045  * FieldLabel
33046  * 
33047  */
33048
33049 /**
33050  * @class Roo.bootstrap.FieldLabel
33051  * @extends Roo.bootstrap.Component
33052  * Bootstrap FieldLabel class
33053  * @cfg {String} html contents of the element
33054  * @cfg {String} tag tag of the element default label
33055  * @cfg {String} cls class of the element
33056  * @cfg {String} target label target 
33057  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33058  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33059  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33060  * @cfg {String} iconTooltip default "This field is required"
33061  * @cfg {String} indicatorpos (left|right) default left
33062  * 
33063  * @constructor
33064  * Create a new FieldLabel
33065  * @param {Object} config The config object
33066  */
33067
33068 Roo.bootstrap.FieldLabel = function(config){
33069     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33070     
33071     this.addEvents({
33072             /**
33073              * @event invalid
33074              * Fires after the field has been marked as invalid.
33075              * @param {Roo.form.FieldLabel} this
33076              * @param {String} msg The validation message
33077              */
33078             invalid : true,
33079             /**
33080              * @event valid
33081              * Fires after the field has been validated with no errors.
33082              * @param {Roo.form.FieldLabel} this
33083              */
33084             valid : true
33085         });
33086 };
33087
33088 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33089     
33090     tag: 'label',
33091     cls: '',
33092     html: '',
33093     target: '',
33094     allowBlank : true,
33095     invalidClass : 'has-warning',
33096     validClass : 'has-success',
33097     iconTooltip : 'This field is required',
33098     indicatorpos : 'left',
33099     
33100     getAutoCreate : function(){
33101         
33102         var cls = "";
33103         if (!this.allowBlank) {
33104             cls  = "visible";
33105         }
33106         
33107         var cfg = {
33108             tag : this.tag,
33109             cls : 'roo-bootstrap-field-label ' + this.cls,
33110             for : this.target,
33111             cn : [
33112                 {
33113                     tag : 'i',
33114                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33115                     tooltip : this.iconTooltip
33116                 },
33117                 {
33118                     tag : 'span',
33119                     html : this.html
33120                 }
33121             ] 
33122         };
33123         
33124         if(this.indicatorpos == 'right'){
33125             var cfg = {
33126                 tag : this.tag,
33127                 cls : 'roo-bootstrap-field-label ' + this.cls,
33128                 for : this.target,
33129                 cn : [
33130                     {
33131                         tag : 'span',
33132                         html : this.html
33133                     },
33134                     {
33135                         tag : 'i',
33136                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33137                         tooltip : this.iconTooltip
33138                     }
33139                 ] 
33140             };
33141         }
33142         
33143         return cfg;
33144     },
33145     
33146     initEvents: function() 
33147     {
33148         Roo.bootstrap.Element.superclass.initEvents.call(this);
33149         
33150         this.indicator = this.indicatorEl();
33151         
33152         if(this.indicator){
33153             this.indicator.removeClass('visible');
33154             this.indicator.addClass('invisible');
33155         }
33156         
33157         Roo.bootstrap.FieldLabel.register(this);
33158     },
33159     
33160     indicatorEl : function()
33161     {
33162         var indicator = this.el.select('i.roo-required-indicator',true).first();
33163         
33164         if(!indicator){
33165             return false;
33166         }
33167         
33168         return indicator;
33169         
33170     },
33171     
33172     /**
33173      * Mark this field as valid
33174      */
33175     markValid : function()
33176     {
33177         if(this.indicator){
33178             this.indicator.removeClass('visible');
33179             this.indicator.addClass('invisible');
33180         }
33181         if (Roo.bootstrap.version == 3) {
33182             this.el.removeClass(this.invalidClass);
33183             this.el.addClass(this.validClass);
33184         } else {
33185             this.el.removeClass('is-invalid');
33186             this.el.addClass('is-valid');
33187         }
33188         
33189         
33190         this.fireEvent('valid', this);
33191     },
33192     
33193     /**
33194      * Mark this field as invalid
33195      * @param {String} msg The validation message
33196      */
33197     markInvalid : function(msg)
33198     {
33199         if(this.indicator){
33200             this.indicator.removeClass('invisible');
33201             this.indicator.addClass('visible');
33202         }
33203           if (Roo.bootstrap.version == 3) {
33204             this.el.removeClass(this.validClass);
33205             this.el.addClass(this.invalidClass);
33206         } else {
33207             this.el.removeClass('is-valid');
33208             this.el.addClass('is-invalid');
33209         }
33210         
33211         
33212         this.fireEvent('invalid', this, msg);
33213     }
33214     
33215    
33216 });
33217
33218 Roo.apply(Roo.bootstrap.FieldLabel, {
33219     
33220     groups: {},
33221     
33222      /**
33223     * register a FieldLabel Group
33224     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33225     */
33226     register : function(label)
33227     {
33228         if(this.groups.hasOwnProperty(label.target)){
33229             return;
33230         }
33231      
33232         this.groups[label.target] = label;
33233         
33234     },
33235     /**
33236     * fetch a FieldLabel Group based on the target
33237     * @param {string} target
33238     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33239     */
33240     get: function(target) {
33241         if (typeof(this.groups[target]) == 'undefined') {
33242             return false;
33243         }
33244         
33245         return this.groups[target] ;
33246     }
33247 });
33248
33249  
33250
33251  /*
33252  * - LGPL
33253  *
33254  * page DateSplitField.
33255  * 
33256  */
33257
33258
33259 /**
33260  * @class Roo.bootstrap.DateSplitField
33261  * @extends Roo.bootstrap.Component
33262  * Bootstrap DateSplitField class
33263  * @cfg {string} fieldLabel - the label associated
33264  * @cfg {Number} labelWidth set the width of label (0-12)
33265  * @cfg {String} labelAlign (top|left)
33266  * @cfg {Boolean} dayAllowBlank (true|false) default false
33267  * @cfg {Boolean} monthAllowBlank (true|false) default false
33268  * @cfg {Boolean} yearAllowBlank (true|false) default false
33269  * @cfg {string} dayPlaceholder 
33270  * @cfg {string} monthPlaceholder
33271  * @cfg {string} yearPlaceholder
33272  * @cfg {string} dayFormat default 'd'
33273  * @cfg {string} monthFormat default 'm'
33274  * @cfg {string} yearFormat default 'Y'
33275  * @cfg {Number} labellg set the width of label (1-12)
33276  * @cfg {Number} labelmd set the width of label (1-12)
33277  * @cfg {Number} labelsm set the width of label (1-12)
33278  * @cfg {Number} labelxs set the width of label (1-12)
33279
33280  *     
33281  * @constructor
33282  * Create a new DateSplitField
33283  * @param {Object} config The config object
33284  */
33285
33286 Roo.bootstrap.DateSplitField = function(config){
33287     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33288     
33289     this.addEvents({
33290         // raw events
33291          /**
33292          * @event years
33293          * getting the data of years
33294          * @param {Roo.bootstrap.DateSplitField} this
33295          * @param {Object} years
33296          */
33297         "years" : true,
33298         /**
33299          * @event days
33300          * getting the data of days
33301          * @param {Roo.bootstrap.DateSplitField} this
33302          * @param {Object} days
33303          */
33304         "days" : true,
33305         /**
33306          * @event invalid
33307          * Fires after the field has been marked as invalid.
33308          * @param {Roo.form.Field} this
33309          * @param {String} msg The validation message
33310          */
33311         invalid : true,
33312        /**
33313          * @event valid
33314          * Fires after the field has been validated with no errors.
33315          * @param {Roo.form.Field} this
33316          */
33317         valid : true
33318     });
33319 };
33320
33321 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33322     
33323     fieldLabel : '',
33324     labelAlign : 'top',
33325     labelWidth : 3,
33326     dayAllowBlank : false,
33327     monthAllowBlank : false,
33328     yearAllowBlank : false,
33329     dayPlaceholder : '',
33330     monthPlaceholder : '',
33331     yearPlaceholder : '',
33332     dayFormat : 'd',
33333     monthFormat : 'm',
33334     yearFormat : 'Y',
33335     isFormField : true,
33336     labellg : 0,
33337     labelmd : 0,
33338     labelsm : 0,
33339     labelxs : 0,
33340     
33341     getAutoCreate : function()
33342     {
33343         var cfg = {
33344             tag : 'div',
33345             cls : 'row roo-date-split-field-group',
33346             cn : [
33347                 {
33348                     tag : 'input',
33349                     type : 'hidden',
33350                     cls : 'form-hidden-field roo-date-split-field-group-value',
33351                     name : this.name
33352                 }
33353             ]
33354         };
33355         
33356         var labelCls = 'col-md-12';
33357         var contentCls = 'col-md-4';
33358         
33359         if(this.fieldLabel){
33360             
33361             var label = {
33362                 tag : 'div',
33363                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33364                 cn : [
33365                     {
33366                         tag : 'label',
33367                         html : this.fieldLabel
33368                     }
33369                 ]
33370             };
33371             
33372             if(this.labelAlign == 'left'){
33373             
33374                 if(this.labelWidth > 12){
33375                     label.style = "width: " + this.labelWidth + 'px';
33376                 }
33377
33378                 if(this.labelWidth < 13 && this.labelmd == 0){
33379                     this.labelmd = this.labelWidth;
33380                 }
33381
33382                 if(this.labellg > 0){
33383                     labelCls = ' col-lg-' + this.labellg;
33384                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33385                 }
33386
33387                 if(this.labelmd > 0){
33388                     labelCls = ' col-md-' + this.labelmd;
33389                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33390                 }
33391
33392                 if(this.labelsm > 0){
33393                     labelCls = ' col-sm-' + this.labelsm;
33394                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33395                 }
33396
33397                 if(this.labelxs > 0){
33398                     labelCls = ' col-xs-' + this.labelxs;
33399                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33400                 }
33401             }
33402             
33403             label.cls += ' ' + labelCls;
33404             
33405             cfg.cn.push(label);
33406         }
33407         
33408         Roo.each(['day', 'month', 'year'], function(t){
33409             cfg.cn.push({
33410                 tag : 'div',
33411                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33412             });
33413         }, this);
33414         
33415         return cfg;
33416     },
33417     
33418     inputEl: function ()
33419     {
33420         return this.el.select('.roo-date-split-field-group-value', true).first();
33421     },
33422     
33423     onRender : function(ct, position) 
33424     {
33425         var _this = this;
33426         
33427         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33428         
33429         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33430         
33431         this.dayField = new Roo.bootstrap.ComboBox({
33432             allowBlank : this.dayAllowBlank,
33433             alwaysQuery : true,
33434             displayField : 'value',
33435             editable : false,
33436             fieldLabel : '',
33437             forceSelection : true,
33438             mode : 'local',
33439             placeholder : this.dayPlaceholder,
33440             selectOnFocus : true,
33441             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33442             triggerAction : 'all',
33443             typeAhead : true,
33444             valueField : 'value',
33445             store : new Roo.data.SimpleStore({
33446                 data : (function() {    
33447                     var days = [];
33448                     _this.fireEvent('days', _this, days);
33449                     return days;
33450                 })(),
33451                 fields : [ 'value' ]
33452             }),
33453             listeners : {
33454                 select : function (_self, record, index)
33455                 {
33456                     _this.setValue(_this.getValue());
33457                 }
33458             }
33459         });
33460
33461         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33462         
33463         this.monthField = new Roo.bootstrap.MonthField({
33464             after : '<i class=\"fa fa-calendar\"></i>',
33465             allowBlank : this.monthAllowBlank,
33466             placeholder : this.monthPlaceholder,
33467             readOnly : true,
33468             listeners : {
33469                 render : function (_self)
33470                 {
33471                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33472                         e.preventDefault();
33473                         _self.focus();
33474                     });
33475                 },
33476                 select : function (_self, oldvalue, newvalue)
33477                 {
33478                     _this.setValue(_this.getValue());
33479                 }
33480             }
33481         });
33482         
33483         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33484         
33485         this.yearField = new Roo.bootstrap.ComboBox({
33486             allowBlank : this.yearAllowBlank,
33487             alwaysQuery : true,
33488             displayField : 'value',
33489             editable : false,
33490             fieldLabel : '',
33491             forceSelection : true,
33492             mode : 'local',
33493             placeholder : this.yearPlaceholder,
33494             selectOnFocus : true,
33495             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33496             triggerAction : 'all',
33497             typeAhead : true,
33498             valueField : 'value',
33499             store : new Roo.data.SimpleStore({
33500                 data : (function() {
33501                     var years = [];
33502                     _this.fireEvent('years', _this, years);
33503                     return years;
33504                 })(),
33505                 fields : [ 'value' ]
33506             }),
33507             listeners : {
33508                 select : function (_self, record, index)
33509                 {
33510                     _this.setValue(_this.getValue());
33511                 }
33512             }
33513         });
33514
33515         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33516     },
33517     
33518     setValue : function(v, format)
33519     {
33520         this.inputEl.dom.value = v;
33521         
33522         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33523         
33524         var d = Date.parseDate(v, f);
33525         
33526         if(!d){
33527             this.validate();
33528             return;
33529         }
33530         
33531         this.setDay(d.format(this.dayFormat));
33532         this.setMonth(d.format(this.monthFormat));
33533         this.setYear(d.format(this.yearFormat));
33534         
33535         this.validate();
33536         
33537         return;
33538     },
33539     
33540     setDay : function(v)
33541     {
33542         this.dayField.setValue(v);
33543         this.inputEl.dom.value = this.getValue();
33544         this.validate();
33545         return;
33546     },
33547     
33548     setMonth : function(v)
33549     {
33550         this.monthField.setValue(v, true);
33551         this.inputEl.dom.value = this.getValue();
33552         this.validate();
33553         return;
33554     },
33555     
33556     setYear : function(v)
33557     {
33558         this.yearField.setValue(v);
33559         this.inputEl.dom.value = this.getValue();
33560         this.validate();
33561         return;
33562     },
33563     
33564     getDay : function()
33565     {
33566         return this.dayField.getValue();
33567     },
33568     
33569     getMonth : function()
33570     {
33571         return this.monthField.getValue();
33572     },
33573     
33574     getYear : function()
33575     {
33576         return this.yearField.getValue();
33577     },
33578     
33579     getValue : function()
33580     {
33581         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33582         
33583         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33584         
33585         return date;
33586     },
33587     
33588     reset : function()
33589     {
33590         this.setDay('');
33591         this.setMonth('');
33592         this.setYear('');
33593         this.inputEl.dom.value = '';
33594         this.validate();
33595         return;
33596     },
33597     
33598     validate : function()
33599     {
33600         var d = this.dayField.validate();
33601         var m = this.monthField.validate();
33602         var y = this.yearField.validate();
33603         
33604         var valid = true;
33605         
33606         if(
33607                 (!this.dayAllowBlank && !d) ||
33608                 (!this.monthAllowBlank && !m) ||
33609                 (!this.yearAllowBlank && !y)
33610         ){
33611             valid = false;
33612         }
33613         
33614         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33615             return valid;
33616         }
33617         
33618         if(valid){
33619             this.markValid();
33620             return valid;
33621         }
33622         
33623         this.markInvalid();
33624         
33625         return valid;
33626     },
33627     
33628     markValid : function()
33629     {
33630         
33631         var label = this.el.select('label', true).first();
33632         var icon = this.el.select('i.fa-star', true).first();
33633
33634         if(label && icon){
33635             icon.remove();
33636         }
33637         
33638         this.fireEvent('valid', this);
33639     },
33640     
33641      /**
33642      * Mark this field as invalid
33643      * @param {String} msg The validation message
33644      */
33645     markInvalid : function(msg)
33646     {
33647         
33648         var label = this.el.select('label', true).first();
33649         var icon = this.el.select('i.fa-star', true).first();
33650
33651         if(label && !icon){
33652             this.el.select('.roo-date-split-field-label', true).createChild({
33653                 tag : 'i',
33654                 cls : 'text-danger fa fa-lg fa-star',
33655                 tooltip : 'This field is required',
33656                 style : 'margin-right:5px;'
33657             }, label, true);
33658         }
33659         
33660         this.fireEvent('invalid', this, msg);
33661     },
33662     
33663     clearInvalid : function()
33664     {
33665         var label = this.el.select('label', true).first();
33666         var icon = this.el.select('i.fa-star', true).first();
33667
33668         if(label && icon){
33669             icon.remove();
33670         }
33671         
33672         this.fireEvent('valid', this);
33673     },
33674     
33675     getName: function()
33676     {
33677         return this.name;
33678     }
33679     
33680 });
33681
33682  /**
33683  *
33684  * This is based on 
33685  * http://masonry.desandro.com
33686  *
33687  * The idea is to render all the bricks based on vertical width...
33688  *
33689  * The original code extends 'outlayer' - we might need to use that....
33690  * 
33691  */
33692
33693
33694 /**
33695  * @class Roo.bootstrap.LayoutMasonry
33696  * @extends Roo.bootstrap.Component
33697  * Bootstrap Layout Masonry class
33698  * 
33699  * @constructor
33700  * Create a new Element
33701  * @param {Object} config The config object
33702  */
33703
33704 Roo.bootstrap.LayoutMasonry = function(config){
33705     
33706     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33707     
33708     this.bricks = [];
33709     
33710     Roo.bootstrap.LayoutMasonry.register(this);
33711     
33712     this.addEvents({
33713         // raw events
33714         /**
33715          * @event layout
33716          * Fire after layout the items
33717          * @param {Roo.bootstrap.LayoutMasonry} this
33718          * @param {Roo.EventObject} e
33719          */
33720         "layout" : true
33721     });
33722     
33723 };
33724
33725 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33726     
33727     /**
33728      * @cfg {Boolean} isLayoutInstant = no animation?
33729      */   
33730     isLayoutInstant : false, // needed?
33731    
33732     /**
33733      * @cfg {Number} boxWidth  width of the columns
33734      */   
33735     boxWidth : 450,
33736     
33737       /**
33738      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33739      */   
33740     boxHeight : 0,
33741     
33742     /**
33743      * @cfg {Number} padWidth padding below box..
33744      */   
33745     padWidth : 10, 
33746     
33747     /**
33748      * @cfg {Number} gutter gutter width..
33749      */   
33750     gutter : 10,
33751     
33752      /**
33753      * @cfg {Number} maxCols maximum number of columns
33754      */   
33755     
33756     maxCols: 0,
33757     
33758     /**
33759      * @cfg {Boolean} isAutoInitial defalut true
33760      */   
33761     isAutoInitial : true, 
33762     
33763     containerWidth: 0,
33764     
33765     /**
33766      * @cfg {Boolean} isHorizontal defalut false
33767      */   
33768     isHorizontal : false, 
33769
33770     currentSize : null,
33771     
33772     tag: 'div',
33773     
33774     cls: '',
33775     
33776     bricks: null, //CompositeElement
33777     
33778     cols : 1,
33779     
33780     _isLayoutInited : false,
33781     
33782 //    isAlternative : false, // only use for vertical layout...
33783     
33784     /**
33785      * @cfg {Number} alternativePadWidth padding below box..
33786      */   
33787     alternativePadWidth : 50,
33788     
33789     selectedBrick : [],
33790     
33791     getAutoCreate : function(){
33792         
33793         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33794         
33795         var cfg = {
33796             tag: this.tag,
33797             cls: 'blog-masonary-wrapper ' + this.cls,
33798             cn : {
33799                 cls : 'mas-boxes masonary'
33800             }
33801         };
33802         
33803         return cfg;
33804     },
33805     
33806     getChildContainer: function( )
33807     {
33808         if (this.boxesEl) {
33809             return this.boxesEl;
33810         }
33811         
33812         this.boxesEl = this.el.select('.mas-boxes').first();
33813         
33814         return this.boxesEl;
33815     },
33816     
33817     
33818     initEvents : function()
33819     {
33820         var _this = this;
33821         
33822         if(this.isAutoInitial){
33823             Roo.log('hook children rendered');
33824             this.on('childrenrendered', function() {
33825                 Roo.log('children rendered');
33826                 _this.initial();
33827             } ,this);
33828         }
33829     },
33830     
33831     initial : function()
33832     {
33833         this.selectedBrick = [];
33834         
33835         this.currentSize = this.el.getBox(true);
33836         
33837         Roo.EventManager.onWindowResize(this.resize, this); 
33838
33839         if(!this.isAutoInitial){
33840             this.layout();
33841             return;
33842         }
33843         
33844         this.layout();
33845         
33846         return;
33847         //this.layout.defer(500,this);
33848         
33849     },
33850     
33851     resize : function()
33852     {
33853         var cs = this.el.getBox(true);
33854         
33855         if (
33856                 this.currentSize.width == cs.width && 
33857                 this.currentSize.x == cs.x && 
33858                 this.currentSize.height == cs.height && 
33859                 this.currentSize.y == cs.y 
33860         ) {
33861             Roo.log("no change in with or X or Y");
33862             return;
33863         }
33864         
33865         this.currentSize = cs;
33866         
33867         this.layout();
33868         
33869     },
33870     
33871     layout : function()
33872     {   
33873         this._resetLayout();
33874         
33875         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33876         
33877         this.layoutItems( isInstant );
33878       
33879         this._isLayoutInited = true;
33880         
33881         this.fireEvent('layout', this);
33882         
33883     },
33884     
33885     _resetLayout : function()
33886     {
33887         if(this.isHorizontal){
33888             this.horizontalMeasureColumns();
33889             return;
33890         }
33891         
33892         this.verticalMeasureColumns();
33893         
33894     },
33895     
33896     verticalMeasureColumns : function()
33897     {
33898         this.getContainerWidth();
33899         
33900 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33901 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33902 //            return;
33903 //        }
33904         
33905         var boxWidth = this.boxWidth + this.padWidth;
33906         
33907         if(this.containerWidth < this.boxWidth){
33908             boxWidth = this.containerWidth
33909         }
33910         
33911         var containerWidth = this.containerWidth;
33912         
33913         var cols = Math.floor(containerWidth / boxWidth);
33914         
33915         this.cols = Math.max( cols, 1 );
33916         
33917         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33918         
33919         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33920         
33921         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33922         
33923         this.colWidth = boxWidth + avail - this.padWidth;
33924         
33925         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33926         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33927     },
33928     
33929     horizontalMeasureColumns : function()
33930     {
33931         this.getContainerWidth();
33932         
33933         var boxWidth = this.boxWidth;
33934         
33935         if(this.containerWidth < boxWidth){
33936             boxWidth = this.containerWidth;
33937         }
33938         
33939         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33940         
33941         this.el.setHeight(boxWidth);
33942         
33943     },
33944     
33945     getContainerWidth : function()
33946     {
33947         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33948     },
33949     
33950     layoutItems : function( isInstant )
33951     {
33952         Roo.log(this.bricks);
33953         
33954         var items = Roo.apply([], this.bricks);
33955         
33956         if(this.isHorizontal){
33957             this._horizontalLayoutItems( items , isInstant );
33958             return;
33959         }
33960         
33961 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33962 //            this._verticalAlternativeLayoutItems( items , isInstant );
33963 //            return;
33964 //        }
33965         
33966         this._verticalLayoutItems( items , isInstant );
33967         
33968     },
33969     
33970     _verticalLayoutItems : function ( items , isInstant)
33971     {
33972         if ( !items || !items.length ) {
33973             return;
33974         }
33975         
33976         var standard = [
33977             ['xs', 'xs', 'xs', 'tall'],
33978             ['xs', 'xs', 'tall'],
33979             ['xs', 'xs', 'sm'],
33980             ['xs', 'xs', 'xs'],
33981             ['xs', 'tall'],
33982             ['xs', 'sm'],
33983             ['xs', 'xs'],
33984             ['xs'],
33985             
33986             ['sm', 'xs', 'xs'],
33987             ['sm', 'xs'],
33988             ['sm'],
33989             
33990             ['tall', 'xs', 'xs', 'xs'],
33991             ['tall', 'xs', 'xs'],
33992             ['tall', 'xs'],
33993             ['tall']
33994             
33995         ];
33996         
33997         var queue = [];
33998         
33999         var boxes = [];
34000         
34001         var box = [];
34002         
34003         Roo.each(items, function(item, k){
34004             
34005             switch (item.size) {
34006                 // these layouts take up a full box,
34007                 case 'md' :
34008                 case 'md-left' :
34009                 case 'md-right' :
34010                 case 'wide' :
34011                     
34012                     if(box.length){
34013                         boxes.push(box);
34014                         box = [];
34015                     }
34016                     
34017                     boxes.push([item]);
34018                     
34019                     break;
34020                     
34021                 case 'xs' :
34022                 case 'sm' :
34023                 case 'tall' :
34024                     
34025                     box.push(item);
34026                     
34027                     break;
34028                 default :
34029                     break;
34030                     
34031             }
34032             
34033         }, this);
34034         
34035         if(box.length){
34036             boxes.push(box);
34037             box = [];
34038         }
34039         
34040         var filterPattern = function(box, length)
34041         {
34042             if(!box.length){
34043                 return;
34044             }
34045             
34046             var match = false;
34047             
34048             var pattern = box.slice(0, length);
34049             
34050             var format = [];
34051             
34052             Roo.each(pattern, function(i){
34053                 format.push(i.size);
34054             }, this);
34055             
34056             Roo.each(standard, function(s){
34057                 
34058                 if(String(s) != String(format)){
34059                     return;
34060                 }
34061                 
34062                 match = true;
34063                 return false;
34064                 
34065             }, this);
34066             
34067             if(!match && length == 1){
34068                 return;
34069             }
34070             
34071             if(!match){
34072                 filterPattern(box, length - 1);
34073                 return;
34074             }
34075                 
34076             queue.push(pattern);
34077
34078             box = box.slice(length, box.length);
34079
34080             filterPattern(box, 4);
34081
34082             return;
34083             
34084         }
34085         
34086         Roo.each(boxes, function(box, k){
34087             
34088             if(!box.length){
34089                 return;
34090             }
34091             
34092             if(box.length == 1){
34093                 queue.push(box);
34094                 return;
34095             }
34096             
34097             filterPattern(box, 4);
34098             
34099         }, this);
34100         
34101         this._processVerticalLayoutQueue( queue, isInstant );
34102         
34103     },
34104     
34105 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34106 //    {
34107 //        if ( !items || !items.length ) {
34108 //            return;
34109 //        }
34110 //
34111 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34112 //        
34113 //    },
34114     
34115     _horizontalLayoutItems : function ( items , isInstant)
34116     {
34117         if ( !items || !items.length || items.length < 3) {
34118             return;
34119         }
34120         
34121         items.reverse();
34122         
34123         var eItems = items.slice(0, 3);
34124         
34125         items = items.slice(3, items.length);
34126         
34127         var standard = [
34128             ['xs', 'xs', 'xs', 'wide'],
34129             ['xs', 'xs', 'wide'],
34130             ['xs', 'xs', 'sm'],
34131             ['xs', 'xs', 'xs'],
34132             ['xs', 'wide'],
34133             ['xs', 'sm'],
34134             ['xs', 'xs'],
34135             ['xs'],
34136             
34137             ['sm', 'xs', 'xs'],
34138             ['sm', 'xs'],
34139             ['sm'],
34140             
34141             ['wide', 'xs', 'xs', 'xs'],
34142             ['wide', 'xs', 'xs'],
34143             ['wide', 'xs'],
34144             ['wide'],
34145             
34146             ['wide-thin']
34147         ];
34148         
34149         var queue = [];
34150         
34151         var boxes = [];
34152         
34153         var box = [];
34154         
34155         Roo.each(items, function(item, k){
34156             
34157             switch (item.size) {
34158                 case 'md' :
34159                 case 'md-left' :
34160                 case 'md-right' :
34161                 case 'tall' :
34162                     
34163                     if(box.length){
34164                         boxes.push(box);
34165                         box = [];
34166                     }
34167                     
34168                     boxes.push([item]);
34169                     
34170                     break;
34171                     
34172                 case 'xs' :
34173                 case 'sm' :
34174                 case 'wide' :
34175                 case 'wide-thin' :
34176                     
34177                     box.push(item);
34178                     
34179                     break;
34180                 default :
34181                     break;
34182                     
34183             }
34184             
34185         }, this);
34186         
34187         if(box.length){
34188             boxes.push(box);
34189             box = [];
34190         }
34191         
34192         var filterPattern = function(box, length)
34193         {
34194             if(!box.length){
34195                 return;
34196             }
34197             
34198             var match = false;
34199             
34200             var pattern = box.slice(0, length);
34201             
34202             var format = [];
34203             
34204             Roo.each(pattern, function(i){
34205                 format.push(i.size);
34206             }, this);
34207             
34208             Roo.each(standard, function(s){
34209                 
34210                 if(String(s) != String(format)){
34211                     return;
34212                 }
34213                 
34214                 match = true;
34215                 return false;
34216                 
34217             }, this);
34218             
34219             if(!match && length == 1){
34220                 return;
34221             }
34222             
34223             if(!match){
34224                 filterPattern(box, length - 1);
34225                 return;
34226             }
34227                 
34228             queue.push(pattern);
34229
34230             box = box.slice(length, box.length);
34231
34232             filterPattern(box, 4);
34233
34234             return;
34235             
34236         }
34237         
34238         Roo.each(boxes, function(box, k){
34239             
34240             if(!box.length){
34241                 return;
34242             }
34243             
34244             if(box.length == 1){
34245                 queue.push(box);
34246                 return;
34247             }
34248             
34249             filterPattern(box, 4);
34250             
34251         }, this);
34252         
34253         
34254         var prune = [];
34255         
34256         var pos = this.el.getBox(true);
34257         
34258         var minX = pos.x;
34259         
34260         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34261         
34262         var hit_end = false;
34263         
34264         Roo.each(queue, function(box){
34265             
34266             if(hit_end){
34267                 
34268                 Roo.each(box, function(b){
34269                 
34270                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34271                     b.el.hide();
34272
34273                 }, this);
34274
34275                 return;
34276             }
34277             
34278             var mx = 0;
34279             
34280             Roo.each(box, function(b){
34281                 
34282                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34283                 b.el.show();
34284
34285                 mx = Math.max(mx, b.x);
34286                 
34287             }, this);
34288             
34289             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34290             
34291             if(maxX < minX){
34292                 
34293                 Roo.each(box, function(b){
34294                 
34295                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34296                     b.el.hide();
34297                     
34298                 }, this);
34299                 
34300                 hit_end = true;
34301                 
34302                 return;
34303             }
34304             
34305             prune.push(box);
34306             
34307         }, this);
34308         
34309         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34310     },
34311     
34312     /** Sets position of item in DOM
34313     * @param {Element} item
34314     * @param {Number} x - horizontal position
34315     * @param {Number} y - vertical position
34316     * @param {Boolean} isInstant - disables transitions
34317     */
34318     _processVerticalLayoutQueue : function( queue, isInstant )
34319     {
34320         var pos = this.el.getBox(true);
34321         var x = pos.x;
34322         var y = pos.y;
34323         var maxY = [];
34324         
34325         for (var i = 0; i < this.cols; i++){
34326             maxY[i] = pos.y;
34327         }
34328         
34329         Roo.each(queue, function(box, k){
34330             
34331             var col = k % this.cols;
34332             
34333             Roo.each(box, function(b,kk){
34334                 
34335                 b.el.position('absolute');
34336                 
34337                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34338                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34339                 
34340                 if(b.size == 'md-left' || b.size == 'md-right'){
34341                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34342                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34343                 }
34344                 
34345                 b.el.setWidth(width);
34346                 b.el.setHeight(height);
34347                 // iframe?
34348                 b.el.select('iframe',true).setSize(width,height);
34349                 
34350             }, this);
34351             
34352             for (var i = 0; i < this.cols; i++){
34353                 
34354                 if(maxY[i] < maxY[col]){
34355                     col = i;
34356                     continue;
34357                 }
34358                 
34359                 col = Math.min(col, i);
34360                 
34361             }
34362             
34363             x = pos.x + col * (this.colWidth + this.padWidth);
34364             
34365             y = maxY[col];
34366             
34367             var positions = [];
34368             
34369             switch (box.length){
34370                 case 1 :
34371                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34372                     break;
34373                 case 2 :
34374                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34375                     break;
34376                 case 3 :
34377                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34378                     break;
34379                 case 4 :
34380                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34381                     break;
34382                 default :
34383                     break;
34384             }
34385             
34386             Roo.each(box, function(b,kk){
34387                 
34388                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34389                 
34390                 var sz = b.el.getSize();
34391                 
34392                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34393                 
34394             }, this);
34395             
34396         }, this);
34397         
34398         var mY = 0;
34399         
34400         for (var i = 0; i < this.cols; i++){
34401             mY = Math.max(mY, maxY[i]);
34402         }
34403         
34404         this.el.setHeight(mY - pos.y);
34405         
34406     },
34407     
34408 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34409 //    {
34410 //        var pos = this.el.getBox(true);
34411 //        var x = pos.x;
34412 //        var y = pos.y;
34413 //        var maxX = pos.right;
34414 //        
34415 //        var maxHeight = 0;
34416 //        
34417 //        Roo.each(items, function(item, k){
34418 //            
34419 //            var c = k % 2;
34420 //            
34421 //            item.el.position('absolute');
34422 //                
34423 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34424 //
34425 //            item.el.setWidth(width);
34426 //
34427 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34428 //
34429 //            item.el.setHeight(height);
34430 //            
34431 //            if(c == 0){
34432 //                item.el.setXY([x, y], isInstant ? false : true);
34433 //            } else {
34434 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34435 //            }
34436 //            
34437 //            y = y + height + this.alternativePadWidth;
34438 //            
34439 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34440 //            
34441 //        }, this);
34442 //        
34443 //        this.el.setHeight(maxHeight);
34444 //        
34445 //    },
34446     
34447     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34448     {
34449         var pos = this.el.getBox(true);
34450         
34451         var minX = pos.x;
34452         var minY = pos.y;
34453         
34454         var maxX = pos.right;
34455         
34456         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34457         
34458         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34459         
34460         Roo.each(queue, function(box, k){
34461             
34462             Roo.each(box, function(b, kk){
34463                 
34464                 b.el.position('absolute');
34465                 
34466                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34467                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34468                 
34469                 if(b.size == 'md-left' || b.size == 'md-right'){
34470                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34471                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34472                 }
34473                 
34474                 b.el.setWidth(width);
34475                 b.el.setHeight(height);
34476                 
34477             }, this);
34478             
34479             if(!box.length){
34480                 return;
34481             }
34482             
34483             var positions = [];
34484             
34485             switch (box.length){
34486                 case 1 :
34487                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34488                     break;
34489                 case 2 :
34490                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34491                     break;
34492                 case 3 :
34493                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34494                     break;
34495                 case 4 :
34496                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34497                     break;
34498                 default :
34499                     break;
34500             }
34501             
34502             Roo.each(box, function(b,kk){
34503                 
34504                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34505                 
34506                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34507                 
34508             }, this);
34509             
34510         }, this);
34511         
34512     },
34513     
34514     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34515     {
34516         Roo.each(eItems, function(b,k){
34517             
34518             b.size = (k == 0) ? 'sm' : 'xs';
34519             b.x = (k == 0) ? 2 : 1;
34520             b.y = (k == 0) ? 2 : 1;
34521             
34522             b.el.position('absolute');
34523             
34524             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34525                 
34526             b.el.setWidth(width);
34527             
34528             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34529             
34530             b.el.setHeight(height);
34531             
34532         }, this);
34533
34534         var positions = [];
34535         
34536         positions.push({
34537             x : maxX - this.unitWidth * 2 - this.gutter,
34538             y : minY
34539         });
34540         
34541         positions.push({
34542             x : maxX - this.unitWidth,
34543             y : minY + (this.unitWidth + this.gutter) * 2
34544         });
34545         
34546         positions.push({
34547             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34548             y : minY
34549         });
34550         
34551         Roo.each(eItems, function(b,k){
34552             
34553             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34554
34555         }, this);
34556         
34557     },
34558     
34559     getVerticalOneBoxColPositions : function(x, y, box)
34560     {
34561         var pos = [];
34562         
34563         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34564         
34565         if(box[0].size == 'md-left'){
34566             rand = 0;
34567         }
34568         
34569         if(box[0].size == 'md-right'){
34570             rand = 1;
34571         }
34572         
34573         pos.push({
34574             x : x + (this.unitWidth + this.gutter) * rand,
34575             y : y
34576         });
34577         
34578         return pos;
34579     },
34580     
34581     getVerticalTwoBoxColPositions : function(x, y, box)
34582     {
34583         var pos = [];
34584         
34585         if(box[0].size == 'xs'){
34586             
34587             pos.push({
34588                 x : x,
34589                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34590             });
34591
34592             pos.push({
34593                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34594                 y : y
34595             });
34596             
34597             return pos;
34598             
34599         }
34600         
34601         pos.push({
34602             x : x,
34603             y : y
34604         });
34605
34606         pos.push({
34607             x : x + (this.unitWidth + this.gutter) * 2,
34608             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34609         });
34610         
34611         return pos;
34612         
34613     },
34614     
34615     getVerticalThreeBoxColPositions : function(x, y, box)
34616     {
34617         var pos = [];
34618         
34619         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34620             
34621             pos.push({
34622                 x : x,
34623                 y : y
34624             });
34625
34626             pos.push({
34627                 x : x + (this.unitWidth + this.gutter) * 1,
34628                 y : y
34629             });
34630             
34631             pos.push({
34632                 x : x + (this.unitWidth + this.gutter) * 2,
34633                 y : y
34634             });
34635             
34636             return pos;
34637             
34638         }
34639         
34640         if(box[0].size == 'xs' && box[1].size == 'xs'){
34641             
34642             pos.push({
34643                 x : x,
34644                 y : y
34645             });
34646
34647             pos.push({
34648                 x : x,
34649                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34650             });
34651             
34652             pos.push({
34653                 x : x + (this.unitWidth + this.gutter) * 1,
34654                 y : y
34655             });
34656             
34657             return pos;
34658             
34659         }
34660         
34661         pos.push({
34662             x : x,
34663             y : y
34664         });
34665
34666         pos.push({
34667             x : x + (this.unitWidth + this.gutter) * 2,
34668             y : y
34669         });
34670
34671         pos.push({
34672             x : x + (this.unitWidth + this.gutter) * 2,
34673             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34674         });
34675             
34676         return pos;
34677         
34678     },
34679     
34680     getVerticalFourBoxColPositions : function(x, y, box)
34681     {
34682         var pos = [];
34683         
34684         if(box[0].size == 'xs'){
34685             
34686             pos.push({
34687                 x : x,
34688                 y : y
34689             });
34690
34691             pos.push({
34692                 x : x,
34693                 y : y + (this.unitHeight + this.gutter) * 1
34694             });
34695             
34696             pos.push({
34697                 x : x,
34698                 y : y + (this.unitHeight + this.gutter) * 2
34699             });
34700             
34701             pos.push({
34702                 x : x + (this.unitWidth + this.gutter) * 1,
34703                 y : y
34704             });
34705             
34706             return pos;
34707             
34708         }
34709         
34710         pos.push({
34711             x : x,
34712             y : y
34713         });
34714
34715         pos.push({
34716             x : x + (this.unitWidth + this.gutter) * 2,
34717             y : y
34718         });
34719
34720         pos.push({
34721             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34722             y : y + (this.unitHeight + this.gutter) * 1
34723         });
34724
34725         pos.push({
34726             x : x + (this.unitWidth + this.gutter) * 2,
34727             y : y + (this.unitWidth + this.gutter) * 2
34728         });
34729
34730         return pos;
34731         
34732     },
34733     
34734     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34735     {
34736         var pos = [];
34737         
34738         if(box[0].size == 'md-left'){
34739             pos.push({
34740                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34741                 y : minY
34742             });
34743             
34744             return pos;
34745         }
34746         
34747         if(box[0].size == 'md-right'){
34748             pos.push({
34749                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34750                 y : minY + (this.unitWidth + this.gutter) * 1
34751             });
34752             
34753             return pos;
34754         }
34755         
34756         var rand = Math.floor(Math.random() * (4 - box[0].y));
34757         
34758         pos.push({
34759             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34760             y : minY + (this.unitWidth + this.gutter) * rand
34761         });
34762         
34763         return pos;
34764         
34765     },
34766     
34767     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34768     {
34769         var pos = [];
34770         
34771         if(box[0].size == 'xs'){
34772             
34773             pos.push({
34774                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34775                 y : minY
34776             });
34777
34778             pos.push({
34779                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34780                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34781             });
34782             
34783             return pos;
34784             
34785         }
34786         
34787         pos.push({
34788             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34789             y : minY
34790         });
34791
34792         pos.push({
34793             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34794             y : minY + (this.unitWidth + this.gutter) * 2
34795         });
34796         
34797         return pos;
34798         
34799     },
34800     
34801     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34802     {
34803         var pos = [];
34804         
34805         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34806             
34807             pos.push({
34808                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34809                 y : minY
34810             });
34811
34812             pos.push({
34813                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34814                 y : minY + (this.unitWidth + this.gutter) * 1
34815             });
34816             
34817             pos.push({
34818                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34819                 y : minY + (this.unitWidth + this.gutter) * 2
34820             });
34821             
34822             return pos;
34823             
34824         }
34825         
34826         if(box[0].size == 'xs' && box[1].size == 'xs'){
34827             
34828             pos.push({
34829                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34830                 y : minY
34831             });
34832
34833             pos.push({
34834                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34835                 y : minY
34836             });
34837             
34838             pos.push({
34839                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34840                 y : minY + (this.unitWidth + this.gutter) * 1
34841             });
34842             
34843             return pos;
34844             
34845         }
34846         
34847         pos.push({
34848             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34849             y : minY
34850         });
34851
34852         pos.push({
34853             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34854             y : minY + (this.unitWidth + this.gutter) * 2
34855         });
34856
34857         pos.push({
34858             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34859             y : minY + (this.unitWidth + this.gutter) * 2
34860         });
34861             
34862         return pos;
34863         
34864     },
34865     
34866     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34867     {
34868         var pos = [];
34869         
34870         if(box[0].size == 'xs'){
34871             
34872             pos.push({
34873                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34874                 y : minY
34875             });
34876
34877             pos.push({
34878                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34879                 y : minY
34880             });
34881             
34882             pos.push({
34883                 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),
34884                 y : minY
34885             });
34886             
34887             pos.push({
34888                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34889                 y : minY + (this.unitWidth + this.gutter) * 1
34890             });
34891             
34892             return pos;
34893             
34894         }
34895         
34896         pos.push({
34897             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34898             y : minY
34899         });
34900         
34901         pos.push({
34902             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34903             y : minY + (this.unitWidth + this.gutter) * 2
34904         });
34905         
34906         pos.push({
34907             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34908             y : minY + (this.unitWidth + this.gutter) * 2
34909         });
34910         
34911         pos.push({
34912             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),
34913             y : minY + (this.unitWidth + this.gutter) * 2
34914         });
34915
34916         return pos;
34917         
34918     },
34919     
34920     /**
34921     * remove a Masonry Brick
34922     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34923     */
34924     removeBrick : function(brick_id)
34925     {
34926         if (!brick_id) {
34927             return;
34928         }
34929         
34930         for (var i = 0; i<this.bricks.length; i++) {
34931             if (this.bricks[i].id == brick_id) {
34932                 this.bricks.splice(i,1);
34933                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34934                 this.initial();
34935             }
34936         }
34937     },
34938     
34939     /**
34940     * adds a Masonry Brick
34941     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34942     */
34943     addBrick : function(cfg)
34944     {
34945         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34946         //this.register(cn);
34947         cn.parentId = this.id;
34948         cn.render(this.el);
34949         return cn;
34950     },
34951     
34952     /**
34953     * register a Masonry Brick
34954     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34955     */
34956     
34957     register : function(brick)
34958     {
34959         this.bricks.push(brick);
34960         brick.masonryId = this.id;
34961     },
34962     
34963     /**
34964     * clear all the Masonry Brick
34965     */
34966     clearAll : function()
34967     {
34968         this.bricks = [];
34969         //this.getChildContainer().dom.innerHTML = "";
34970         this.el.dom.innerHTML = '';
34971     },
34972     
34973     getSelected : function()
34974     {
34975         if (!this.selectedBrick) {
34976             return false;
34977         }
34978         
34979         return this.selectedBrick;
34980     }
34981 });
34982
34983 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34984     
34985     groups: {},
34986      /**
34987     * register a Masonry Layout
34988     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34989     */
34990     
34991     register : function(layout)
34992     {
34993         this.groups[layout.id] = layout;
34994     },
34995     /**
34996     * fetch a  Masonry Layout based on the masonry layout ID
34997     * @param {string} the masonry layout to add
34998     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34999     */
35000     
35001     get: function(layout_id) {
35002         if (typeof(this.groups[layout_id]) == 'undefined') {
35003             return false;
35004         }
35005         return this.groups[layout_id] ;
35006     }
35007     
35008     
35009     
35010 });
35011
35012  
35013
35014  /**
35015  *
35016  * This is based on 
35017  * http://masonry.desandro.com
35018  *
35019  * The idea is to render all the bricks based on vertical width...
35020  *
35021  * The original code extends 'outlayer' - we might need to use that....
35022  * 
35023  */
35024
35025
35026 /**
35027  * @class Roo.bootstrap.LayoutMasonryAuto
35028  * @extends Roo.bootstrap.Component
35029  * Bootstrap Layout Masonry class
35030  * 
35031  * @constructor
35032  * Create a new Element
35033  * @param {Object} config The config object
35034  */
35035
35036 Roo.bootstrap.LayoutMasonryAuto = function(config){
35037     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35038 };
35039
35040 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35041     
35042       /**
35043      * @cfg {Boolean} isFitWidth  - resize the width..
35044      */   
35045     isFitWidth : false,  // options..
35046     /**
35047      * @cfg {Boolean} isOriginLeft = left align?
35048      */   
35049     isOriginLeft : true,
35050     /**
35051      * @cfg {Boolean} isOriginTop = top align?
35052      */   
35053     isOriginTop : false,
35054     /**
35055      * @cfg {Boolean} isLayoutInstant = no animation?
35056      */   
35057     isLayoutInstant : false, // needed?
35058     /**
35059      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35060      */   
35061     isResizingContainer : true,
35062     /**
35063      * @cfg {Number} columnWidth  width of the columns 
35064      */   
35065     
35066     columnWidth : 0,
35067     
35068     /**
35069      * @cfg {Number} maxCols maximum number of columns
35070      */   
35071     
35072     maxCols: 0,
35073     /**
35074      * @cfg {Number} padHeight padding below box..
35075      */   
35076     
35077     padHeight : 10, 
35078     
35079     /**
35080      * @cfg {Boolean} isAutoInitial defalut true
35081      */   
35082     
35083     isAutoInitial : true, 
35084     
35085     // private?
35086     gutter : 0,
35087     
35088     containerWidth: 0,
35089     initialColumnWidth : 0,
35090     currentSize : null,
35091     
35092     colYs : null, // array.
35093     maxY : 0,
35094     padWidth: 10,
35095     
35096     
35097     tag: 'div',
35098     cls: '',
35099     bricks: null, //CompositeElement
35100     cols : 0, // array?
35101     // element : null, // wrapped now this.el
35102     _isLayoutInited : null, 
35103     
35104     
35105     getAutoCreate : function(){
35106         
35107         var cfg = {
35108             tag: this.tag,
35109             cls: 'blog-masonary-wrapper ' + this.cls,
35110             cn : {
35111                 cls : 'mas-boxes masonary'
35112             }
35113         };
35114         
35115         return cfg;
35116     },
35117     
35118     getChildContainer: function( )
35119     {
35120         if (this.boxesEl) {
35121             return this.boxesEl;
35122         }
35123         
35124         this.boxesEl = this.el.select('.mas-boxes').first();
35125         
35126         return this.boxesEl;
35127     },
35128     
35129     
35130     initEvents : function()
35131     {
35132         var _this = this;
35133         
35134         if(this.isAutoInitial){
35135             Roo.log('hook children rendered');
35136             this.on('childrenrendered', function() {
35137                 Roo.log('children rendered');
35138                 _this.initial();
35139             } ,this);
35140         }
35141         
35142     },
35143     
35144     initial : function()
35145     {
35146         this.reloadItems();
35147
35148         this.currentSize = this.el.getBox(true);
35149
35150         /// was window resize... - let's see if this works..
35151         Roo.EventManager.onWindowResize(this.resize, this); 
35152
35153         if(!this.isAutoInitial){
35154             this.layout();
35155             return;
35156         }
35157         
35158         this.layout.defer(500,this);
35159     },
35160     
35161     reloadItems: function()
35162     {
35163         this.bricks = this.el.select('.masonry-brick', true);
35164         
35165         this.bricks.each(function(b) {
35166             //Roo.log(b.getSize());
35167             if (!b.attr('originalwidth')) {
35168                 b.attr('originalwidth',  b.getSize().width);
35169             }
35170             
35171         });
35172         
35173         Roo.log(this.bricks.elements.length);
35174     },
35175     
35176     resize : function()
35177     {
35178         Roo.log('resize');
35179         var cs = this.el.getBox(true);
35180         
35181         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35182             Roo.log("no change in with or X");
35183             return;
35184         }
35185         this.currentSize = cs;
35186         this.layout();
35187     },
35188     
35189     layout : function()
35190     {
35191          Roo.log('layout');
35192         this._resetLayout();
35193         //this._manageStamps();
35194       
35195         // don't animate first layout
35196         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35197         this.layoutItems( isInstant );
35198       
35199         // flag for initalized
35200         this._isLayoutInited = true;
35201     },
35202     
35203     layoutItems : function( isInstant )
35204     {
35205         //var items = this._getItemsForLayout( this.items );
35206         // original code supports filtering layout items.. we just ignore it..
35207         
35208         this._layoutItems( this.bricks , isInstant );
35209       
35210         this._postLayout();
35211     },
35212     _layoutItems : function ( items , isInstant)
35213     {
35214        //this.fireEvent( 'layout', this, items );
35215     
35216
35217         if ( !items || !items.elements.length ) {
35218           // no items, emit event with empty array
35219             return;
35220         }
35221
35222         var queue = [];
35223         items.each(function(item) {
35224             Roo.log("layout item");
35225             Roo.log(item);
35226             // get x/y object from method
35227             var position = this._getItemLayoutPosition( item );
35228             // enqueue
35229             position.item = item;
35230             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35231             queue.push( position );
35232         }, this);
35233       
35234         this._processLayoutQueue( queue );
35235     },
35236     /** Sets position of item in DOM
35237     * @param {Element} item
35238     * @param {Number} x - horizontal position
35239     * @param {Number} y - vertical position
35240     * @param {Boolean} isInstant - disables transitions
35241     */
35242     _processLayoutQueue : function( queue )
35243     {
35244         for ( var i=0, len = queue.length; i < len; i++ ) {
35245             var obj = queue[i];
35246             obj.item.position('absolute');
35247             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35248         }
35249     },
35250       
35251     
35252     /**
35253     * Any logic you want to do after each layout,
35254     * i.e. size the container
35255     */
35256     _postLayout : function()
35257     {
35258         this.resizeContainer();
35259     },
35260     
35261     resizeContainer : function()
35262     {
35263         if ( !this.isResizingContainer ) {
35264             return;
35265         }
35266         var size = this._getContainerSize();
35267         if ( size ) {
35268             this.el.setSize(size.width,size.height);
35269             this.boxesEl.setSize(size.width,size.height);
35270         }
35271     },
35272     
35273     
35274     
35275     _resetLayout : function()
35276     {
35277         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35278         this.colWidth = this.el.getWidth();
35279         //this.gutter = this.el.getWidth(); 
35280         
35281         this.measureColumns();
35282
35283         // reset column Y
35284         var i = this.cols;
35285         this.colYs = [];
35286         while (i--) {
35287             this.colYs.push( 0 );
35288         }
35289     
35290         this.maxY = 0;
35291     },
35292
35293     measureColumns : function()
35294     {
35295         this.getContainerWidth();
35296       // if columnWidth is 0, default to outerWidth of first item
35297         if ( !this.columnWidth ) {
35298             var firstItem = this.bricks.first();
35299             Roo.log(firstItem);
35300             this.columnWidth  = this.containerWidth;
35301             if (firstItem && firstItem.attr('originalwidth') ) {
35302                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35303             }
35304             // columnWidth fall back to item of first element
35305             Roo.log("set column width?");
35306                         this.initialColumnWidth = this.columnWidth  ;
35307
35308             // if first elem has no width, default to size of container
35309             
35310         }
35311         
35312         
35313         if (this.initialColumnWidth) {
35314             this.columnWidth = this.initialColumnWidth;
35315         }
35316         
35317         
35318             
35319         // column width is fixed at the top - however if container width get's smaller we should
35320         // reduce it...
35321         
35322         // this bit calcs how man columns..
35323             
35324         var columnWidth = this.columnWidth += this.gutter;
35325       
35326         // calculate columns
35327         var containerWidth = this.containerWidth + this.gutter;
35328         
35329         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35330         // fix rounding errors, typically with gutters
35331         var excess = columnWidth - containerWidth % columnWidth;
35332         
35333         
35334         // if overshoot is less than a pixel, round up, otherwise floor it
35335         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35336         cols = Math[ mathMethod ]( cols );
35337         this.cols = Math.max( cols, 1 );
35338         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35339         
35340          // padding positioning..
35341         var totalColWidth = this.cols * this.columnWidth;
35342         var padavail = this.containerWidth - totalColWidth;
35343         // so for 2 columns - we need 3 'pads'
35344         
35345         var padNeeded = (1+this.cols) * this.padWidth;
35346         
35347         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35348         
35349         this.columnWidth += padExtra
35350         //this.padWidth = Math.floor(padavail /  ( this.cols));
35351         
35352         // adjust colum width so that padding is fixed??
35353         
35354         // we have 3 columns ... total = width * 3
35355         // we have X left over... that should be used by 
35356         
35357         //if (this.expandC) {
35358             
35359         //}
35360         
35361         
35362         
35363     },
35364     
35365     getContainerWidth : function()
35366     {
35367        /* // container is parent if fit width
35368         var container = this.isFitWidth ? this.element.parentNode : this.element;
35369         // check that this.size and size are there
35370         // IE8 triggers resize on body size change, so they might not be
35371         
35372         var size = getSize( container );  //FIXME
35373         this.containerWidth = size && size.innerWidth; //FIXME
35374         */
35375          
35376         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35377         
35378     },
35379     
35380     _getItemLayoutPosition : function( item )  // what is item?
35381     {
35382         // we resize the item to our columnWidth..
35383       
35384         item.setWidth(this.columnWidth);
35385         item.autoBoxAdjust  = false;
35386         
35387         var sz = item.getSize();
35388  
35389         // how many columns does this brick span
35390         var remainder = this.containerWidth % this.columnWidth;
35391         
35392         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35393         // round if off by 1 pixel, otherwise use ceil
35394         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35395         colSpan = Math.min( colSpan, this.cols );
35396         
35397         // normally this should be '1' as we dont' currently allow multi width columns..
35398         
35399         var colGroup = this._getColGroup( colSpan );
35400         // get the minimum Y value from the columns
35401         var minimumY = Math.min.apply( Math, colGroup );
35402         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35403         
35404         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35405          
35406         // position the brick
35407         var position = {
35408             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35409             y: this.currentSize.y + minimumY + this.padHeight
35410         };
35411         
35412         Roo.log(position);
35413         // apply setHeight to necessary columns
35414         var setHeight = minimumY + sz.height + this.padHeight;
35415         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35416         
35417         var setSpan = this.cols + 1 - colGroup.length;
35418         for ( var i = 0; i < setSpan; i++ ) {
35419           this.colYs[ shortColIndex + i ] = setHeight ;
35420         }
35421       
35422         return position;
35423     },
35424     
35425     /**
35426      * @param {Number} colSpan - number of columns the element spans
35427      * @returns {Array} colGroup
35428      */
35429     _getColGroup : function( colSpan )
35430     {
35431         if ( colSpan < 2 ) {
35432           // if brick spans only one column, use all the column Ys
35433           return this.colYs;
35434         }
35435       
35436         var colGroup = [];
35437         // how many different places could this brick fit horizontally
35438         var groupCount = this.cols + 1 - colSpan;
35439         // for each group potential horizontal position
35440         for ( var i = 0; i < groupCount; i++ ) {
35441           // make an array of colY values for that one group
35442           var groupColYs = this.colYs.slice( i, i + colSpan );
35443           // and get the max value of the array
35444           colGroup[i] = Math.max.apply( Math, groupColYs );
35445         }
35446         return colGroup;
35447     },
35448     /*
35449     _manageStamp : function( stamp )
35450     {
35451         var stampSize =  stamp.getSize();
35452         var offset = stamp.getBox();
35453         // get the columns that this stamp affects
35454         var firstX = this.isOriginLeft ? offset.x : offset.right;
35455         var lastX = firstX + stampSize.width;
35456         var firstCol = Math.floor( firstX / this.columnWidth );
35457         firstCol = Math.max( 0, firstCol );
35458         
35459         var lastCol = Math.floor( lastX / this.columnWidth );
35460         // lastCol should not go over if multiple of columnWidth #425
35461         lastCol -= lastX % this.columnWidth ? 0 : 1;
35462         lastCol = Math.min( this.cols - 1, lastCol );
35463         
35464         // set colYs to bottom of the stamp
35465         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35466             stampSize.height;
35467             
35468         for ( var i = firstCol; i <= lastCol; i++ ) {
35469           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35470         }
35471     },
35472     */
35473     
35474     _getContainerSize : function()
35475     {
35476         this.maxY = Math.max.apply( Math, this.colYs );
35477         var size = {
35478             height: this.maxY
35479         };
35480       
35481         if ( this.isFitWidth ) {
35482             size.width = this._getContainerFitWidth();
35483         }
35484       
35485         return size;
35486     },
35487     
35488     _getContainerFitWidth : function()
35489     {
35490         var unusedCols = 0;
35491         // count unused columns
35492         var i = this.cols;
35493         while ( --i ) {
35494           if ( this.colYs[i] !== 0 ) {
35495             break;
35496           }
35497           unusedCols++;
35498         }
35499         // fit container to columns that have been used
35500         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35501     },
35502     
35503     needsResizeLayout : function()
35504     {
35505         var previousWidth = this.containerWidth;
35506         this.getContainerWidth();
35507         return previousWidth !== this.containerWidth;
35508     }
35509  
35510 });
35511
35512  
35513
35514  /*
35515  * - LGPL
35516  *
35517  * element
35518  * 
35519  */
35520
35521 /**
35522  * @class Roo.bootstrap.MasonryBrick
35523  * @extends Roo.bootstrap.Component
35524  * Bootstrap MasonryBrick class
35525  * 
35526  * @constructor
35527  * Create a new MasonryBrick
35528  * @param {Object} config The config object
35529  */
35530
35531 Roo.bootstrap.MasonryBrick = function(config){
35532     
35533     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35534     
35535     Roo.bootstrap.MasonryBrick.register(this);
35536     
35537     this.addEvents({
35538         // raw events
35539         /**
35540          * @event click
35541          * When a MasonryBrick is clcik
35542          * @param {Roo.bootstrap.MasonryBrick} this
35543          * @param {Roo.EventObject} e
35544          */
35545         "click" : true
35546     });
35547 };
35548
35549 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35550     
35551     /**
35552      * @cfg {String} title
35553      */   
35554     title : '',
35555     /**
35556      * @cfg {String} html
35557      */   
35558     html : '',
35559     /**
35560      * @cfg {String} bgimage
35561      */   
35562     bgimage : '',
35563     /**
35564      * @cfg {String} videourl
35565      */   
35566     videourl : '',
35567     /**
35568      * @cfg {String} cls
35569      */   
35570     cls : '',
35571     /**
35572      * @cfg {String} href
35573      */   
35574     href : '',
35575     /**
35576      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35577      */   
35578     size : 'xs',
35579     
35580     /**
35581      * @cfg {String} placetitle (center|bottom)
35582      */   
35583     placetitle : '',
35584     
35585     /**
35586      * @cfg {Boolean} isFitContainer defalut true
35587      */   
35588     isFitContainer : true, 
35589     
35590     /**
35591      * @cfg {Boolean} preventDefault defalut false
35592      */   
35593     preventDefault : false, 
35594     
35595     /**
35596      * @cfg {Boolean} inverse defalut false
35597      */   
35598     maskInverse : false, 
35599     
35600     getAutoCreate : function()
35601     {
35602         if(!this.isFitContainer){
35603             return this.getSplitAutoCreate();
35604         }
35605         
35606         var cls = 'masonry-brick masonry-brick-full';
35607         
35608         if(this.href.length){
35609             cls += ' masonry-brick-link';
35610         }
35611         
35612         if(this.bgimage.length){
35613             cls += ' masonry-brick-image';
35614         }
35615         
35616         if(this.maskInverse){
35617             cls += ' mask-inverse';
35618         }
35619         
35620         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35621             cls += ' enable-mask';
35622         }
35623         
35624         if(this.size){
35625             cls += ' masonry-' + this.size + '-brick';
35626         }
35627         
35628         if(this.placetitle.length){
35629             
35630             switch (this.placetitle) {
35631                 case 'center' :
35632                     cls += ' masonry-center-title';
35633                     break;
35634                 case 'bottom' :
35635                     cls += ' masonry-bottom-title';
35636                     break;
35637                 default:
35638                     break;
35639             }
35640             
35641         } else {
35642             if(!this.html.length && !this.bgimage.length){
35643                 cls += ' masonry-center-title';
35644             }
35645
35646             if(!this.html.length && this.bgimage.length){
35647                 cls += ' masonry-bottom-title';
35648             }
35649         }
35650         
35651         if(this.cls){
35652             cls += ' ' + this.cls;
35653         }
35654         
35655         var cfg = {
35656             tag: (this.href.length) ? 'a' : 'div',
35657             cls: cls,
35658             cn: [
35659                 {
35660                     tag: 'div',
35661                     cls: 'masonry-brick-mask'
35662                 },
35663                 {
35664                     tag: 'div',
35665                     cls: 'masonry-brick-paragraph',
35666                     cn: []
35667                 }
35668             ]
35669         };
35670         
35671         if(this.href.length){
35672             cfg.href = this.href;
35673         }
35674         
35675         var cn = cfg.cn[1].cn;
35676         
35677         if(this.title.length){
35678             cn.push({
35679                 tag: 'h4',
35680                 cls: 'masonry-brick-title',
35681                 html: this.title
35682             });
35683         }
35684         
35685         if(this.html.length){
35686             cn.push({
35687                 tag: 'p',
35688                 cls: 'masonry-brick-text',
35689                 html: this.html
35690             });
35691         }
35692         
35693         if (!this.title.length && !this.html.length) {
35694             cfg.cn[1].cls += ' hide';
35695         }
35696         
35697         if(this.bgimage.length){
35698             cfg.cn.push({
35699                 tag: 'img',
35700                 cls: 'masonry-brick-image-view',
35701                 src: this.bgimage
35702             });
35703         }
35704         
35705         if(this.videourl.length){
35706             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35707             // youtube support only?
35708             cfg.cn.push({
35709                 tag: 'iframe',
35710                 cls: 'masonry-brick-image-view',
35711                 src: vurl,
35712                 frameborder : 0,
35713                 allowfullscreen : true
35714             });
35715         }
35716         
35717         return cfg;
35718         
35719     },
35720     
35721     getSplitAutoCreate : function()
35722     {
35723         var cls = 'masonry-brick masonry-brick-split';
35724         
35725         if(this.href.length){
35726             cls += ' masonry-brick-link';
35727         }
35728         
35729         if(this.bgimage.length){
35730             cls += ' masonry-brick-image';
35731         }
35732         
35733         if(this.size){
35734             cls += ' masonry-' + this.size + '-brick';
35735         }
35736         
35737         switch (this.placetitle) {
35738             case 'center' :
35739                 cls += ' masonry-center-title';
35740                 break;
35741             case 'bottom' :
35742                 cls += ' masonry-bottom-title';
35743                 break;
35744             default:
35745                 if(!this.bgimage.length){
35746                     cls += ' masonry-center-title';
35747                 }
35748
35749                 if(this.bgimage.length){
35750                     cls += ' masonry-bottom-title';
35751                 }
35752                 break;
35753         }
35754         
35755         if(this.cls){
35756             cls += ' ' + this.cls;
35757         }
35758         
35759         var cfg = {
35760             tag: (this.href.length) ? 'a' : 'div',
35761             cls: cls,
35762             cn: [
35763                 {
35764                     tag: 'div',
35765                     cls: 'masonry-brick-split-head',
35766                     cn: [
35767                         {
35768                             tag: 'div',
35769                             cls: 'masonry-brick-paragraph',
35770                             cn: []
35771                         }
35772                     ]
35773                 },
35774                 {
35775                     tag: 'div',
35776                     cls: 'masonry-brick-split-body',
35777                     cn: []
35778                 }
35779             ]
35780         };
35781         
35782         if(this.href.length){
35783             cfg.href = this.href;
35784         }
35785         
35786         if(this.title.length){
35787             cfg.cn[0].cn[0].cn.push({
35788                 tag: 'h4',
35789                 cls: 'masonry-brick-title',
35790                 html: this.title
35791             });
35792         }
35793         
35794         if(this.html.length){
35795             cfg.cn[1].cn.push({
35796                 tag: 'p',
35797                 cls: 'masonry-brick-text',
35798                 html: this.html
35799             });
35800         }
35801
35802         if(this.bgimage.length){
35803             cfg.cn[0].cn.push({
35804                 tag: 'img',
35805                 cls: 'masonry-brick-image-view',
35806                 src: this.bgimage
35807             });
35808         }
35809         
35810         if(this.videourl.length){
35811             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35812             // youtube support only?
35813             cfg.cn[0].cn.cn.push({
35814                 tag: 'iframe',
35815                 cls: 'masonry-brick-image-view',
35816                 src: vurl,
35817                 frameborder : 0,
35818                 allowfullscreen : true
35819             });
35820         }
35821         
35822         return cfg;
35823     },
35824     
35825     initEvents: function() 
35826     {
35827         switch (this.size) {
35828             case 'xs' :
35829                 this.x = 1;
35830                 this.y = 1;
35831                 break;
35832             case 'sm' :
35833                 this.x = 2;
35834                 this.y = 2;
35835                 break;
35836             case 'md' :
35837             case 'md-left' :
35838             case 'md-right' :
35839                 this.x = 3;
35840                 this.y = 3;
35841                 break;
35842             case 'tall' :
35843                 this.x = 2;
35844                 this.y = 3;
35845                 break;
35846             case 'wide' :
35847                 this.x = 3;
35848                 this.y = 2;
35849                 break;
35850             case 'wide-thin' :
35851                 this.x = 3;
35852                 this.y = 1;
35853                 break;
35854                         
35855             default :
35856                 break;
35857         }
35858         
35859         if(Roo.isTouch){
35860             this.el.on('touchstart', this.onTouchStart, this);
35861             this.el.on('touchmove', this.onTouchMove, this);
35862             this.el.on('touchend', this.onTouchEnd, this);
35863             this.el.on('contextmenu', this.onContextMenu, this);
35864         } else {
35865             this.el.on('mouseenter'  ,this.enter, this);
35866             this.el.on('mouseleave', this.leave, this);
35867             this.el.on('click', this.onClick, this);
35868         }
35869         
35870         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35871             this.parent().bricks.push(this);   
35872         }
35873         
35874     },
35875     
35876     onClick: function(e, el)
35877     {
35878         var time = this.endTimer - this.startTimer;
35879         // Roo.log(e.preventDefault());
35880         if(Roo.isTouch){
35881             if(time > 1000){
35882                 e.preventDefault();
35883                 return;
35884             }
35885         }
35886         
35887         if(!this.preventDefault){
35888             return;
35889         }
35890         
35891         e.preventDefault();
35892         
35893         if (this.activeClass != '') {
35894             this.selectBrick();
35895         }
35896         
35897         this.fireEvent('click', this, e);
35898     },
35899     
35900     enter: function(e, el)
35901     {
35902         e.preventDefault();
35903         
35904         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35905             return;
35906         }
35907         
35908         if(this.bgimage.length && this.html.length){
35909             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35910         }
35911     },
35912     
35913     leave: function(e, el)
35914     {
35915         e.preventDefault();
35916         
35917         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35918             return;
35919         }
35920         
35921         if(this.bgimage.length && this.html.length){
35922             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35923         }
35924     },
35925     
35926     onTouchStart: function(e, el)
35927     {
35928 //        e.preventDefault();
35929         
35930         this.touchmoved = false;
35931         
35932         if(!this.isFitContainer){
35933             return;
35934         }
35935         
35936         if(!this.bgimage.length || !this.html.length){
35937             return;
35938         }
35939         
35940         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35941         
35942         this.timer = new Date().getTime();
35943         
35944     },
35945     
35946     onTouchMove: function(e, el)
35947     {
35948         this.touchmoved = true;
35949     },
35950     
35951     onContextMenu : function(e,el)
35952     {
35953         e.preventDefault();
35954         e.stopPropagation();
35955         return false;
35956     },
35957     
35958     onTouchEnd: function(e, el)
35959     {
35960 //        e.preventDefault();
35961         
35962         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35963         
35964             this.leave(e,el);
35965             
35966             return;
35967         }
35968         
35969         if(!this.bgimage.length || !this.html.length){
35970             
35971             if(this.href.length){
35972                 window.location.href = this.href;
35973             }
35974             
35975             return;
35976         }
35977         
35978         if(!this.isFitContainer){
35979             return;
35980         }
35981         
35982         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35983         
35984         window.location.href = this.href;
35985     },
35986     
35987     //selection on single brick only
35988     selectBrick : function() {
35989         
35990         if (!this.parentId) {
35991             return;
35992         }
35993         
35994         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35995         var index = m.selectedBrick.indexOf(this.id);
35996         
35997         if ( index > -1) {
35998             m.selectedBrick.splice(index,1);
35999             this.el.removeClass(this.activeClass);
36000             return;
36001         }
36002         
36003         for(var i = 0; i < m.selectedBrick.length; i++) {
36004             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36005             b.el.removeClass(b.activeClass);
36006         }
36007         
36008         m.selectedBrick = [];
36009         
36010         m.selectedBrick.push(this.id);
36011         this.el.addClass(this.activeClass);
36012         return;
36013     },
36014     
36015     isSelected : function(){
36016         return this.el.hasClass(this.activeClass);
36017         
36018     }
36019 });
36020
36021 Roo.apply(Roo.bootstrap.MasonryBrick, {
36022     
36023     //groups: {},
36024     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36025      /**
36026     * register a Masonry Brick
36027     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36028     */
36029     
36030     register : function(brick)
36031     {
36032         //this.groups[brick.id] = brick;
36033         this.groups.add(brick.id, brick);
36034     },
36035     /**
36036     * fetch a  masonry brick based on the masonry brick ID
36037     * @param {string} the masonry brick to add
36038     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36039     */
36040     
36041     get: function(brick_id) 
36042     {
36043         // if (typeof(this.groups[brick_id]) == 'undefined') {
36044         //     return false;
36045         // }
36046         // return this.groups[brick_id] ;
36047         
36048         if(this.groups.key(brick_id)) {
36049             return this.groups.key(brick_id);
36050         }
36051         
36052         return false;
36053     }
36054     
36055     
36056     
36057 });
36058
36059  /*
36060  * - LGPL
36061  *
36062  * element
36063  * 
36064  */
36065
36066 /**
36067  * @class Roo.bootstrap.Brick
36068  * @extends Roo.bootstrap.Component
36069  * Bootstrap Brick class
36070  * 
36071  * @constructor
36072  * Create a new Brick
36073  * @param {Object} config The config object
36074  */
36075
36076 Roo.bootstrap.Brick = function(config){
36077     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36078     
36079     this.addEvents({
36080         // raw events
36081         /**
36082          * @event click
36083          * When a Brick is click
36084          * @param {Roo.bootstrap.Brick} this
36085          * @param {Roo.EventObject} e
36086          */
36087         "click" : true
36088     });
36089 };
36090
36091 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36092     
36093     /**
36094      * @cfg {String} title
36095      */   
36096     title : '',
36097     /**
36098      * @cfg {String} html
36099      */   
36100     html : '',
36101     /**
36102      * @cfg {String} bgimage
36103      */   
36104     bgimage : '',
36105     /**
36106      * @cfg {String} cls
36107      */   
36108     cls : '',
36109     /**
36110      * @cfg {String} href
36111      */   
36112     href : '',
36113     /**
36114      * @cfg {String} video
36115      */   
36116     video : '',
36117     /**
36118      * @cfg {Boolean} square
36119      */   
36120     square : true,
36121     
36122     getAutoCreate : function()
36123     {
36124         var cls = 'roo-brick';
36125         
36126         if(this.href.length){
36127             cls += ' roo-brick-link';
36128         }
36129         
36130         if(this.bgimage.length){
36131             cls += ' roo-brick-image';
36132         }
36133         
36134         if(!this.html.length && !this.bgimage.length){
36135             cls += ' roo-brick-center-title';
36136         }
36137         
36138         if(!this.html.length && this.bgimage.length){
36139             cls += ' roo-brick-bottom-title';
36140         }
36141         
36142         if(this.cls){
36143             cls += ' ' + this.cls;
36144         }
36145         
36146         var cfg = {
36147             tag: (this.href.length) ? 'a' : 'div',
36148             cls: cls,
36149             cn: [
36150                 {
36151                     tag: 'div',
36152                     cls: 'roo-brick-paragraph',
36153                     cn: []
36154                 }
36155             ]
36156         };
36157         
36158         if(this.href.length){
36159             cfg.href = this.href;
36160         }
36161         
36162         var cn = cfg.cn[0].cn;
36163         
36164         if(this.title.length){
36165             cn.push({
36166                 tag: 'h4',
36167                 cls: 'roo-brick-title',
36168                 html: this.title
36169             });
36170         }
36171         
36172         if(this.html.length){
36173             cn.push({
36174                 tag: 'p',
36175                 cls: 'roo-brick-text',
36176                 html: this.html
36177             });
36178         } else {
36179             cn.cls += ' hide';
36180         }
36181         
36182         if(this.bgimage.length){
36183             cfg.cn.push({
36184                 tag: 'img',
36185                 cls: 'roo-brick-image-view',
36186                 src: this.bgimage
36187             });
36188         }
36189         
36190         return cfg;
36191     },
36192     
36193     initEvents: function() 
36194     {
36195         if(this.title.length || this.html.length){
36196             this.el.on('mouseenter'  ,this.enter, this);
36197             this.el.on('mouseleave', this.leave, this);
36198         }
36199         
36200         Roo.EventManager.onWindowResize(this.resize, this); 
36201         
36202         if(this.bgimage.length){
36203             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36204             this.imageEl.on('load', this.onImageLoad, this);
36205             return;
36206         }
36207         
36208         this.resize();
36209     },
36210     
36211     onImageLoad : function()
36212     {
36213         this.resize();
36214     },
36215     
36216     resize : function()
36217     {
36218         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36219         
36220         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36221         
36222         if(this.bgimage.length){
36223             var image = this.el.select('.roo-brick-image-view', true).first();
36224             
36225             image.setWidth(paragraph.getWidth());
36226             
36227             if(this.square){
36228                 image.setHeight(paragraph.getWidth());
36229             }
36230             
36231             this.el.setHeight(image.getHeight());
36232             paragraph.setHeight(image.getHeight());
36233             
36234         }
36235         
36236     },
36237     
36238     enter: function(e, el)
36239     {
36240         e.preventDefault();
36241         
36242         if(this.bgimage.length){
36243             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36244             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36245         }
36246     },
36247     
36248     leave: function(e, el)
36249     {
36250         e.preventDefault();
36251         
36252         if(this.bgimage.length){
36253             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36254             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36255         }
36256     }
36257     
36258 });
36259
36260  
36261
36262  /*
36263  * - LGPL
36264  *
36265  * Number field 
36266  */
36267
36268 /**
36269  * @class Roo.bootstrap.NumberField
36270  * @extends Roo.bootstrap.Input
36271  * Bootstrap NumberField class
36272  * 
36273  * 
36274  * 
36275  * 
36276  * @constructor
36277  * Create a new NumberField
36278  * @param {Object} config The config object
36279  */
36280
36281 Roo.bootstrap.NumberField = function(config){
36282     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36283 };
36284
36285 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36286     
36287     /**
36288      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36289      */
36290     allowDecimals : true,
36291     /**
36292      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36293      */
36294     decimalSeparator : ".",
36295     /**
36296      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36297      */
36298     decimalPrecision : 2,
36299     /**
36300      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36301      */
36302     allowNegative : true,
36303     
36304     /**
36305      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36306      */
36307     allowZero: true,
36308     /**
36309      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36310      */
36311     minValue : Number.NEGATIVE_INFINITY,
36312     /**
36313      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36314      */
36315     maxValue : Number.MAX_VALUE,
36316     /**
36317      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36318      */
36319     minText : "The minimum value for this field is {0}",
36320     /**
36321      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36322      */
36323     maxText : "The maximum value for this field is {0}",
36324     /**
36325      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36326      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36327      */
36328     nanText : "{0} is not a valid number",
36329     /**
36330      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36331      */
36332     thousandsDelimiter : false,
36333     /**
36334      * @cfg {String} valueAlign alignment of value
36335      */
36336     valueAlign : "left",
36337
36338     getAutoCreate : function()
36339     {
36340         var hiddenInput = {
36341             tag: 'input',
36342             type: 'hidden',
36343             id: Roo.id(),
36344             cls: 'hidden-number-input'
36345         };
36346         
36347         if (this.name) {
36348             hiddenInput.name = this.name;
36349         }
36350         
36351         this.name = '';
36352         
36353         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36354         
36355         this.name = hiddenInput.name;
36356         
36357         if(cfg.cn.length > 0) {
36358             cfg.cn.push(hiddenInput);
36359         }
36360         
36361         return cfg;
36362     },
36363
36364     // private
36365     initEvents : function()
36366     {   
36367         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36368         
36369         var allowed = "0123456789";
36370         
36371         if(this.allowDecimals){
36372             allowed += this.decimalSeparator;
36373         }
36374         
36375         if(this.allowNegative){
36376             allowed += "-";
36377         }
36378         
36379         if(this.thousandsDelimiter) {
36380             allowed += ",";
36381         }
36382         
36383         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36384         
36385         var keyPress = function(e){
36386             
36387             var k = e.getKey();
36388             
36389             var c = e.getCharCode();
36390             
36391             if(
36392                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36393                     allowed.indexOf(String.fromCharCode(c)) === -1
36394             ){
36395                 e.stopEvent();
36396                 return;
36397             }
36398             
36399             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36400                 return;
36401             }
36402             
36403             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36404                 e.stopEvent();
36405             }
36406         };
36407         
36408         this.el.on("keypress", keyPress, this);
36409     },
36410     
36411     validateValue : function(value)
36412     {
36413         
36414         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36415             return false;
36416         }
36417         
36418         var num = this.parseValue(value);
36419         
36420         if(isNaN(num)){
36421             this.markInvalid(String.format(this.nanText, value));
36422             return false;
36423         }
36424         
36425         if(num < this.minValue){
36426             this.markInvalid(String.format(this.minText, this.minValue));
36427             return false;
36428         }
36429         
36430         if(num > this.maxValue){
36431             this.markInvalid(String.format(this.maxText, this.maxValue));
36432             return false;
36433         }
36434         
36435         return true;
36436     },
36437
36438     getValue : function()
36439     {
36440         var v = this.hiddenEl().getValue();
36441         
36442         return this.fixPrecision(this.parseValue(v));
36443     },
36444
36445     parseValue : function(value)
36446     {
36447         if(this.thousandsDelimiter) {
36448             value += "";
36449             r = new RegExp(",", "g");
36450             value = value.replace(r, "");
36451         }
36452         
36453         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36454         return isNaN(value) ? '' : value;
36455     },
36456
36457     fixPrecision : function(value)
36458     {
36459         if(this.thousandsDelimiter) {
36460             value += "";
36461             r = new RegExp(",", "g");
36462             value = value.replace(r, "");
36463         }
36464         
36465         var nan = isNaN(value);
36466         
36467         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36468             return nan ? '' : value;
36469         }
36470         return parseFloat(value).toFixed(this.decimalPrecision);
36471     },
36472
36473     setValue : function(v)
36474     {
36475         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36476         
36477         this.value = v;
36478         
36479         if(this.rendered){
36480             
36481             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36482             
36483             this.inputEl().dom.value = (v == '') ? '' :
36484                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36485             
36486             if(!this.allowZero && v === '0') {
36487                 this.hiddenEl().dom.value = '';
36488                 this.inputEl().dom.value = '';
36489             }
36490             
36491             this.validate();
36492         }
36493     },
36494
36495     decimalPrecisionFcn : function(v)
36496     {
36497         return Math.floor(v);
36498     },
36499
36500     beforeBlur : function()
36501     {
36502         var v = this.parseValue(this.getRawValue());
36503         
36504         if(v || v === 0 || v === ''){
36505             this.setValue(v);
36506         }
36507     },
36508     
36509     hiddenEl : function()
36510     {
36511         return this.el.select('input.hidden-number-input',true).first();
36512     }
36513     
36514 });
36515
36516  
36517
36518 /*
36519 * Licence: LGPL
36520 */
36521
36522 /**
36523  * @class Roo.bootstrap.DocumentSlider
36524  * @extends Roo.bootstrap.Component
36525  * Bootstrap DocumentSlider class
36526  * 
36527  * @constructor
36528  * Create a new DocumentViewer
36529  * @param {Object} config The config object
36530  */
36531
36532 Roo.bootstrap.DocumentSlider = function(config){
36533     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36534     
36535     this.files = [];
36536     
36537     this.addEvents({
36538         /**
36539          * @event initial
36540          * Fire after initEvent
36541          * @param {Roo.bootstrap.DocumentSlider} this
36542          */
36543         "initial" : true,
36544         /**
36545          * @event update
36546          * Fire after update
36547          * @param {Roo.bootstrap.DocumentSlider} this
36548          */
36549         "update" : true,
36550         /**
36551          * @event click
36552          * Fire after click
36553          * @param {Roo.bootstrap.DocumentSlider} this
36554          */
36555         "click" : true
36556     });
36557 };
36558
36559 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36560     
36561     files : false,
36562     
36563     indicator : 0,
36564     
36565     getAutoCreate : function()
36566     {
36567         var cfg = {
36568             tag : 'div',
36569             cls : 'roo-document-slider',
36570             cn : [
36571                 {
36572                     tag : 'div',
36573                     cls : 'roo-document-slider-header',
36574                     cn : [
36575                         {
36576                             tag : 'div',
36577                             cls : 'roo-document-slider-header-title'
36578                         }
36579                     ]
36580                 },
36581                 {
36582                     tag : 'div',
36583                     cls : 'roo-document-slider-body',
36584                     cn : [
36585                         {
36586                             tag : 'div',
36587                             cls : 'roo-document-slider-prev',
36588                             cn : [
36589                                 {
36590                                     tag : 'i',
36591                                     cls : 'fa fa-chevron-left'
36592                                 }
36593                             ]
36594                         },
36595                         {
36596                             tag : 'div',
36597                             cls : 'roo-document-slider-thumb',
36598                             cn : [
36599                                 {
36600                                     tag : 'img',
36601                                     cls : 'roo-document-slider-image'
36602                                 }
36603                             ]
36604                         },
36605                         {
36606                             tag : 'div',
36607                             cls : 'roo-document-slider-next',
36608                             cn : [
36609                                 {
36610                                     tag : 'i',
36611                                     cls : 'fa fa-chevron-right'
36612                                 }
36613                             ]
36614                         }
36615                     ]
36616                 }
36617             ]
36618         };
36619         
36620         return cfg;
36621     },
36622     
36623     initEvents : function()
36624     {
36625         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36626         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36627         
36628         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36629         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36630         
36631         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36632         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36633         
36634         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36635         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36636         
36637         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36638         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36639         
36640         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36641         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36642         
36643         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36644         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36645         
36646         this.thumbEl.on('click', this.onClick, this);
36647         
36648         this.prevIndicator.on('click', this.prev, this);
36649         
36650         this.nextIndicator.on('click', this.next, this);
36651         
36652     },
36653     
36654     initial : function()
36655     {
36656         if(this.files.length){
36657             this.indicator = 1;
36658             this.update()
36659         }
36660         
36661         this.fireEvent('initial', this);
36662     },
36663     
36664     update : function()
36665     {
36666         this.imageEl.attr('src', this.files[this.indicator - 1]);
36667         
36668         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36669         
36670         this.prevIndicator.show();
36671         
36672         if(this.indicator == 1){
36673             this.prevIndicator.hide();
36674         }
36675         
36676         this.nextIndicator.show();
36677         
36678         if(this.indicator == this.files.length){
36679             this.nextIndicator.hide();
36680         }
36681         
36682         this.thumbEl.scrollTo('top');
36683         
36684         this.fireEvent('update', this);
36685     },
36686     
36687     onClick : function(e)
36688     {
36689         e.preventDefault();
36690         
36691         this.fireEvent('click', this);
36692     },
36693     
36694     prev : function(e)
36695     {
36696         e.preventDefault();
36697         
36698         this.indicator = Math.max(1, this.indicator - 1);
36699         
36700         this.update();
36701     },
36702     
36703     next : function(e)
36704     {
36705         e.preventDefault();
36706         
36707         this.indicator = Math.min(this.files.length, this.indicator + 1);
36708         
36709         this.update();
36710     }
36711 });
36712 /*
36713  * - LGPL
36714  *
36715  * RadioSet
36716  *
36717  *
36718  */
36719
36720 /**
36721  * @class Roo.bootstrap.RadioSet
36722  * @extends Roo.bootstrap.Input
36723  * Bootstrap RadioSet class
36724  * @cfg {String} indicatorpos (left|right) default left
36725  * @cfg {Boolean} inline (true|false) inline the element (default true)
36726  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36727  * @constructor
36728  * Create a new RadioSet
36729  * @param {Object} config The config object
36730  */
36731
36732 Roo.bootstrap.RadioSet = function(config){
36733     
36734     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36735     
36736     this.radioes = [];
36737     
36738     Roo.bootstrap.RadioSet.register(this);
36739     
36740     this.addEvents({
36741         /**
36742         * @event check
36743         * Fires when the element is checked or unchecked.
36744         * @param {Roo.bootstrap.RadioSet} this This radio
36745         * @param {Roo.bootstrap.Radio} item The checked item
36746         */
36747        check : true,
36748        /**
36749         * @event click
36750         * Fires when the element is click.
36751         * @param {Roo.bootstrap.RadioSet} this This radio set
36752         * @param {Roo.bootstrap.Radio} item The checked item
36753         * @param {Roo.EventObject} e The event object
36754         */
36755        click : true
36756     });
36757     
36758 };
36759
36760 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36761
36762     radioes : false,
36763     
36764     inline : true,
36765     
36766     weight : '',
36767     
36768     indicatorpos : 'left',
36769     
36770     getAutoCreate : function()
36771     {
36772         var label = {
36773             tag : 'label',
36774             cls : 'roo-radio-set-label',
36775             cn : [
36776                 {
36777                     tag : 'span',
36778                     html : this.fieldLabel
36779                 }
36780             ]
36781         };
36782         if (Roo.bootstrap.version == 3) {
36783             
36784             
36785             if(this.indicatorpos == 'left'){
36786                 label.cn.unshift({
36787                     tag : 'i',
36788                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36789                     tooltip : 'This field is required'
36790                 });
36791             } else {
36792                 label.cn.push({
36793                     tag : 'i',
36794                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36795                     tooltip : 'This field is required'
36796                 });
36797             }
36798         }
36799         var items = {
36800             tag : 'div',
36801             cls : 'roo-radio-set-items'
36802         };
36803         
36804         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36805         
36806         if (align === 'left' && this.fieldLabel.length) {
36807             
36808             items = {
36809                 cls : "roo-radio-set-right", 
36810                 cn: [
36811                     items
36812                 ]
36813             };
36814             
36815             if(this.labelWidth > 12){
36816                 label.style = "width: " + this.labelWidth + 'px';
36817             }
36818             
36819             if(this.labelWidth < 13 && this.labelmd == 0){
36820                 this.labelmd = this.labelWidth;
36821             }
36822             
36823             if(this.labellg > 0){
36824                 label.cls += ' col-lg-' + this.labellg;
36825                 items.cls += ' col-lg-' + (12 - this.labellg);
36826             }
36827             
36828             if(this.labelmd > 0){
36829                 label.cls += ' col-md-' + this.labelmd;
36830                 items.cls += ' col-md-' + (12 - this.labelmd);
36831             }
36832             
36833             if(this.labelsm > 0){
36834                 label.cls += ' col-sm-' + this.labelsm;
36835                 items.cls += ' col-sm-' + (12 - this.labelsm);
36836             }
36837             
36838             if(this.labelxs > 0){
36839                 label.cls += ' col-xs-' + this.labelxs;
36840                 items.cls += ' col-xs-' + (12 - this.labelxs);
36841             }
36842         }
36843         
36844         var cfg = {
36845             tag : 'div',
36846             cls : 'roo-radio-set',
36847             cn : [
36848                 {
36849                     tag : 'input',
36850                     cls : 'roo-radio-set-input',
36851                     type : 'hidden',
36852                     name : this.name,
36853                     value : this.value ? this.value :  ''
36854                 },
36855                 label,
36856                 items
36857             ]
36858         };
36859         
36860         if(this.weight.length){
36861             cfg.cls += ' roo-radio-' + this.weight;
36862         }
36863         
36864         if(this.inline) {
36865             cfg.cls += ' roo-radio-set-inline';
36866         }
36867         
36868         var settings=this;
36869         ['xs','sm','md','lg'].map(function(size){
36870             if (settings[size]) {
36871                 cfg.cls += ' col-' + size + '-' + settings[size];
36872             }
36873         });
36874         
36875         return cfg;
36876         
36877     },
36878
36879     initEvents : function()
36880     {
36881         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36882         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36883         
36884         if(!this.fieldLabel.length){
36885             this.labelEl.hide();
36886         }
36887         
36888         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36889         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36890         
36891         this.indicator = this.indicatorEl();
36892         
36893         if(this.indicator){
36894             this.indicator.addClass('invisible');
36895         }
36896         
36897         this.originalValue = this.getValue();
36898         
36899     },
36900     
36901     inputEl: function ()
36902     {
36903         return this.el.select('.roo-radio-set-input', true).first();
36904     },
36905     
36906     getChildContainer : function()
36907     {
36908         return this.itemsEl;
36909     },
36910     
36911     register : function(item)
36912     {
36913         this.radioes.push(item);
36914         
36915     },
36916     
36917     validate : function()
36918     {   
36919         if(this.getVisibilityEl().hasClass('hidden')){
36920             return true;
36921         }
36922         
36923         var valid = false;
36924         
36925         Roo.each(this.radioes, function(i){
36926             if(!i.checked){
36927                 return;
36928             }
36929             
36930             valid = true;
36931             return false;
36932         });
36933         
36934         if(this.allowBlank) {
36935             return true;
36936         }
36937         
36938         if(this.disabled || valid){
36939             this.markValid();
36940             return true;
36941         }
36942         
36943         this.markInvalid();
36944         return false;
36945         
36946     },
36947     
36948     markValid : function()
36949     {
36950         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36951             this.indicatorEl().removeClass('visible');
36952             this.indicatorEl().addClass('invisible');
36953         }
36954         
36955         
36956         if (Roo.bootstrap.version == 3) {
36957             this.el.removeClass([this.invalidClass, this.validClass]);
36958             this.el.addClass(this.validClass);
36959         } else {
36960             this.el.removeClass(['is-invalid','is-valid']);
36961             this.el.addClass(['is-valid']);
36962         }
36963         this.fireEvent('valid', this);
36964     },
36965     
36966     markInvalid : function(msg)
36967     {
36968         if(this.allowBlank || this.disabled){
36969             return;
36970         }
36971         
36972         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36973             this.indicatorEl().removeClass('invisible');
36974             this.indicatorEl().addClass('visible');
36975         }
36976         if (Roo.bootstrap.version == 3) {
36977             this.el.removeClass([this.invalidClass, this.validClass]);
36978             this.el.addClass(this.invalidClass);
36979         } else {
36980             this.el.removeClass(['is-invalid','is-valid']);
36981             this.el.addClass(['is-invalid']);
36982         }
36983         
36984         this.fireEvent('invalid', this, msg);
36985         
36986     },
36987     
36988     setValue : function(v, suppressEvent)
36989     {   
36990         if(this.value === v){
36991             return;
36992         }
36993         
36994         this.value = v;
36995         
36996         if(this.rendered){
36997             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36998         }
36999         
37000         Roo.each(this.radioes, function(i){
37001             i.checked = false;
37002             i.el.removeClass('checked');
37003         });
37004         
37005         Roo.each(this.radioes, function(i){
37006             
37007             if(i.value === v || i.value.toString() === v.toString()){
37008                 i.checked = true;
37009                 i.el.addClass('checked');
37010                 
37011                 if(suppressEvent !== true){
37012                     this.fireEvent('check', this, i);
37013                 }
37014                 
37015                 return false;
37016             }
37017             
37018         }, this);
37019         
37020         this.validate();
37021     },
37022     
37023     clearInvalid : function(){
37024         
37025         if(!this.el || this.preventMark){
37026             return;
37027         }
37028         
37029         this.el.removeClass([this.invalidClass]);
37030         
37031         this.fireEvent('valid', this);
37032     }
37033     
37034 });
37035
37036 Roo.apply(Roo.bootstrap.RadioSet, {
37037     
37038     groups: {},
37039     
37040     register : function(set)
37041     {
37042         this.groups[set.name] = set;
37043     },
37044     
37045     get: function(name) 
37046     {
37047         if (typeof(this.groups[name]) == 'undefined') {
37048             return false;
37049         }
37050         
37051         return this.groups[name] ;
37052     }
37053     
37054 });
37055 /*
37056  * Based on:
37057  * Ext JS Library 1.1.1
37058  * Copyright(c) 2006-2007, Ext JS, LLC.
37059  *
37060  * Originally Released Under LGPL - original licence link has changed is not relivant.
37061  *
37062  * Fork - LGPL
37063  * <script type="text/javascript">
37064  */
37065
37066
37067 /**
37068  * @class Roo.bootstrap.SplitBar
37069  * @extends Roo.util.Observable
37070  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37071  * <br><br>
37072  * Usage:
37073  * <pre><code>
37074 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37075                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37076 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37077 split.minSize = 100;
37078 split.maxSize = 600;
37079 split.animate = true;
37080 split.on('moved', splitterMoved);
37081 </code></pre>
37082  * @constructor
37083  * Create a new SplitBar
37084  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37085  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37086  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37087  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37088                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37089                         position of the SplitBar).
37090  */
37091 Roo.bootstrap.SplitBar = function(cfg){
37092     
37093     /** @private */
37094     
37095     //{
37096     //  dragElement : elm
37097     //  resizingElement: el,
37098         // optional..
37099     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37100     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37101         // existingProxy ???
37102     //}
37103     
37104     this.el = Roo.get(cfg.dragElement, true);
37105     this.el.dom.unselectable = "on";
37106     /** @private */
37107     this.resizingEl = Roo.get(cfg.resizingElement, true);
37108
37109     /**
37110      * @private
37111      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37112      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37113      * @type Number
37114      */
37115     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37116     
37117     /**
37118      * The minimum size of the resizing element. (Defaults to 0)
37119      * @type Number
37120      */
37121     this.minSize = 0;
37122     
37123     /**
37124      * The maximum size of the resizing element. (Defaults to 2000)
37125      * @type Number
37126      */
37127     this.maxSize = 2000;
37128     
37129     /**
37130      * Whether to animate the transition to the new size
37131      * @type Boolean
37132      */
37133     this.animate = false;
37134     
37135     /**
37136      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37137      * @type Boolean
37138      */
37139     this.useShim = false;
37140     
37141     /** @private */
37142     this.shim = null;
37143     
37144     if(!cfg.existingProxy){
37145         /** @private */
37146         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37147     }else{
37148         this.proxy = Roo.get(cfg.existingProxy).dom;
37149     }
37150     /** @private */
37151     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37152     
37153     /** @private */
37154     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37155     
37156     /** @private */
37157     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37158     
37159     /** @private */
37160     this.dragSpecs = {};
37161     
37162     /**
37163      * @private The adapter to use to positon and resize elements
37164      */
37165     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37166     this.adapter.init(this);
37167     
37168     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37169         /** @private */
37170         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37171         this.el.addClass("roo-splitbar-h");
37172     }else{
37173         /** @private */
37174         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37175         this.el.addClass("roo-splitbar-v");
37176     }
37177     
37178     this.addEvents({
37179         /**
37180          * @event resize
37181          * Fires when the splitter is moved (alias for {@link #event-moved})
37182          * @param {Roo.bootstrap.SplitBar} this
37183          * @param {Number} newSize the new width or height
37184          */
37185         "resize" : true,
37186         /**
37187          * @event moved
37188          * Fires when the splitter is moved
37189          * @param {Roo.bootstrap.SplitBar} this
37190          * @param {Number} newSize the new width or height
37191          */
37192         "moved" : true,
37193         /**
37194          * @event beforeresize
37195          * Fires before the splitter is dragged
37196          * @param {Roo.bootstrap.SplitBar} this
37197          */
37198         "beforeresize" : true,
37199
37200         "beforeapply" : true
37201     });
37202
37203     Roo.util.Observable.call(this);
37204 };
37205
37206 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37207     onStartProxyDrag : function(x, y){
37208         this.fireEvent("beforeresize", this);
37209         if(!this.overlay){
37210             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37211             o.unselectable();
37212             o.enableDisplayMode("block");
37213             // all splitbars share the same overlay
37214             Roo.bootstrap.SplitBar.prototype.overlay = o;
37215         }
37216         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37217         this.overlay.show();
37218         Roo.get(this.proxy).setDisplayed("block");
37219         var size = this.adapter.getElementSize(this);
37220         this.activeMinSize = this.getMinimumSize();;
37221         this.activeMaxSize = this.getMaximumSize();;
37222         var c1 = size - this.activeMinSize;
37223         var c2 = Math.max(this.activeMaxSize - size, 0);
37224         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37225             this.dd.resetConstraints();
37226             this.dd.setXConstraint(
37227                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37228                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37229             );
37230             this.dd.setYConstraint(0, 0);
37231         }else{
37232             this.dd.resetConstraints();
37233             this.dd.setXConstraint(0, 0);
37234             this.dd.setYConstraint(
37235                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37236                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37237             );
37238          }
37239         this.dragSpecs.startSize = size;
37240         this.dragSpecs.startPoint = [x, y];
37241         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37242     },
37243     
37244     /** 
37245      * @private Called after the drag operation by the DDProxy
37246      */
37247     onEndProxyDrag : function(e){
37248         Roo.get(this.proxy).setDisplayed(false);
37249         var endPoint = Roo.lib.Event.getXY(e);
37250         if(this.overlay){
37251             this.overlay.hide();
37252         }
37253         var newSize;
37254         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37255             newSize = this.dragSpecs.startSize + 
37256                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37257                     endPoint[0] - this.dragSpecs.startPoint[0] :
37258                     this.dragSpecs.startPoint[0] - endPoint[0]
37259                 );
37260         }else{
37261             newSize = this.dragSpecs.startSize + 
37262                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37263                     endPoint[1] - this.dragSpecs.startPoint[1] :
37264                     this.dragSpecs.startPoint[1] - endPoint[1]
37265                 );
37266         }
37267         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37268         if(newSize != this.dragSpecs.startSize){
37269             if(this.fireEvent('beforeapply', this, newSize) !== false){
37270                 this.adapter.setElementSize(this, newSize);
37271                 this.fireEvent("moved", this, newSize);
37272                 this.fireEvent("resize", this, newSize);
37273             }
37274         }
37275     },
37276     
37277     /**
37278      * Get the adapter this SplitBar uses
37279      * @return The adapter object
37280      */
37281     getAdapter : function(){
37282         return this.adapter;
37283     },
37284     
37285     /**
37286      * Set the adapter this SplitBar uses
37287      * @param {Object} adapter A SplitBar adapter object
37288      */
37289     setAdapter : function(adapter){
37290         this.adapter = adapter;
37291         this.adapter.init(this);
37292     },
37293     
37294     /**
37295      * Gets the minimum size for the resizing element
37296      * @return {Number} The minimum size
37297      */
37298     getMinimumSize : function(){
37299         return this.minSize;
37300     },
37301     
37302     /**
37303      * Sets the minimum size for the resizing element
37304      * @param {Number} minSize The minimum size
37305      */
37306     setMinimumSize : function(minSize){
37307         this.minSize = minSize;
37308     },
37309     
37310     /**
37311      * Gets the maximum size for the resizing element
37312      * @return {Number} The maximum size
37313      */
37314     getMaximumSize : function(){
37315         return this.maxSize;
37316     },
37317     
37318     /**
37319      * Sets the maximum size for the resizing element
37320      * @param {Number} maxSize The maximum size
37321      */
37322     setMaximumSize : function(maxSize){
37323         this.maxSize = maxSize;
37324     },
37325     
37326     /**
37327      * Sets the initialize size for the resizing element
37328      * @param {Number} size The initial size
37329      */
37330     setCurrentSize : function(size){
37331         var oldAnimate = this.animate;
37332         this.animate = false;
37333         this.adapter.setElementSize(this, size);
37334         this.animate = oldAnimate;
37335     },
37336     
37337     /**
37338      * Destroy this splitbar. 
37339      * @param {Boolean} removeEl True to remove the element
37340      */
37341     destroy : function(removeEl){
37342         if(this.shim){
37343             this.shim.remove();
37344         }
37345         this.dd.unreg();
37346         this.proxy.parentNode.removeChild(this.proxy);
37347         if(removeEl){
37348             this.el.remove();
37349         }
37350     }
37351 });
37352
37353 /**
37354  * @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.
37355  */
37356 Roo.bootstrap.SplitBar.createProxy = function(dir){
37357     var proxy = new Roo.Element(document.createElement("div"));
37358     proxy.unselectable();
37359     var cls = 'roo-splitbar-proxy';
37360     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37361     document.body.appendChild(proxy.dom);
37362     return proxy.dom;
37363 };
37364
37365 /** 
37366  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37367  * Default Adapter. It assumes the splitter and resizing element are not positioned
37368  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37369  */
37370 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37371 };
37372
37373 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37374     // do nothing for now
37375     init : function(s){
37376     
37377     },
37378     /**
37379      * Called before drag operations to get the current size of the resizing element. 
37380      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37381      */
37382      getElementSize : function(s){
37383         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37384             return s.resizingEl.getWidth();
37385         }else{
37386             return s.resizingEl.getHeight();
37387         }
37388     },
37389     
37390     /**
37391      * Called after drag operations to set the size of the resizing element.
37392      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37393      * @param {Number} newSize The new size to set
37394      * @param {Function} onComplete A function to be invoked when resizing is complete
37395      */
37396     setElementSize : function(s, newSize, onComplete){
37397         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37398             if(!s.animate){
37399                 s.resizingEl.setWidth(newSize);
37400                 if(onComplete){
37401                     onComplete(s, newSize);
37402                 }
37403             }else{
37404                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37405             }
37406         }else{
37407             
37408             if(!s.animate){
37409                 s.resizingEl.setHeight(newSize);
37410                 if(onComplete){
37411                     onComplete(s, newSize);
37412                 }
37413             }else{
37414                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37415             }
37416         }
37417     }
37418 };
37419
37420 /** 
37421  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37422  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37423  * Adapter that  moves the splitter element to align with the resized sizing element. 
37424  * Used with an absolute positioned SplitBar.
37425  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37426  * document.body, make sure you assign an id to the body element.
37427  */
37428 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37429     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37430     this.container = Roo.get(container);
37431 };
37432
37433 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37434     init : function(s){
37435         this.basic.init(s);
37436     },
37437     
37438     getElementSize : function(s){
37439         return this.basic.getElementSize(s);
37440     },
37441     
37442     setElementSize : function(s, newSize, onComplete){
37443         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37444     },
37445     
37446     moveSplitter : function(s){
37447         var yes = Roo.bootstrap.SplitBar;
37448         switch(s.placement){
37449             case yes.LEFT:
37450                 s.el.setX(s.resizingEl.getRight());
37451                 break;
37452             case yes.RIGHT:
37453                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37454                 break;
37455             case yes.TOP:
37456                 s.el.setY(s.resizingEl.getBottom());
37457                 break;
37458             case yes.BOTTOM:
37459                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37460                 break;
37461         }
37462     }
37463 };
37464
37465 /**
37466  * Orientation constant - Create a vertical SplitBar
37467  * @static
37468  * @type Number
37469  */
37470 Roo.bootstrap.SplitBar.VERTICAL = 1;
37471
37472 /**
37473  * Orientation constant - Create a horizontal SplitBar
37474  * @static
37475  * @type Number
37476  */
37477 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37478
37479 /**
37480  * Placement constant - The resizing element is to the left of the splitter element
37481  * @static
37482  * @type Number
37483  */
37484 Roo.bootstrap.SplitBar.LEFT = 1;
37485
37486 /**
37487  * Placement constant - The resizing element is to the right of the splitter element
37488  * @static
37489  * @type Number
37490  */
37491 Roo.bootstrap.SplitBar.RIGHT = 2;
37492
37493 /**
37494  * Placement constant - The resizing element is positioned above the splitter element
37495  * @static
37496  * @type Number
37497  */
37498 Roo.bootstrap.SplitBar.TOP = 3;
37499
37500 /**
37501  * Placement constant - The resizing element is positioned under splitter element
37502  * @static
37503  * @type Number
37504  */
37505 Roo.bootstrap.SplitBar.BOTTOM = 4;
37506 Roo.namespace("Roo.bootstrap.layout");/*
37507  * Based on:
37508  * Ext JS Library 1.1.1
37509  * Copyright(c) 2006-2007, Ext JS, LLC.
37510  *
37511  * Originally Released Under LGPL - original licence link has changed is not relivant.
37512  *
37513  * Fork - LGPL
37514  * <script type="text/javascript">
37515  */
37516
37517 /**
37518  * @class Roo.bootstrap.layout.Manager
37519  * @extends Roo.bootstrap.Component
37520  * Base class for layout managers.
37521  */
37522 Roo.bootstrap.layout.Manager = function(config)
37523 {
37524     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37525
37526
37527
37528
37529
37530     /** false to disable window resize monitoring @type Boolean */
37531     this.monitorWindowResize = true;
37532     this.regions = {};
37533     this.addEvents({
37534         /**
37535          * @event layout
37536          * Fires when a layout is performed.
37537          * @param {Roo.LayoutManager} this
37538          */
37539         "layout" : true,
37540         /**
37541          * @event regionresized
37542          * Fires when the user resizes a region.
37543          * @param {Roo.LayoutRegion} region The resized region
37544          * @param {Number} newSize The new size (width for east/west, height for north/south)
37545          */
37546         "regionresized" : true,
37547         /**
37548          * @event regioncollapsed
37549          * Fires when a region is collapsed.
37550          * @param {Roo.LayoutRegion} region The collapsed region
37551          */
37552         "regioncollapsed" : true,
37553         /**
37554          * @event regionexpanded
37555          * Fires when a region is expanded.
37556          * @param {Roo.LayoutRegion} region The expanded region
37557          */
37558         "regionexpanded" : true
37559     });
37560     this.updating = false;
37561
37562     if (config.el) {
37563         this.el = Roo.get(config.el);
37564         this.initEvents();
37565     }
37566
37567 };
37568
37569 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37570
37571
37572     regions : null,
37573
37574     monitorWindowResize : true,
37575
37576
37577     updating : false,
37578
37579
37580     onRender : function(ct, position)
37581     {
37582         if(!this.el){
37583             this.el = Roo.get(ct);
37584             this.initEvents();
37585         }
37586         //this.fireEvent('render',this);
37587     },
37588
37589
37590     initEvents: function()
37591     {
37592
37593
37594         // ie scrollbar fix
37595         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37596             document.body.scroll = "no";
37597         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37598             this.el.position('relative');
37599         }
37600         this.id = this.el.id;
37601         this.el.addClass("roo-layout-container");
37602         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37603         if(this.el.dom != document.body ) {
37604             this.el.on('resize', this.layout,this);
37605             this.el.on('show', this.layout,this);
37606         }
37607
37608     },
37609
37610     /**
37611      * Returns true if this layout is currently being updated
37612      * @return {Boolean}
37613      */
37614     isUpdating : function(){
37615         return this.updating;
37616     },
37617
37618     /**
37619      * Suspend the LayoutManager from doing auto-layouts while
37620      * making multiple add or remove calls
37621      */
37622     beginUpdate : function(){
37623         this.updating = true;
37624     },
37625
37626     /**
37627      * Restore auto-layouts and optionally disable the manager from performing a layout
37628      * @param {Boolean} noLayout true to disable a layout update
37629      */
37630     endUpdate : function(noLayout){
37631         this.updating = false;
37632         if(!noLayout){
37633             this.layout();
37634         }
37635     },
37636
37637     layout: function(){
37638         // abstract...
37639     },
37640
37641     onRegionResized : function(region, newSize){
37642         this.fireEvent("regionresized", region, newSize);
37643         this.layout();
37644     },
37645
37646     onRegionCollapsed : function(region){
37647         this.fireEvent("regioncollapsed", region);
37648     },
37649
37650     onRegionExpanded : function(region){
37651         this.fireEvent("regionexpanded", region);
37652     },
37653
37654     /**
37655      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37656      * performs box-model adjustments.
37657      * @return {Object} The size as an object {width: (the width), height: (the height)}
37658      */
37659     getViewSize : function()
37660     {
37661         var size;
37662         if(this.el.dom != document.body){
37663             size = this.el.getSize();
37664         }else{
37665             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37666         }
37667         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37668         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37669         return size;
37670     },
37671
37672     /**
37673      * Returns the Element this layout is bound to.
37674      * @return {Roo.Element}
37675      */
37676     getEl : function(){
37677         return this.el;
37678     },
37679
37680     /**
37681      * Returns the specified region.
37682      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37683      * @return {Roo.LayoutRegion}
37684      */
37685     getRegion : function(target){
37686         return this.regions[target.toLowerCase()];
37687     },
37688
37689     onWindowResize : function(){
37690         if(this.monitorWindowResize){
37691             this.layout();
37692         }
37693     }
37694 });
37695 /*
37696  * Based on:
37697  * Ext JS Library 1.1.1
37698  * Copyright(c) 2006-2007, Ext JS, LLC.
37699  *
37700  * Originally Released Under LGPL - original licence link has changed is not relivant.
37701  *
37702  * Fork - LGPL
37703  * <script type="text/javascript">
37704  */
37705 /**
37706  * @class Roo.bootstrap.layout.Border
37707  * @extends Roo.bootstrap.layout.Manager
37708  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37709  * please see: examples/bootstrap/nested.html<br><br>
37710  
37711 <b>The container the layout is rendered into can be either the body element or any other element.
37712 If it is not the body element, the container needs to either be an absolute positioned element,
37713 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37714 the container size if it is not the body element.</b>
37715
37716 * @constructor
37717 * Create a new Border
37718 * @param {Object} config Configuration options
37719  */
37720 Roo.bootstrap.layout.Border = function(config){
37721     config = config || {};
37722     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37723     
37724     
37725     
37726     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37727         if(config[region]){
37728             config[region].region = region;
37729             this.addRegion(config[region]);
37730         }
37731     },this);
37732     
37733 };
37734
37735 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37736
37737 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37738     
37739     parent : false, // this might point to a 'nest' or a ???
37740     
37741     /**
37742      * Creates and adds a new region if it doesn't already exist.
37743      * @param {String} target The target region key (north, south, east, west or center).
37744      * @param {Object} config The regions config object
37745      * @return {BorderLayoutRegion} The new region
37746      */
37747     addRegion : function(config)
37748     {
37749         if(!this.regions[config.region]){
37750             var r = this.factory(config);
37751             this.bindRegion(r);
37752         }
37753         return this.regions[config.region];
37754     },
37755
37756     // private (kinda)
37757     bindRegion : function(r){
37758         this.regions[r.config.region] = r;
37759         
37760         r.on("visibilitychange",    this.layout, this);
37761         r.on("paneladded",          this.layout, this);
37762         r.on("panelremoved",        this.layout, this);
37763         r.on("invalidated",         this.layout, this);
37764         r.on("resized",             this.onRegionResized, this);
37765         r.on("collapsed",           this.onRegionCollapsed, this);
37766         r.on("expanded",            this.onRegionExpanded, this);
37767     },
37768
37769     /**
37770      * Performs a layout update.
37771      */
37772     layout : function()
37773     {
37774         if(this.updating) {
37775             return;
37776         }
37777         
37778         // render all the rebions if they have not been done alreayd?
37779         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37780             if(this.regions[region] && !this.regions[region].bodyEl){
37781                 this.regions[region].onRender(this.el)
37782             }
37783         },this);
37784         
37785         var size = this.getViewSize();
37786         var w = size.width;
37787         var h = size.height;
37788         var centerW = w;
37789         var centerH = h;
37790         var centerY = 0;
37791         var centerX = 0;
37792         //var x = 0, y = 0;
37793
37794         var rs = this.regions;
37795         var north = rs["north"];
37796         var south = rs["south"]; 
37797         var west = rs["west"];
37798         var east = rs["east"];
37799         var center = rs["center"];
37800         //if(this.hideOnLayout){ // not supported anymore
37801             //c.el.setStyle("display", "none");
37802         //}
37803         if(north && north.isVisible()){
37804             var b = north.getBox();
37805             var m = north.getMargins();
37806             b.width = w - (m.left+m.right);
37807             b.x = m.left;
37808             b.y = m.top;
37809             centerY = b.height + b.y + m.bottom;
37810             centerH -= centerY;
37811             north.updateBox(this.safeBox(b));
37812         }
37813         if(south && south.isVisible()){
37814             var b = south.getBox();
37815             var m = south.getMargins();
37816             b.width = w - (m.left+m.right);
37817             b.x = m.left;
37818             var totalHeight = (b.height + m.top + m.bottom);
37819             b.y = h - totalHeight + m.top;
37820             centerH -= totalHeight;
37821             south.updateBox(this.safeBox(b));
37822         }
37823         if(west && west.isVisible()){
37824             var b = west.getBox();
37825             var m = west.getMargins();
37826             b.height = centerH - (m.top+m.bottom);
37827             b.x = m.left;
37828             b.y = centerY + m.top;
37829             var totalWidth = (b.width + m.left + m.right);
37830             centerX += totalWidth;
37831             centerW -= totalWidth;
37832             west.updateBox(this.safeBox(b));
37833         }
37834         if(east && east.isVisible()){
37835             var b = east.getBox();
37836             var m = east.getMargins();
37837             b.height = centerH - (m.top+m.bottom);
37838             var totalWidth = (b.width + m.left + m.right);
37839             b.x = w - totalWidth + m.left;
37840             b.y = centerY + m.top;
37841             centerW -= totalWidth;
37842             east.updateBox(this.safeBox(b));
37843         }
37844         if(center){
37845             var m = center.getMargins();
37846             var centerBox = {
37847                 x: centerX + m.left,
37848                 y: centerY + m.top,
37849                 width: centerW - (m.left+m.right),
37850                 height: centerH - (m.top+m.bottom)
37851             };
37852             //if(this.hideOnLayout){
37853                 //center.el.setStyle("display", "block");
37854             //}
37855             center.updateBox(this.safeBox(centerBox));
37856         }
37857         this.el.repaint();
37858         this.fireEvent("layout", this);
37859     },
37860
37861     // private
37862     safeBox : function(box){
37863         box.width = Math.max(0, box.width);
37864         box.height = Math.max(0, box.height);
37865         return box;
37866     },
37867
37868     /**
37869      * Adds a ContentPanel (or subclass) to this layout.
37870      * @param {String} target The target region key (north, south, east, west or center).
37871      * @param {Roo.ContentPanel} panel The panel to add
37872      * @return {Roo.ContentPanel} The added panel
37873      */
37874     add : function(target, panel){
37875          
37876         target = target.toLowerCase();
37877         return this.regions[target].add(panel);
37878     },
37879
37880     /**
37881      * Remove a ContentPanel (or subclass) to this layout.
37882      * @param {String} target The target region key (north, south, east, west or center).
37883      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37884      * @return {Roo.ContentPanel} The removed panel
37885      */
37886     remove : function(target, panel){
37887         target = target.toLowerCase();
37888         return this.regions[target].remove(panel);
37889     },
37890
37891     /**
37892      * Searches all regions for a panel with the specified id
37893      * @param {String} panelId
37894      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37895      */
37896     findPanel : function(panelId){
37897         var rs = this.regions;
37898         for(var target in rs){
37899             if(typeof rs[target] != "function"){
37900                 var p = rs[target].getPanel(panelId);
37901                 if(p){
37902                     return p;
37903                 }
37904             }
37905         }
37906         return null;
37907     },
37908
37909     /**
37910      * Searches all regions for a panel with the specified id and activates (shows) it.
37911      * @param {String/ContentPanel} panelId The panels id or the panel itself
37912      * @return {Roo.ContentPanel} The shown panel or null
37913      */
37914     showPanel : function(panelId) {
37915       var rs = this.regions;
37916       for(var target in rs){
37917          var r = rs[target];
37918          if(typeof r != "function"){
37919             if(r.hasPanel(panelId)){
37920                return r.showPanel(panelId);
37921             }
37922          }
37923       }
37924       return null;
37925    },
37926
37927    /**
37928      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37929      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37930      */
37931    /*
37932     restoreState : function(provider){
37933         if(!provider){
37934             provider = Roo.state.Manager;
37935         }
37936         var sm = new Roo.LayoutStateManager();
37937         sm.init(this, provider);
37938     },
37939 */
37940  
37941  
37942     /**
37943      * Adds a xtype elements to the layout.
37944      * <pre><code>
37945
37946 layout.addxtype({
37947        xtype : 'ContentPanel',
37948        region: 'west',
37949        items: [ .... ]
37950    }
37951 );
37952
37953 layout.addxtype({
37954         xtype : 'NestedLayoutPanel',
37955         region: 'west',
37956         layout: {
37957            center: { },
37958            west: { }   
37959         },
37960         items : [ ... list of content panels or nested layout panels.. ]
37961    }
37962 );
37963 </code></pre>
37964      * @param {Object} cfg Xtype definition of item to add.
37965      */
37966     addxtype : function(cfg)
37967     {
37968         // basically accepts a pannel...
37969         // can accept a layout region..!?!?
37970         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37971         
37972         
37973         // theory?  children can only be panels??
37974         
37975         //if (!cfg.xtype.match(/Panel$/)) {
37976         //    return false;
37977         //}
37978         var ret = false;
37979         
37980         if (typeof(cfg.region) == 'undefined') {
37981             Roo.log("Failed to add Panel, region was not set");
37982             Roo.log(cfg);
37983             return false;
37984         }
37985         var region = cfg.region;
37986         delete cfg.region;
37987         
37988           
37989         var xitems = [];
37990         if (cfg.items) {
37991             xitems = cfg.items;
37992             delete cfg.items;
37993         }
37994         var nb = false;
37995         
37996         if ( region == 'center') {
37997             Roo.log("Center: " + cfg.title);
37998         }
37999         
38000         
38001         switch(cfg.xtype) 
38002         {
38003             case 'Content':  // ContentPanel (el, cfg)
38004             case 'Scroll':  // ContentPanel (el, cfg)
38005             case 'View': 
38006                 cfg.autoCreate = cfg.autoCreate || true;
38007                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38008                 //} else {
38009                 //    var el = this.el.createChild();
38010                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38011                 //}
38012                 
38013                 this.add(region, ret);
38014                 break;
38015             
38016             /*
38017             case 'TreePanel': // our new panel!
38018                 cfg.el = this.el.createChild();
38019                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38020                 this.add(region, ret);
38021                 break;
38022             */
38023             
38024             case 'Nest': 
38025                 // create a new Layout (which is  a Border Layout...
38026                 
38027                 var clayout = cfg.layout;
38028                 clayout.el  = this.el.createChild();
38029                 clayout.items   = clayout.items  || [];
38030                 
38031                 delete cfg.layout;
38032                 
38033                 // replace this exitems with the clayout ones..
38034                 xitems = clayout.items;
38035                  
38036                 // force background off if it's in center...
38037                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38038                     cfg.background = false;
38039                 }
38040                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38041                 
38042                 
38043                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38044                 //console.log('adding nested layout panel '  + cfg.toSource());
38045                 this.add(region, ret);
38046                 nb = {}; /// find first...
38047                 break;
38048             
38049             case 'Grid':
38050                 
38051                 // needs grid and region
38052                 
38053                 //var el = this.getRegion(region).el.createChild();
38054                 /*
38055                  *var el = this.el.createChild();
38056                 // create the grid first...
38057                 cfg.grid.container = el;
38058                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38059                 */
38060                 
38061                 if (region == 'center' && this.active ) {
38062                     cfg.background = false;
38063                 }
38064                 
38065                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38066                 
38067                 this.add(region, ret);
38068                 /*
38069                 if (cfg.background) {
38070                     // render grid on panel activation (if panel background)
38071                     ret.on('activate', function(gp) {
38072                         if (!gp.grid.rendered) {
38073                     //        gp.grid.render(el);
38074                         }
38075                     });
38076                 } else {
38077                   //  cfg.grid.render(el);
38078                 }
38079                 */
38080                 break;
38081            
38082            
38083             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38084                 // it was the old xcomponent building that caused this before.
38085                 // espeically if border is the top element in the tree.
38086                 ret = this;
38087                 break; 
38088                 
38089                     
38090                 
38091                 
38092                 
38093             default:
38094                 /*
38095                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38096                     
38097                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38098                     this.add(region, ret);
38099                 } else {
38100                 */
38101                     Roo.log(cfg);
38102                     throw "Can not add '" + cfg.xtype + "' to Border";
38103                     return null;
38104              
38105                                 
38106              
38107         }
38108         this.beginUpdate();
38109         // add children..
38110         var region = '';
38111         var abn = {};
38112         Roo.each(xitems, function(i)  {
38113             region = nb && i.region ? i.region : false;
38114             
38115             var add = ret.addxtype(i);
38116            
38117             if (region) {
38118                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38119                 if (!i.background) {
38120                     abn[region] = nb[region] ;
38121                 }
38122             }
38123             
38124         });
38125         this.endUpdate();
38126
38127         // make the last non-background panel active..
38128         //if (nb) { Roo.log(abn); }
38129         if (nb) {
38130             
38131             for(var r in abn) {
38132                 region = this.getRegion(r);
38133                 if (region) {
38134                     // tried using nb[r], but it does not work..
38135                      
38136                     region.showPanel(abn[r]);
38137                    
38138                 }
38139             }
38140         }
38141         return ret;
38142         
38143     },
38144     
38145     
38146 // private
38147     factory : function(cfg)
38148     {
38149         
38150         var validRegions = Roo.bootstrap.layout.Border.regions;
38151
38152         var target = cfg.region;
38153         cfg.mgr = this;
38154         
38155         var r = Roo.bootstrap.layout;
38156         Roo.log(target);
38157         switch(target){
38158             case "north":
38159                 return new r.North(cfg);
38160             case "south":
38161                 return new r.South(cfg);
38162             case "east":
38163                 return new r.East(cfg);
38164             case "west":
38165                 return new r.West(cfg);
38166             case "center":
38167                 return new r.Center(cfg);
38168         }
38169         throw 'Layout region "'+target+'" not supported.';
38170     }
38171     
38172     
38173 });
38174  /*
38175  * Based on:
38176  * Ext JS Library 1.1.1
38177  * Copyright(c) 2006-2007, Ext JS, LLC.
38178  *
38179  * Originally Released Under LGPL - original licence link has changed is not relivant.
38180  *
38181  * Fork - LGPL
38182  * <script type="text/javascript">
38183  */
38184  
38185 /**
38186  * @class Roo.bootstrap.layout.Basic
38187  * @extends Roo.util.Observable
38188  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38189  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38190  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38191  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38192  * @cfg {string}   region  the region that it inhabits..
38193  * @cfg {bool}   skipConfig skip config?
38194  * 
38195
38196  */
38197 Roo.bootstrap.layout.Basic = function(config){
38198     
38199     this.mgr = config.mgr;
38200     
38201     this.position = config.region;
38202     
38203     var skipConfig = config.skipConfig;
38204     
38205     this.events = {
38206         /**
38207          * @scope Roo.BasicLayoutRegion
38208          */
38209         
38210         /**
38211          * @event beforeremove
38212          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38213          * @param {Roo.LayoutRegion} this
38214          * @param {Roo.ContentPanel} panel The panel
38215          * @param {Object} e The cancel event object
38216          */
38217         "beforeremove" : true,
38218         /**
38219          * @event invalidated
38220          * Fires when the layout for this region is changed.
38221          * @param {Roo.LayoutRegion} this
38222          */
38223         "invalidated" : true,
38224         /**
38225          * @event visibilitychange
38226          * Fires when this region is shown or hidden 
38227          * @param {Roo.LayoutRegion} this
38228          * @param {Boolean} visibility true or false
38229          */
38230         "visibilitychange" : true,
38231         /**
38232          * @event paneladded
38233          * Fires when a panel is added. 
38234          * @param {Roo.LayoutRegion} this
38235          * @param {Roo.ContentPanel} panel The panel
38236          */
38237         "paneladded" : true,
38238         /**
38239          * @event panelremoved
38240          * Fires when a panel is removed. 
38241          * @param {Roo.LayoutRegion} this
38242          * @param {Roo.ContentPanel} panel The panel
38243          */
38244         "panelremoved" : true,
38245         /**
38246          * @event beforecollapse
38247          * Fires when this region before collapse.
38248          * @param {Roo.LayoutRegion} this
38249          */
38250         "beforecollapse" : true,
38251         /**
38252          * @event collapsed
38253          * Fires when this region is collapsed.
38254          * @param {Roo.LayoutRegion} this
38255          */
38256         "collapsed" : true,
38257         /**
38258          * @event expanded
38259          * Fires when this region is expanded.
38260          * @param {Roo.LayoutRegion} this
38261          */
38262         "expanded" : true,
38263         /**
38264          * @event slideshow
38265          * Fires when this region is slid into view.
38266          * @param {Roo.LayoutRegion} this
38267          */
38268         "slideshow" : true,
38269         /**
38270          * @event slidehide
38271          * Fires when this region slides out of view. 
38272          * @param {Roo.LayoutRegion} this
38273          */
38274         "slidehide" : true,
38275         /**
38276          * @event panelactivated
38277          * Fires when a panel is activated. 
38278          * @param {Roo.LayoutRegion} this
38279          * @param {Roo.ContentPanel} panel The activated panel
38280          */
38281         "panelactivated" : true,
38282         /**
38283          * @event resized
38284          * Fires when the user resizes this region. 
38285          * @param {Roo.LayoutRegion} this
38286          * @param {Number} newSize The new size (width for east/west, height for north/south)
38287          */
38288         "resized" : true
38289     };
38290     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38291     this.panels = new Roo.util.MixedCollection();
38292     this.panels.getKey = this.getPanelId.createDelegate(this);
38293     this.box = null;
38294     this.activePanel = null;
38295     // ensure listeners are added...
38296     
38297     if (config.listeners || config.events) {
38298         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38299             listeners : config.listeners || {},
38300             events : config.events || {}
38301         });
38302     }
38303     
38304     if(skipConfig !== true){
38305         this.applyConfig(config);
38306     }
38307 };
38308
38309 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38310 {
38311     getPanelId : function(p){
38312         return p.getId();
38313     },
38314     
38315     applyConfig : function(config){
38316         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38317         this.config = config;
38318         
38319     },
38320     
38321     /**
38322      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38323      * the width, for horizontal (north, south) the height.
38324      * @param {Number} newSize The new width or height
38325      */
38326     resizeTo : function(newSize){
38327         var el = this.el ? this.el :
38328                  (this.activePanel ? this.activePanel.getEl() : null);
38329         if(el){
38330             switch(this.position){
38331                 case "east":
38332                 case "west":
38333                     el.setWidth(newSize);
38334                     this.fireEvent("resized", this, newSize);
38335                 break;
38336                 case "north":
38337                 case "south":
38338                     el.setHeight(newSize);
38339                     this.fireEvent("resized", this, newSize);
38340                 break;                
38341             }
38342         }
38343     },
38344     
38345     getBox : function(){
38346         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38347     },
38348     
38349     getMargins : function(){
38350         return this.margins;
38351     },
38352     
38353     updateBox : function(box){
38354         this.box = box;
38355         var el = this.activePanel.getEl();
38356         el.dom.style.left = box.x + "px";
38357         el.dom.style.top = box.y + "px";
38358         this.activePanel.setSize(box.width, box.height);
38359     },
38360     
38361     /**
38362      * Returns the container element for this region.
38363      * @return {Roo.Element}
38364      */
38365     getEl : function(){
38366         return this.activePanel;
38367     },
38368     
38369     /**
38370      * Returns true if this region is currently visible.
38371      * @return {Boolean}
38372      */
38373     isVisible : function(){
38374         return this.activePanel ? true : false;
38375     },
38376     
38377     setActivePanel : function(panel){
38378         panel = this.getPanel(panel);
38379         if(this.activePanel && this.activePanel != panel){
38380             this.activePanel.setActiveState(false);
38381             this.activePanel.getEl().setLeftTop(-10000,-10000);
38382         }
38383         this.activePanel = panel;
38384         panel.setActiveState(true);
38385         if(this.box){
38386             panel.setSize(this.box.width, this.box.height);
38387         }
38388         this.fireEvent("panelactivated", this, panel);
38389         this.fireEvent("invalidated");
38390     },
38391     
38392     /**
38393      * Show the specified panel.
38394      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38395      * @return {Roo.ContentPanel} The shown panel or null
38396      */
38397     showPanel : function(panel){
38398         panel = this.getPanel(panel);
38399         if(panel){
38400             this.setActivePanel(panel);
38401         }
38402         return panel;
38403     },
38404     
38405     /**
38406      * Get the active panel for this region.
38407      * @return {Roo.ContentPanel} The active panel or null
38408      */
38409     getActivePanel : function(){
38410         return this.activePanel;
38411     },
38412     
38413     /**
38414      * Add the passed ContentPanel(s)
38415      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38416      * @return {Roo.ContentPanel} The panel added (if only one was added)
38417      */
38418     add : function(panel){
38419         if(arguments.length > 1){
38420             for(var i = 0, len = arguments.length; i < len; i++) {
38421                 this.add(arguments[i]);
38422             }
38423             return null;
38424         }
38425         if(this.hasPanel(panel)){
38426             this.showPanel(panel);
38427             return panel;
38428         }
38429         var el = panel.getEl();
38430         if(el.dom.parentNode != this.mgr.el.dom){
38431             this.mgr.el.dom.appendChild(el.dom);
38432         }
38433         if(panel.setRegion){
38434             panel.setRegion(this);
38435         }
38436         this.panels.add(panel);
38437         el.setStyle("position", "absolute");
38438         if(!panel.background){
38439             this.setActivePanel(panel);
38440             if(this.config.initialSize && this.panels.getCount()==1){
38441                 this.resizeTo(this.config.initialSize);
38442             }
38443         }
38444         this.fireEvent("paneladded", this, panel);
38445         return panel;
38446     },
38447     
38448     /**
38449      * Returns true if the panel is in this region.
38450      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38451      * @return {Boolean}
38452      */
38453     hasPanel : function(panel){
38454         if(typeof panel == "object"){ // must be panel obj
38455             panel = panel.getId();
38456         }
38457         return this.getPanel(panel) ? true : false;
38458     },
38459     
38460     /**
38461      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38462      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38463      * @param {Boolean} preservePanel Overrides the config preservePanel option
38464      * @return {Roo.ContentPanel} The panel that was removed
38465      */
38466     remove : function(panel, preservePanel){
38467         panel = this.getPanel(panel);
38468         if(!panel){
38469             return null;
38470         }
38471         var e = {};
38472         this.fireEvent("beforeremove", this, panel, e);
38473         if(e.cancel === true){
38474             return null;
38475         }
38476         var panelId = panel.getId();
38477         this.panels.removeKey(panelId);
38478         return panel;
38479     },
38480     
38481     /**
38482      * Returns the panel specified or null if it's not in this region.
38483      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38484      * @return {Roo.ContentPanel}
38485      */
38486     getPanel : function(id){
38487         if(typeof id == "object"){ // must be panel obj
38488             return id;
38489         }
38490         return this.panels.get(id);
38491     },
38492     
38493     /**
38494      * Returns this regions position (north/south/east/west/center).
38495      * @return {String} 
38496      */
38497     getPosition: function(){
38498         return this.position;    
38499     }
38500 });/*
38501  * Based on:
38502  * Ext JS Library 1.1.1
38503  * Copyright(c) 2006-2007, Ext JS, LLC.
38504  *
38505  * Originally Released Under LGPL - original licence link has changed is not relivant.
38506  *
38507  * Fork - LGPL
38508  * <script type="text/javascript">
38509  */
38510  
38511 /**
38512  * @class Roo.bootstrap.layout.Region
38513  * @extends Roo.bootstrap.layout.Basic
38514  * This class represents a region in a layout manager.
38515  
38516  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38517  * @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})
38518  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38519  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38520  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38521  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38522  * @cfg {String}    title           The title for the region (overrides panel titles)
38523  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38524  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38525  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38526  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38527  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38528  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38529  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38530  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38531  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38532  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38533
38534  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38535  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38536  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38537  * @cfg {Number}    width           For East/West panels
38538  * @cfg {Number}    height          For North/South panels
38539  * @cfg {Boolean}   split           To show the splitter
38540  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38541  * 
38542  * @cfg {string}   cls             Extra CSS classes to add to region
38543  * 
38544  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38545  * @cfg {string}   region  the region that it inhabits..
38546  *
38547
38548  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38549  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38550
38551  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38552  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38553  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38554  */
38555 Roo.bootstrap.layout.Region = function(config)
38556 {
38557     this.applyConfig(config);
38558
38559     var mgr = config.mgr;
38560     var pos = config.region;
38561     config.skipConfig = true;
38562     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38563     
38564     if (mgr.el) {
38565         this.onRender(mgr.el);   
38566     }
38567      
38568     this.visible = true;
38569     this.collapsed = false;
38570     this.unrendered_panels = [];
38571 };
38572
38573 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38574
38575     position: '', // set by wrapper (eg. north/south etc..)
38576     unrendered_panels : null,  // unrendered panels.
38577     
38578     tabPosition : false,
38579     
38580     mgr: false, // points to 'Border'
38581     
38582     
38583     createBody : function(){
38584         /** This region's body element 
38585         * @type Roo.Element */
38586         this.bodyEl = this.el.createChild({
38587                 tag: "div",
38588                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38589         });
38590     },
38591
38592     onRender: function(ctr, pos)
38593     {
38594         var dh = Roo.DomHelper;
38595         /** This region's container element 
38596         * @type Roo.Element */
38597         this.el = dh.append(ctr.dom, {
38598                 tag: "div",
38599                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38600             }, true);
38601         /** This region's title element 
38602         * @type Roo.Element */
38603     
38604         this.titleEl = dh.append(this.el.dom,  {
38605                 tag: "div",
38606                 unselectable: "on",
38607                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38608                 children:[
38609                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38610                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38611                 ]
38612             }, true);
38613         
38614         this.titleEl.enableDisplayMode();
38615         /** This region's title text element 
38616         * @type HTMLElement */
38617         this.titleTextEl = this.titleEl.dom.firstChild;
38618         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38619         /*
38620         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38621         this.closeBtn.enableDisplayMode();
38622         this.closeBtn.on("click", this.closeClicked, this);
38623         this.closeBtn.hide();
38624     */
38625         this.createBody(this.config);
38626         if(this.config.hideWhenEmpty){
38627             this.hide();
38628             this.on("paneladded", this.validateVisibility, this);
38629             this.on("panelremoved", this.validateVisibility, this);
38630         }
38631         if(this.autoScroll){
38632             this.bodyEl.setStyle("overflow", "auto");
38633         }else{
38634             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38635         }
38636         //if(c.titlebar !== false){
38637             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38638                 this.titleEl.hide();
38639             }else{
38640                 this.titleEl.show();
38641                 if(this.config.title){
38642                     this.titleTextEl.innerHTML = this.config.title;
38643                 }
38644             }
38645         //}
38646         if(this.config.collapsed){
38647             this.collapse(true);
38648         }
38649         if(this.config.hidden){
38650             this.hide();
38651         }
38652         
38653         if (this.unrendered_panels && this.unrendered_panels.length) {
38654             for (var i =0;i< this.unrendered_panels.length; i++) {
38655                 this.add(this.unrendered_panels[i]);
38656             }
38657             this.unrendered_panels = null;
38658             
38659         }
38660         
38661     },
38662     
38663     applyConfig : function(c)
38664     {
38665         /*
38666          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38667             var dh = Roo.DomHelper;
38668             if(c.titlebar !== false){
38669                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38670                 this.collapseBtn.on("click", this.collapse, this);
38671                 this.collapseBtn.enableDisplayMode();
38672                 /*
38673                 if(c.showPin === true || this.showPin){
38674                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38675                     this.stickBtn.enableDisplayMode();
38676                     this.stickBtn.on("click", this.expand, this);
38677                     this.stickBtn.hide();
38678                 }
38679                 
38680             }
38681             */
38682             /** This region's collapsed element
38683             * @type Roo.Element */
38684             /*
38685              *
38686             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38687                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38688             ]}, true);
38689             
38690             if(c.floatable !== false){
38691                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38692                this.collapsedEl.on("click", this.collapseClick, this);
38693             }
38694
38695             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38696                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38697                    id: "message", unselectable: "on", style:{"float":"left"}});
38698                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38699              }
38700             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38701             this.expandBtn.on("click", this.expand, this);
38702             
38703         }
38704         
38705         if(this.collapseBtn){
38706             this.collapseBtn.setVisible(c.collapsible == true);
38707         }
38708         
38709         this.cmargins = c.cmargins || this.cmargins ||
38710                          (this.position == "west" || this.position == "east" ?
38711                              {top: 0, left: 2, right:2, bottom: 0} :
38712                              {top: 2, left: 0, right:0, bottom: 2});
38713         */
38714         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38715         
38716         
38717         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38718         
38719         this.autoScroll = c.autoScroll || false;
38720         
38721         
38722        
38723         
38724         this.duration = c.duration || .30;
38725         this.slideDuration = c.slideDuration || .45;
38726         this.config = c;
38727        
38728     },
38729     /**
38730      * Returns true if this region is currently visible.
38731      * @return {Boolean}
38732      */
38733     isVisible : function(){
38734         return this.visible;
38735     },
38736
38737     /**
38738      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38739      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38740      */
38741     //setCollapsedTitle : function(title){
38742     //    title = title || "&#160;";
38743      //   if(this.collapsedTitleTextEl){
38744       //      this.collapsedTitleTextEl.innerHTML = title;
38745        // }
38746     //},
38747
38748     getBox : function(){
38749         var b;
38750       //  if(!this.collapsed){
38751             b = this.el.getBox(false, true);
38752        // }else{
38753           //  b = this.collapsedEl.getBox(false, true);
38754         //}
38755         return b;
38756     },
38757
38758     getMargins : function(){
38759         return this.margins;
38760         //return this.collapsed ? this.cmargins : this.margins;
38761     },
38762 /*
38763     highlight : function(){
38764         this.el.addClass("x-layout-panel-dragover");
38765     },
38766
38767     unhighlight : function(){
38768         this.el.removeClass("x-layout-panel-dragover");
38769     },
38770 */
38771     updateBox : function(box)
38772     {
38773         if (!this.bodyEl) {
38774             return; // not rendered yet..
38775         }
38776         
38777         this.box = box;
38778         if(!this.collapsed){
38779             this.el.dom.style.left = box.x + "px";
38780             this.el.dom.style.top = box.y + "px";
38781             this.updateBody(box.width, box.height);
38782         }else{
38783             this.collapsedEl.dom.style.left = box.x + "px";
38784             this.collapsedEl.dom.style.top = box.y + "px";
38785             this.collapsedEl.setSize(box.width, box.height);
38786         }
38787         if(this.tabs){
38788             this.tabs.autoSizeTabs();
38789         }
38790     },
38791
38792     updateBody : function(w, h)
38793     {
38794         if(w !== null){
38795             this.el.setWidth(w);
38796             w -= this.el.getBorderWidth("rl");
38797             if(this.config.adjustments){
38798                 w += this.config.adjustments[0];
38799             }
38800         }
38801         if(h !== null && h > 0){
38802             this.el.setHeight(h);
38803             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38804             h -= this.el.getBorderWidth("tb");
38805             if(this.config.adjustments){
38806                 h += this.config.adjustments[1];
38807             }
38808             this.bodyEl.setHeight(h);
38809             if(this.tabs){
38810                 h = this.tabs.syncHeight(h);
38811             }
38812         }
38813         if(this.panelSize){
38814             w = w !== null ? w : this.panelSize.width;
38815             h = h !== null ? h : this.panelSize.height;
38816         }
38817         if(this.activePanel){
38818             var el = this.activePanel.getEl();
38819             w = w !== null ? w : el.getWidth();
38820             h = h !== null ? h : el.getHeight();
38821             this.panelSize = {width: w, height: h};
38822             this.activePanel.setSize(w, h);
38823         }
38824         if(Roo.isIE && this.tabs){
38825             this.tabs.el.repaint();
38826         }
38827     },
38828
38829     /**
38830      * Returns the container element for this region.
38831      * @return {Roo.Element}
38832      */
38833     getEl : function(){
38834         return this.el;
38835     },
38836
38837     /**
38838      * Hides this region.
38839      */
38840     hide : function(){
38841         //if(!this.collapsed){
38842             this.el.dom.style.left = "-2000px";
38843             this.el.hide();
38844         //}else{
38845          //   this.collapsedEl.dom.style.left = "-2000px";
38846          //   this.collapsedEl.hide();
38847        // }
38848         this.visible = false;
38849         this.fireEvent("visibilitychange", this, false);
38850     },
38851
38852     /**
38853      * Shows this region if it was previously hidden.
38854      */
38855     show : function(){
38856         //if(!this.collapsed){
38857             this.el.show();
38858         //}else{
38859         //    this.collapsedEl.show();
38860        // }
38861         this.visible = true;
38862         this.fireEvent("visibilitychange", this, true);
38863     },
38864 /*
38865     closeClicked : function(){
38866         if(this.activePanel){
38867             this.remove(this.activePanel);
38868         }
38869     },
38870
38871     collapseClick : function(e){
38872         if(this.isSlid){
38873            e.stopPropagation();
38874            this.slideIn();
38875         }else{
38876            e.stopPropagation();
38877            this.slideOut();
38878         }
38879     },
38880 */
38881     /**
38882      * Collapses this region.
38883      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38884      */
38885     /*
38886     collapse : function(skipAnim, skipCheck = false){
38887         if(this.collapsed) {
38888             return;
38889         }
38890         
38891         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38892             
38893             this.collapsed = true;
38894             if(this.split){
38895                 this.split.el.hide();
38896             }
38897             if(this.config.animate && skipAnim !== true){
38898                 this.fireEvent("invalidated", this);
38899                 this.animateCollapse();
38900             }else{
38901                 this.el.setLocation(-20000,-20000);
38902                 this.el.hide();
38903                 this.collapsedEl.show();
38904                 this.fireEvent("collapsed", this);
38905                 this.fireEvent("invalidated", this);
38906             }
38907         }
38908         
38909     },
38910 */
38911     animateCollapse : function(){
38912         // overridden
38913     },
38914
38915     /**
38916      * Expands this region if it was previously collapsed.
38917      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38918      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38919      */
38920     /*
38921     expand : function(e, skipAnim){
38922         if(e) {
38923             e.stopPropagation();
38924         }
38925         if(!this.collapsed || this.el.hasActiveFx()) {
38926             return;
38927         }
38928         if(this.isSlid){
38929             this.afterSlideIn();
38930             skipAnim = true;
38931         }
38932         this.collapsed = false;
38933         if(this.config.animate && skipAnim !== true){
38934             this.animateExpand();
38935         }else{
38936             this.el.show();
38937             if(this.split){
38938                 this.split.el.show();
38939             }
38940             this.collapsedEl.setLocation(-2000,-2000);
38941             this.collapsedEl.hide();
38942             this.fireEvent("invalidated", this);
38943             this.fireEvent("expanded", this);
38944         }
38945     },
38946 */
38947     animateExpand : function(){
38948         // overridden
38949     },
38950
38951     initTabs : function()
38952     {
38953         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38954         
38955         var ts = new Roo.bootstrap.panel.Tabs({
38956             el: this.bodyEl.dom,
38957             region : this,
38958             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38959             disableTooltips: this.config.disableTabTips,
38960             toolbar : this.config.toolbar
38961         });
38962         
38963         if(this.config.hideTabs){
38964             ts.stripWrap.setDisplayed(false);
38965         }
38966         this.tabs = ts;
38967         ts.resizeTabs = this.config.resizeTabs === true;
38968         ts.minTabWidth = this.config.minTabWidth || 40;
38969         ts.maxTabWidth = this.config.maxTabWidth || 250;
38970         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38971         ts.monitorResize = false;
38972         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38973         ts.bodyEl.addClass('roo-layout-tabs-body');
38974         this.panels.each(this.initPanelAsTab, this);
38975     },
38976
38977     initPanelAsTab : function(panel){
38978         var ti = this.tabs.addTab(
38979             panel.getEl().id,
38980             panel.getTitle(),
38981             null,
38982             this.config.closeOnTab && panel.isClosable(),
38983             panel.tpl
38984         );
38985         if(panel.tabTip !== undefined){
38986             ti.setTooltip(panel.tabTip);
38987         }
38988         ti.on("activate", function(){
38989               this.setActivePanel(panel);
38990         }, this);
38991         
38992         if(this.config.closeOnTab){
38993             ti.on("beforeclose", function(t, e){
38994                 e.cancel = true;
38995                 this.remove(panel);
38996             }, this);
38997         }
38998         
38999         panel.tabItem = ti;
39000         
39001         return ti;
39002     },
39003
39004     updatePanelTitle : function(panel, title)
39005     {
39006         if(this.activePanel == panel){
39007             this.updateTitle(title);
39008         }
39009         if(this.tabs){
39010             var ti = this.tabs.getTab(panel.getEl().id);
39011             ti.setText(title);
39012             if(panel.tabTip !== undefined){
39013                 ti.setTooltip(panel.tabTip);
39014             }
39015         }
39016     },
39017
39018     updateTitle : function(title){
39019         if(this.titleTextEl && !this.config.title){
39020             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39021         }
39022     },
39023
39024     setActivePanel : function(panel)
39025     {
39026         panel = this.getPanel(panel);
39027         if(this.activePanel && this.activePanel != panel){
39028             if(this.activePanel.setActiveState(false) === false){
39029                 return;
39030             }
39031         }
39032         this.activePanel = panel;
39033         panel.setActiveState(true);
39034         if(this.panelSize){
39035             panel.setSize(this.panelSize.width, this.panelSize.height);
39036         }
39037         if(this.closeBtn){
39038             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39039         }
39040         this.updateTitle(panel.getTitle());
39041         if(this.tabs){
39042             this.fireEvent("invalidated", this);
39043         }
39044         this.fireEvent("panelactivated", this, panel);
39045     },
39046
39047     /**
39048      * Shows the specified panel.
39049      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39050      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39051      */
39052     showPanel : function(panel)
39053     {
39054         panel = this.getPanel(panel);
39055         if(panel){
39056             if(this.tabs){
39057                 var tab = this.tabs.getTab(panel.getEl().id);
39058                 if(tab.isHidden()){
39059                     this.tabs.unhideTab(tab.id);
39060                 }
39061                 tab.activate();
39062             }else{
39063                 this.setActivePanel(panel);
39064             }
39065         }
39066         return panel;
39067     },
39068
39069     /**
39070      * Get the active panel for this region.
39071      * @return {Roo.ContentPanel} The active panel or null
39072      */
39073     getActivePanel : function(){
39074         return this.activePanel;
39075     },
39076
39077     validateVisibility : function(){
39078         if(this.panels.getCount() < 1){
39079             this.updateTitle("&#160;");
39080             this.closeBtn.hide();
39081             this.hide();
39082         }else{
39083             if(!this.isVisible()){
39084                 this.show();
39085             }
39086         }
39087     },
39088
39089     /**
39090      * Adds the passed ContentPanel(s) to this region.
39091      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39092      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39093      */
39094     add : function(panel)
39095     {
39096         if(arguments.length > 1){
39097             for(var i = 0, len = arguments.length; i < len; i++) {
39098                 this.add(arguments[i]);
39099             }
39100             return null;
39101         }
39102         
39103         // if we have not been rendered yet, then we can not really do much of this..
39104         if (!this.bodyEl) {
39105             this.unrendered_panels.push(panel);
39106             return panel;
39107         }
39108         
39109         
39110         
39111         
39112         if(this.hasPanel(panel)){
39113             this.showPanel(panel);
39114             return panel;
39115         }
39116         panel.setRegion(this);
39117         this.panels.add(panel);
39118        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39119             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39120             // and hide them... ???
39121             this.bodyEl.dom.appendChild(panel.getEl().dom);
39122             if(panel.background !== true){
39123                 this.setActivePanel(panel);
39124             }
39125             this.fireEvent("paneladded", this, panel);
39126             return panel;
39127         }
39128         */
39129         if(!this.tabs){
39130             this.initTabs();
39131         }else{
39132             this.initPanelAsTab(panel);
39133         }
39134         
39135         
39136         if(panel.background !== true){
39137             this.tabs.activate(panel.getEl().id);
39138         }
39139         this.fireEvent("paneladded", this, panel);
39140         return panel;
39141     },
39142
39143     /**
39144      * Hides the tab for the specified panel.
39145      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39146      */
39147     hidePanel : function(panel){
39148         if(this.tabs && (panel = this.getPanel(panel))){
39149             this.tabs.hideTab(panel.getEl().id);
39150         }
39151     },
39152
39153     /**
39154      * Unhides the tab for a previously hidden panel.
39155      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39156      */
39157     unhidePanel : function(panel){
39158         if(this.tabs && (panel = this.getPanel(panel))){
39159             this.tabs.unhideTab(panel.getEl().id);
39160         }
39161     },
39162
39163     clearPanels : function(){
39164         while(this.panels.getCount() > 0){
39165              this.remove(this.panels.first());
39166         }
39167     },
39168
39169     /**
39170      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39171      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39172      * @param {Boolean} preservePanel Overrides the config preservePanel option
39173      * @return {Roo.ContentPanel} The panel that was removed
39174      */
39175     remove : function(panel, preservePanel)
39176     {
39177         panel = this.getPanel(panel);
39178         if(!panel){
39179             return null;
39180         }
39181         var e = {};
39182         this.fireEvent("beforeremove", this, panel, e);
39183         if(e.cancel === true){
39184             return null;
39185         }
39186         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39187         var panelId = panel.getId();
39188         this.panels.removeKey(panelId);
39189         if(preservePanel){
39190             document.body.appendChild(panel.getEl().dom);
39191         }
39192         if(this.tabs){
39193             this.tabs.removeTab(panel.getEl().id);
39194         }else if (!preservePanel){
39195             this.bodyEl.dom.removeChild(panel.getEl().dom);
39196         }
39197         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39198             var p = this.panels.first();
39199             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39200             tempEl.appendChild(p.getEl().dom);
39201             this.bodyEl.update("");
39202             this.bodyEl.dom.appendChild(p.getEl().dom);
39203             tempEl = null;
39204             this.updateTitle(p.getTitle());
39205             this.tabs = null;
39206             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39207             this.setActivePanel(p);
39208         }
39209         panel.setRegion(null);
39210         if(this.activePanel == panel){
39211             this.activePanel = null;
39212         }
39213         if(this.config.autoDestroy !== false && preservePanel !== true){
39214             try{panel.destroy();}catch(e){}
39215         }
39216         this.fireEvent("panelremoved", this, panel);
39217         return panel;
39218     },
39219
39220     /**
39221      * Returns the TabPanel component used by this region
39222      * @return {Roo.TabPanel}
39223      */
39224     getTabs : function(){
39225         return this.tabs;
39226     },
39227
39228     createTool : function(parentEl, className){
39229         var btn = Roo.DomHelper.append(parentEl, {
39230             tag: "div",
39231             cls: "x-layout-tools-button",
39232             children: [ {
39233                 tag: "div",
39234                 cls: "roo-layout-tools-button-inner " + className,
39235                 html: "&#160;"
39236             }]
39237         }, true);
39238         btn.addClassOnOver("roo-layout-tools-button-over");
39239         return btn;
39240     }
39241 });/*
39242  * Based on:
39243  * Ext JS Library 1.1.1
39244  * Copyright(c) 2006-2007, Ext JS, LLC.
39245  *
39246  * Originally Released Under LGPL - original licence link has changed is not relivant.
39247  *
39248  * Fork - LGPL
39249  * <script type="text/javascript">
39250  */
39251  
39252
39253
39254 /**
39255  * @class Roo.SplitLayoutRegion
39256  * @extends Roo.LayoutRegion
39257  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39258  */
39259 Roo.bootstrap.layout.Split = function(config){
39260     this.cursor = config.cursor;
39261     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39262 };
39263
39264 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39265 {
39266     splitTip : "Drag to resize.",
39267     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39268     useSplitTips : false,
39269
39270     applyConfig : function(config){
39271         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39272     },
39273     
39274     onRender : function(ctr,pos) {
39275         
39276         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39277         if(!this.config.split){
39278             return;
39279         }
39280         if(!this.split){
39281             
39282             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39283                             tag: "div",
39284                             id: this.el.id + "-split",
39285                             cls: "roo-layout-split roo-layout-split-"+this.position,
39286                             html: "&#160;"
39287             });
39288             /** The SplitBar for this region 
39289             * @type Roo.SplitBar */
39290             // does not exist yet...
39291             Roo.log([this.position, this.orientation]);
39292             
39293             this.split = new Roo.bootstrap.SplitBar({
39294                 dragElement : splitEl,
39295                 resizingElement: this.el,
39296                 orientation : this.orientation
39297             });
39298             
39299             this.split.on("moved", this.onSplitMove, this);
39300             this.split.useShim = this.config.useShim === true;
39301             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39302             if(this.useSplitTips){
39303                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39304             }
39305             //if(config.collapsible){
39306             //    this.split.el.on("dblclick", this.collapse,  this);
39307             //}
39308         }
39309         if(typeof this.config.minSize != "undefined"){
39310             this.split.minSize = this.config.minSize;
39311         }
39312         if(typeof this.config.maxSize != "undefined"){
39313             this.split.maxSize = this.config.maxSize;
39314         }
39315         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39316             this.hideSplitter();
39317         }
39318         
39319     },
39320
39321     getHMaxSize : function(){
39322          var cmax = this.config.maxSize || 10000;
39323          var center = this.mgr.getRegion("center");
39324          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39325     },
39326
39327     getVMaxSize : function(){
39328          var cmax = this.config.maxSize || 10000;
39329          var center = this.mgr.getRegion("center");
39330          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39331     },
39332
39333     onSplitMove : function(split, newSize){
39334         this.fireEvent("resized", this, newSize);
39335     },
39336     
39337     /** 
39338      * Returns the {@link Roo.SplitBar} for this region.
39339      * @return {Roo.SplitBar}
39340      */
39341     getSplitBar : function(){
39342         return this.split;
39343     },
39344     
39345     hide : function(){
39346         this.hideSplitter();
39347         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39348     },
39349
39350     hideSplitter : function(){
39351         if(this.split){
39352             this.split.el.setLocation(-2000,-2000);
39353             this.split.el.hide();
39354         }
39355     },
39356
39357     show : function(){
39358         if(this.split){
39359             this.split.el.show();
39360         }
39361         Roo.bootstrap.layout.Split.superclass.show.call(this);
39362     },
39363     
39364     beforeSlide: function(){
39365         if(Roo.isGecko){// firefox overflow auto bug workaround
39366             this.bodyEl.clip();
39367             if(this.tabs) {
39368                 this.tabs.bodyEl.clip();
39369             }
39370             if(this.activePanel){
39371                 this.activePanel.getEl().clip();
39372                 
39373                 if(this.activePanel.beforeSlide){
39374                     this.activePanel.beforeSlide();
39375                 }
39376             }
39377         }
39378     },
39379     
39380     afterSlide : function(){
39381         if(Roo.isGecko){// firefox overflow auto bug workaround
39382             this.bodyEl.unclip();
39383             if(this.tabs) {
39384                 this.tabs.bodyEl.unclip();
39385             }
39386             if(this.activePanel){
39387                 this.activePanel.getEl().unclip();
39388                 if(this.activePanel.afterSlide){
39389                     this.activePanel.afterSlide();
39390                 }
39391             }
39392         }
39393     },
39394
39395     initAutoHide : function(){
39396         if(this.autoHide !== false){
39397             if(!this.autoHideHd){
39398                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39399                 this.autoHideHd = {
39400                     "mouseout": function(e){
39401                         if(!e.within(this.el, true)){
39402                             st.delay(500);
39403                         }
39404                     },
39405                     "mouseover" : function(e){
39406                         st.cancel();
39407                     },
39408                     scope : this
39409                 };
39410             }
39411             this.el.on(this.autoHideHd);
39412         }
39413     },
39414
39415     clearAutoHide : function(){
39416         if(this.autoHide !== false){
39417             this.el.un("mouseout", this.autoHideHd.mouseout);
39418             this.el.un("mouseover", this.autoHideHd.mouseover);
39419         }
39420     },
39421
39422     clearMonitor : function(){
39423         Roo.get(document).un("click", this.slideInIf, this);
39424     },
39425
39426     // these names are backwards but not changed for compat
39427     slideOut : function(){
39428         if(this.isSlid || this.el.hasActiveFx()){
39429             return;
39430         }
39431         this.isSlid = true;
39432         if(this.collapseBtn){
39433             this.collapseBtn.hide();
39434         }
39435         this.closeBtnState = this.closeBtn.getStyle('display');
39436         this.closeBtn.hide();
39437         if(this.stickBtn){
39438             this.stickBtn.show();
39439         }
39440         this.el.show();
39441         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39442         this.beforeSlide();
39443         this.el.setStyle("z-index", 10001);
39444         this.el.slideIn(this.getSlideAnchor(), {
39445             callback: function(){
39446                 this.afterSlide();
39447                 this.initAutoHide();
39448                 Roo.get(document).on("click", this.slideInIf, this);
39449                 this.fireEvent("slideshow", this);
39450             },
39451             scope: this,
39452             block: true
39453         });
39454     },
39455
39456     afterSlideIn : function(){
39457         this.clearAutoHide();
39458         this.isSlid = false;
39459         this.clearMonitor();
39460         this.el.setStyle("z-index", "");
39461         if(this.collapseBtn){
39462             this.collapseBtn.show();
39463         }
39464         this.closeBtn.setStyle('display', this.closeBtnState);
39465         if(this.stickBtn){
39466             this.stickBtn.hide();
39467         }
39468         this.fireEvent("slidehide", this);
39469     },
39470
39471     slideIn : function(cb){
39472         if(!this.isSlid || this.el.hasActiveFx()){
39473             Roo.callback(cb);
39474             return;
39475         }
39476         this.isSlid = false;
39477         this.beforeSlide();
39478         this.el.slideOut(this.getSlideAnchor(), {
39479             callback: function(){
39480                 this.el.setLeftTop(-10000, -10000);
39481                 this.afterSlide();
39482                 this.afterSlideIn();
39483                 Roo.callback(cb);
39484             },
39485             scope: this,
39486             block: true
39487         });
39488     },
39489     
39490     slideInIf : function(e){
39491         if(!e.within(this.el)){
39492             this.slideIn();
39493         }
39494     },
39495
39496     animateCollapse : function(){
39497         this.beforeSlide();
39498         this.el.setStyle("z-index", 20000);
39499         var anchor = this.getSlideAnchor();
39500         this.el.slideOut(anchor, {
39501             callback : function(){
39502                 this.el.setStyle("z-index", "");
39503                 this.collapsedEl.slideIn(anchor, {duration:.3});
39504                 this.afterSlide();
39505                 this.el.setLocation(-10000,-10000);
39506                 this.el.hide();
39507                 this.fireEvent("collapsed", this);
39508             },
39509             scope: this,
39510             block: true
39511         });
39512     },
39513
39514     animateExpand : function(){
39515         this.beforeSlide();
39516         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39517         this.el.setStyle("z-index", 20000);
39518         this.collapsedEl.hide({
39519             duration:.1
39520         });
39521         this.el.slideIn(this.getSlideAnchor(), {
39522             callback : function(){
39523                 this.el.setStyle("z-index", "");
39524                 this.afterSlide();
39525                 if(this.split){
39526                     this.split.el.show();
39527                 }
39528                 this.fireEvent("invalidated", this);
39529                 this.fireEvent("expanded", this);
39530             },
39531             scope: this,
39532             block: true
39533         });
39534     },
39535
39536     anchors : {
39537         "west" : "left",
39538         "east" : "right",
39539         "north" : "top",
39540         "south" : "bottom"
39541     },
39542
39543     sanchors : {
39544         "west" : "l",
39545         "east" : "r",
39546         "north" : "t",
39547         "south" : "b"
39548     },
39549
39550     canchors : {
39551         "west" : "tl-tr",
39552         "east" : "tr-tl",
39553         "north" : "tl-bl",
39554         "south" : "bl-tl"
39555     },
39556
39557     getAnchor : function(){
39558         return this.anchors[this.position];
39559     },
39560
39561     getCollapseAnchor : function(){
39562         return this.canchors[this.position];
39563     },
39564
39565     getSlideAnchor : function(){
39566         return this.sanchors[this.position];
39567     },
39568
39569     getAlignAdj : function(){
39570         var cm = this.cmargins;
39571         switch(this.position){
39572             case "west":
39573                 return [0, 0];
39574             break;
39575             case "east":
39576                 return [0, 0];
39577             break;
39578             case "north":
39579                 return [0, 0];
39580             break;
39581             case "south":
39582                 return [0, 0];
39583             break;
39584         }
39585     },
39586
39587     getExpandAdj : function(){
39588         var c = this.collapsedEl, cm = this.cmargins;
39589         switch(this.position){
39590             case "west":
39591                 return [-(cm.right+c.getWidth()+cm.left), 0];
39592             break;
39593             case "east":
39594                 return [cm.right+c.getWidth()+cm.left, 0];
39595             break;
39596             case "north":
39597                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39598             break;
39599             case "south":
39600                 return [0, cm.top+cm.bottom+c.getHeight()];
39601             break;
39602         }
39603     }
39604 });/*
39605  * Based on:
39606  * Ext JS Library 1.1.1
39607  * Copyright(c) 2006-2007, Ext JS, LLC.
39608  *
39609  * Originally Released Under LGPL - original licence link has changed is not relivant.
39610  *
39611  * Fork - LGPL
39612  * <script type="text/javascript">
39613  */
39614 /*
39615  * These classes are private internal classes
39616  */
39617 Roo.bootstrap.layout.Center = function(config){
39618     config.region = "center";
39619     Roo.bootstrap.layout.Region.call(this, config);
39620     this.visible = true;
39621     this.minWidth = config.minWidth || 20;
39622     this.minHeight = config.minHeight || 20;
39623 };
39624
39625 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39626     hide : function(){
39627         // center panel can't be hidden
39628     },
39629     
39630     show : function(){
39631         // center panel can't be hidden
39632     },
39633     
39634     getMinWidth: function(){
39635         return this.minWidth;
39636     },
39637     
39638     getMinHeight: function(){
39639         return this.minHeight;
39640     }
39641 });
39642
39643
39644
39645
39646  
39647
39648
39649
39650
39651
39652
39653 Roo.bootstrap.layout.North = function(config)
39654 {
39655     config.region = 'north';
39656     config.cursor = 'n-resize';
39657     
39658     Roo.bootstrap.layout.Split.call(this, config);
39659     
39660     
39661     if(this.split){
39662         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39663         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39664         this.split.el.addClass("roo-layout-split-v");
39665     }
39666     //var size = config.initialSize || config.height;
39667     //if(this.el && typeof size != "undefined"){
39668     //    this.el.setHeight(size);
39669     //}
39670 };
39671 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39672 {
39673     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39674      
39675      
39676     onRender : function(ctr, pos)
39677     {
39678         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39679         var size = this.config.initialSize || this.config.height;
39680         if(this.el && typeof size != "undefined"){
39681             this.el.setHeight(size);
39682         }
39683     
39684     },
39685     
39686     getBox : function(){
39687         if(this.collapsed){
39688             return this.collapsedEl.getBox();
39689         }
39690         var box = this.el.getBox();
39691         if(this.split){
39692             box.height += this.split.el.getHeight();
39693         }
39694         return box;
39695     },
39696     
39697     updateBox : function(box){
39698         if(this.split && !this.collapsed){
39699             box.height -= this.split.el.getHeight();
39700             this.split.el.setLeft(box.x);
39701             this.split.el.setTop(box.y+box.height);
39702             this.split.el.setWidth(box.width);
39703         }
39704         if(this.collapsed){
39705             this.updateBody(box.width, null);
39706         }
39707         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39708     }
39709 });
39710
39711
39712
39713
39714
39715 Roo.bootstrap.layout.South = function(config){
39716     config.region = 'south';
39717     config.cursor = 's-resize';
39718     Roo.bootstrap.layout.Split.call(this, config);
39719     if(this.split){
39720         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39721         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39722         this.split.el.addClass("roo-layout-split-v");
39723     }
39724     
39725 };
39726
39727 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39728     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39729     
39730     onRender : function(ctr, pos)
39731     {
39732         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39733         var size = this.config.initialSize || this.config.height;
39734         if(this.el && typeof size != "undefined"){
39735             this.el.setHeight(size);
39736         }
39737     
39738     },
39739     
39740     getBox : function(){
39741         if(this.collapsed){
39742             return this.collapsedEl.getBox();
39743         }
39744         var box = this.el.getBox();
39745         if(this.split){
39746             var sh = this.split.el.getHeight();
39747             box.height += sh;
39748             box.y -= sh;
39749         }
39750         return box;
39751     },
39752     
39753     updateBox : function(box){
39754         if(this.split && !this.collapsed){
39755             var sh = this.split.el.getHeight();
39756             box.height -= sh;
39757             box.y += sh;
39758             this.split.el.setLeft(box.x);
39759             this.split.el.setTop(box.y-sh);
39760             this.split.el.setWidth(box.width);
39761         }
39762         if(this.collapsed){
39763             this.updateBody(box.width, null);
39764         }
39765         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39766     }
39767 });
39768
39769 Roo.bootstrap.layout.East = function(config){
39770     config.region = "east";
39771     config.cursor = "e-resize";
39772     Roo.bootstrap.layout.Split.call(this, config);
39773     if(this.split){
39774         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39775         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39776         this.split.el.addClass("roo-layout-split-h");
39777     }
39778     
39779 };
39780 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39781     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39782     
39783     onRender : function(ctr, pos)
39784     {
39785         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39786         var size = this.config.initialSize || this.config.width;
39787         if(this.el && typeof size != "undefined"){
39788             this.el.setWidth(size);
39789         }
39790     
39791     },
39792     
39793     getBox : function(){
39794         if(this.collapsed){
39795             return this.collapsedEl.getBox();
39796         }
39797         var box = this.el.getBox();
39798         if(this.split){
39799             var sw = this.split.el.getWidth();
39800             box.width += sw;
39801             box.x -= sw;
39802         }
39803         return box;
39804     },
39805
39806     updateBox : function(box){
39807         if(this.split && !this.collapsed){
39808             var sw = this.split.el.getWidth();
39809             box.width -= sw;
39810             this.split.el.setLeft(box.x);
39811             this.split.el.setTop(box.y);
39812             this.split.el.setHeight(box.height);
39813             box.x += sw;
39814         }
39815         if(this.collapsed){
39816             this.updateBody(null, box.height);
39817         }
39818         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39819     }
39820 });
39821
39822 Roo.bootstrap.layout.West = function(config){
39823     config.region = "west";
39824     config.cursor = "w-resize";
39825     
39826     Roo.bootstrap.layout.Split.call(this, config);
39827     if(this.split){
39828         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39829         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39830         this.split.el.addClass("roo-layout-split-h");
39831     }
39832     
39833 };
39834 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39835     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39836     
39837     onRender: function(ctr, pos)
39838     {
39839         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39840         var size = this.config.initialSize || this.config.width;
39841         if(typeof size != "undefined"){
39842             this.el.setWidth(size);
39843         }
39844     },
39845     
39846     getBox : function(){
39847         if(this.collapsed){
39848             return this.collapsedEl.getBox();
39849         }
39850         var box = this.el.getBox();
39851         if (box.width == 0) {
39852             box.width = this.config.width; // kludge?
39853         }
39854         if(this.split){
39855             box.width += this.split.el.getWidth();
39856         }
39857         return box;
39858     },
39859     
39860     updateBox : function(box){
39861         if(this.split && !this.collapsed){
39862             var sw = this.split.el.getWidth();
39863             box.width -= sw;
39864             this.split.el.setLeft(box.x+box.width);
39865             this.split.el.setTop(box.y);
39866             this.split.el.setHeight(box.height);
39867         }
39868         if(this.collapsed){
39869             this.updateBody(null, box.height);
39870         }
39871         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39872     }
39873 });Roo.namespace("Roo.bootstrap.panel");/*
39874  * Based on:
39875  * Ext JS Library 1.1.1
39876  * Copyright(c) 2006-2007, Ext JS, LLC.
39877  *
39878  * Originally Released Under LGPL - original licence link has changed is not relivant.
39879  *
39880  * Fork - LGPL
39881  * <script type="text/javascript">
39882  */
39883 /**
39884  * @class Roo.ContentPanel
39885  * @extends Roo.util.Observable
39886  * A basic ContentPanel element.
39887  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39888  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39889  * @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
39890  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39891  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39892  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39893  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39894  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39895  * @cfg {String} title          The title for this panel
39896  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39897  * @cfg {String} url            Calls {@link #setUrl} with this value
39898  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39899  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39900  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39901  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39902  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39903  * @cfg {Boolean} badges render the badges
39904  * @cfg {String} cls  extra classes to use  
39905  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39906
39907  * @constructor
39908  * Create a new ContentPanel.
39909  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39910  * @param {String/Object} config A string to set only the title or a config object
39911  * @param {String} content (optional) Set the HTML content for this panel
39912  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39913  */
39914 Roo.bootstrap.panel.Content = function( config){
39915     
39916     this.tpl = config.tpl || false;
39917     
39918     var el = config.el;
39919     var content = config.content;
39920
39921     if(config.autoCreate){ // xtype is available if this is called from factory
39922         el = Roo.id();
39923     }
39924     this.el = Roo.get(el);
39925     if(!this.el && config && config.autoCreate){
39926         if(typeof config.autoCreate == "object"){
39927             if(!config.autoCreate.id){
39928                 config.autoCreate.id = config.id||el;
39929             }
39930             this.el = Roo.DomHelper.append(document.body,
39931                         config.autoCreate, true);
39932         }else{
39933             var elcfg =  {
39934                 tag: "div",
39935                 cls: (config.cls || '') +
39936                     (config.background ? ' bg-' + config.background : '') +
39937                     " roo-layout-inactive-content",
39938                 id: config.id||el
39939             };
39940             if (config.iframe) {
39941                 elcfg.cn = [
39942                     {
39943                         tag : 'iframe',
39944                         style : 'border: 0px',
39945                         src : 'about:blank'
39946                     }
39947                 ];
39948             }
39949               
39950             if (config.html) {
39951                 elcfg.html = config.html;
39952                 
39953             }
39954                         
39955             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39956             if (config.iframe) {
39957                 this.iframeEl = this.el.select('iframe',true).first();
39958             }
39959             
39960         }
39961     } 
39962     this.closable = false;
39963     this.loaded = false;
39964     this.active = false;
39965    
39966       
39967     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39968         
39969         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39970         
39971         this.wrapEl = this.el; //this.el.wrap();
39972         var ti = [];
39973         if (config.toolbar.items) {
39974             ti = config.toolbar.items ;
39975             delete config.toolbar.items ;
39976         }
39977         
39978         var nitems = [];
39979         this.toolbar.render(this.wrapEl, 'before');
39980         for(var i =0;i < ti.length;i++) {
39981           //  Roo.log(['add child', items[i]]);
39982             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39983         }
39984         this.toolbar.items = nitems;
39985         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39986         delete config.toolbar;
39987         
39988     }
39989     /*
39990     // xtype created footer. - not sure if will work as we normally have to render first..
39991     if (this.footer && !this.footer.el && this.footer.xtype) {
39992         if (!this.wrapEl) {
39993             this.wrapEl = this.el.wrap();
39994         }
39995     
39996         this.footer.container = this.wrapEl.createChild();
39997          
39998         this.footer = Roo.factory(this.footer, Roo);
39999         
40000     }
40001     */
40002     
40003      if(typeof config == "string"){
40004         this.title = config;
40005     }else{
40006         Roo.apply(this, config);
40007     }
40008     
40009     if(this.resizeEl){
40010         this.resizeEl = Roo.get(this.resizeEl, true);
40011     }else{
40012         this.resizeEl = this.el;
40013     }
40014     // handle view.xtype
40015     
40016  
40017     
40018     
40019     this.addEvents({
40020         /**
40021          * @event activate
40022          * Fires when this panel is activated. 
40023          * @param {Roo.ContentPanel} this
40024          */
40025         "activate" : true,
40026         /**
40027          * @event deactivate
40028          * Fires when this panel is activated. 
40029          * @param {Roo.ContentPanel} this
40030          */
40031         "deactivate" : true,
40032
40033         /**
40034          * @event resize
40035          * Fires when this panel is resized if fitToFrame is true.
40036          * @param {Roo.ContentPanel} this
40037          * @param {Number} width The width after any component adjustments
40038          * @param {Number} height The height after any component adjustments
40039          */
40040         "resize" : true,
40041         
40042          /**
40043          * @event render
40044          * Fires when this tab is created
40045          * @param {Roo.ContentPanel} this
40046          */
40047         "render" : true
40048         
40049         
40050         
40051     });
40052     
40053
40054     
40055     
40056     if(this.autoScroll && !this.iframe){
40057         this.resizeEl.setStyle("overflow", "auto");
40058     } else {
40059         // fix randome scrolling
40060         //this.el.on('scroll', function() {
40061         //    Roo.log('fix random scolling');
40062         //    this.scrollTo('top',0); 
40063         //});
40064     }
40065     content = content || this.content;
40066     if(content){
40067         this.setContent(content);
40068     }
40069     if(config && config.url){
40070         this.setUrl(this.url, this.params, this.loadOnce);
40071     }
40072     
40073     
40074     
40075     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40076     
40077     if (this.view && typeof(this.view.xtype) != 'undefined') {
40078         this.view.el = this.el.appendChild(document.createElement("div"));
40079         this.view = Roo.factory(this.view); 
40080         this.view.render  &&  this.view.render(false, '');  
40081     }
40082     
40083     
40084     this.fireEvent('render', this);
40085 };
40086
40087 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40088     
40089     cls : '',
40090     background : '',
40091     
40092     tabTip : '',
40093     
40094     iframe : false,
40095     iframeEl : false,
40096     
40097     setRegion : function(region){
40098         this.region = region;
40099         this.setActiveClass(region && !this.background);
40100     },
40101     
40102     
40103     setActiveClass: function(state)
40104     {
40105         if(state){
40106            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40107            this.el.setStyle('position','relative');
40108         }else{
40109            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40110            this.el.setStyle('position', 'absolute');
40111         } 
40112     },
40113     
40114     /**
40115      * Returns the toolbar for this Panel if one was configured. 
40116      * @return {Roo.Toolbar} 
40117      */
40118     getToolbar : function(){
40119         return this.toolbar;
40120     },
40121     
40122     setActiveState : function(active)
40123     {
40124         this.active = active;
40125         this.setActiveClass(active);
40126         if(!active){
40127             if(this.fireEvent("deactivate", this) === false){
40128                 return false;
40129             }
40130             return true;
40131         }
40132         this.fireEvent("activate", this);
40133         return true;
40134     },
40135     /**
40136      * Updates this panel's element (not for iframe)
40137      * @param {String} content The new content
40138      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40139     */
40140     setContent : function(content, loadScripts){
40141         if (this.iframe) {
40142             return;
40143         }
40144         
40145         this.el.update(content, loadScripts);
40146     },
40147
40148     ignoreResize : function(w, h){
40149         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40150             return true;
40151         }else{
40152             this.lastSize = {width: w, height: h};
40153             return false;
40154         }
40155     },
40156     /**
40157      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40158      * @return {Roo.UpdateManager} The UpdateManager
40159      */
40160     getUpdateManager : function(){
40161         if (this.iframe) {
40162             return false;
40163         }
40164         return this.el.getUpdateManager();
40165     },
40166      /**
40167      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40168      * Does not work with IFRAME contents
40169      * @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:
40170 <pre><code>
40171 panel.load({
40172     url: "your-url.php",
40173     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40174     callback: yourFunction,
40175     scope: yourObject, //(optional scope)
40176     discardUrl: false,
40177     nocache: false,
40178     text: "Loading...",
40179     timeout: 30,
40180     scripts: false
40181 });
40182 </code></pre>
40183      
40184      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40185      * 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.
40186      * @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}
40187      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40188      * @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.
40189      * @return {Roo.ContentPanel} this
40190      */
40191     load : function(){
40192         
40193         if (this.iframe) {
40194             return this;
40195         }
40196         
40197         var um = this.el.getUpdateManager();
40198         um.update.apply(um, arguments);
40199         return this;
40200     },
40201
40202
40203     /**
40204      * 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.
40205      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40206      * @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)
40207      * @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)
40208      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40209      */
40210     setUrl : function(url, params, loadOnce){
40211         if (this.iframe) {
40212             this.iframeEl.dom.src = url;
40213             return false;
40214         }
40215         
40216         if(this.refreshDelegate){
40217             this.removeListener("activate", this.refreshDelegate);
40218         }
40219         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40220         this.on("activate", this.refreshDelegate);
40221         return this.el.getUpdateManager();
40222     },
40223     
40224     _handleRefresh : function(url, params, loadOnce){
40225         if(!loadOnce || !this.loaded){
40226             var updater = this.el.getUpdateManager();
40227             updater.update(url, params, this._setLoaded.createDelegate(this));
40228         }
40229     },
40230     
40231     _setLoaded : function(){
40232         this.loaded = true;
40233     }, 
40234     
40235     /**
40236      * Returns this panel's id
40237      * @return {String} 
40238      */
40239     getId : function(){
40240         return this.el.id;
40241     },
40242     
40243     /** 
40244      * Returns this panel's element - used by regiosn to add.
40245      * @return {Roo.Element} 
40246      */
40247     getEl : function(){
40248         return this.wrapEl || this.el;
40249     },
40250     
40251    
40252     
40253     adjustForComponents : function(width, height)
40254     {
40255         //Roo.log('adjustForComponents ');
40256         if(this.resizeEl != this.el){
40257             width -= this.el.getFrameWidth('lr');
40258             height -= this.el.getFrameWidth('tb');
40259         }
40260         if(this.toolbar){
40261             var te = this.toolbar.getEl();
40262             te.setWidth(width);
40263             height -= te.getHeight();
40264         }
40265         if(this.footer){
40266             var te = this.footer.getEl();
40267             te.setWidth(width);
40268             height -= te.getHeight();
40269         }
40270         
40271         
40272         if(this.adjustments){
40273             width += this.adjustments[0];
40274             height += this.adjustments[1];
40275         }
40276         return {"width": width, "height": height};
40277     },
40278     
40279     setSize : function(width, height){
40280         if(this.fitToFrame && !this.ignoreResize(width, height)){
40281             if(this.fitContainer && this.resizeEl != this.el){
40282                 this.el.setSize(width, height);
40283             }
40284             var size = this.adjustForComponents(width, height);
40285             if (this.iframe) {
40286                 this.iframeEl.setSize(width,height);
40287             }
40288             
40289             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40290             this.fireEvent('resize', this, size.width, size.height);
40291             
40292             
40293         }
40294     },
40295     
40296     /**
40297      * Returns this panel's title
40298      * @return {String} 
40299      */
40300     getTitle : function(){
40301         
40302         if (typeof(this.title) != 'object') {
40303             return this.title;
40304         }
40305         
40306         var t = '';
40307         for (var k in this.title) {
40308             if (!this.title.hasOwnProperty(k)) {
40309                 continue;
40310             }
40311             
40312             if (k.indexOf('-') >= 0) {
40313                 var s = k.split('-');
40314                 for (var i = 0; i<s.length; i++) {
40315                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40316                 }
40317             } else {
40318                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40319             }
40320         }
40321         return t;
40322     },
40323     
40324     /**
40325      * Set this panel's title
40326      * @param {String} title
40327      */
40328     setTitle : function(title){
40329         this.title = title;
40330         if(this.region){
40331             this.region.updatePanelTitle(this, title);
40332         }
40333     },
40334     
40335     /**
40336      * Returns true is this panel was configured to be closable
40337      * @return {Boolean} 
40338      */
40339     isClosable : function(){
40340         return this.closable;
40341     },
40342     
40343     beforeSlide : function(){
40344         this.el.clip();
40345         this.resizeEl.clip();
40346     },
40347     
40348     afterSlide : function(){
40349         this.el.unclip();
40350         this.resizeEl.unclip();
40351     },
40352     
40353     /**
40354      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40355      *   Will fail silently if the {@link #setUrl} method has not been called.
40356      *   This does not activate the panel, just updates its content.
40357      */
40358     refresh : function(){
40359         if(this.refreshDelegate){
40360            this.loaded = false;
40361            this.refreshDelegate();
40362         }
40363     },
40364     
40365     /**
40366      * Destroys this panel
40367      */
40368     destroy : function(){
40369         this.el.removeAllListeners();
40370         var tempEl = document.createElement("span");
40371         tempEl.appendChild(this.el.dom);
40372         tempEl.innerHTML = "";
40373         this.el.remove();
40374         this.el = null;
40375     },
40376     
40377     /**
40378      * form - if the content panel contains a form - this is a reference to it.
40379      * @type {Roo.form.Form}
40380      */
40381     form : false,
40382     /**
40383      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40384      *    This contains a reference to it.
40385      * @type {Roo.View}
40386      */
40387     view : false,
40388     
40389       /**
40390      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40391      * <pre><code>
40392
40393 layout.addxtype({
40394        xtype : 'Form',
40395        items: [ .... ]
40396    }
40397 );
40398
40399 </code></pre>
40400      * @param {Object} cfg Xtype definition of item to add.
40401      */
40402     
40403     
40404     getChildContainer: function () {
40405         return this.getEl();
40406     }
40407     
40408     
40409     /*
40410         var  ret = new Roo.factory(cfg);
40411         return ret;
40412         
40413         
40414         // add form..
40415         if (cfg.xtype.match(/^Form$/)) {
40416             
40417             var el;
40418             //if (this.footer) {
40419             //    el = this.footer.container.insertSibling(false, 'before');
40420             //} else {
40421                 el = this.el.createChild();
40422             //}
40423
40424             this.form = new  Roo.form.Form(cfg);
40425             
40426             
40427             if ( this.form.allItems.length) {
40428                 this.form.render(el.dom);
40429             }
40430             return this.form;
40431         }
40432         // should only have one of theses..
40433         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40434             // views.. should not be just added - used named prop 'view''
40435             
40436             cfg.el = this.el.appendChild(document.createElement("div"));
40437             // factory?
40438             
40439             var ret = new Roo.factory(cfg);
40440              
40441              ret.render && ret.render(false, ''); // render blank..
40442             this.view = ret;
40443             return ret;
40444         }
40445         return false;
40446     }
40447     \*/
40448 });
40449  
40450 /**
40451  * @class Roo.bootstrap.panel.Grid
40452  * @extends Roo.bootstrap.panel.Content
40453  * @constructor
40454  * Create a new GridPanel.
40455  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40456  * @param {Object} config A the config object
40457   
40458  */
40459
40460
40461
40462 Roo.bootstrap.panel.Grid = function(config)
40463 {
40464     
40465       
40466     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40467         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40468
40469     config.el = this.wrapper;
40470     //this.el = this.wrapper;
40471     
40472       if (config.container) {
40473         // ctor'ed from a Border/panel.grid
40474         
40475         
40476         this.wrapper.setStyle("overflow", "hidden");
40477         this.wrapper.addClass('roo-grid-container');
40478
40479     }
40480     
40481     
40482     if(config.toolbar){
40483         var tool_el = this.wrapper.createChild();    
40484         this.toolbar = Roo.factory(config.toolbar);
40485         var ti = [];
40486         if (config.toolbar.items) {
40487             ti = config.toolbar.items ;
40488             delete config.toolbar.items ;
40489         }
40490         
40491         var nitems = [];
40492         this.toolbar.render(tool_el);
40493         for(var i =0;i < ti.length;i++) {
40494           //  Roo.log(['add child', items[i]]);
40495             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40496         }
40497         this.toolbar.items = nitems;
40498         
40499         delete config.toolbar;
40500     }
40501     
40502     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40503     config.grid.scrollBody = true;;
40504     config.grid.monitorWindowResize = false; // turn off autosizing
40505     config.grid.autoHeight = false;
40506     config.grid.autoWidth = false;
40507     
40508     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40509     
40510     if (config.background) {
40511         // render grid on panel activation (if panel background)
40512         this.on('activate', function(gp) {
40513             if (!gp.grid.rendered) {
40514                 gp.grid.render(this.wrapper);
40515                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40516             }
40517         });
40518             
40519     } else {
40520         this.grid.render(this.wrapper);
40521         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40522
40523     }
40524     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40525     // ??? needed ??? config.el = this.wrapper;
40526     
40527     
40528     
40529   
40530     // xtype created footer. - not sure if will work as we normally have to render first..
40531     if (this.footer && !this.footer.el && this.footer.xtype) {
40532         
40533         var ctr = this.grid.getView().getFooterPanel(true);
40534         this.footer.dataSource = this.grid.dataSource;
40535         this.footer = Roo.factory(this.footer, Roo);
40536         this.footer.render(ctr);
40537         
40538     }
40539     
40540     
40541     
40542     
40543      
40544 };
40545
40546 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40547     getId : function(){
40548         return this.grid.id;
40549     },
40550     
40551     /**
40552      * Returns the grid for this panel
40553      * @return {Roo.bootstrap.Table} 
40554      */
40555     getGrid : function(){
40556         return this.grid;    
40557     },
40558     
40559     setSize : function(width, height){
40560         if(!this.ignoreResize(width, height)){
40561             var grid = this.grid;
40562             var size = this.adjustForComponents(width, height);
40563             // tfoot is not a footer?
40564           
40565             
40566             var gridel = grid.getGridEl();
40567             gridel.setSize(size.width, size.height);
40568             
40569             var tbd = grid.getGridEl().select('tbody', true).first();
40570             var thd = grid.getGridEl().select('thead',true).first();
40571             var tbf= grid.getGridEl().select('tfoot', true).first();
40572
40573             if (tbf) {
40574                 size.height -= tbf.getHeight();
40575             }
40576             if (thd) {
40577                 size.height -= thd.getHeight();
40578             }
40579             
40580             tbd.setSize(size.width, size.height );
40581             // this is for the account management tab -seems to work there.
40582             var thd = grid.getGridEl().select('thead',true).first();
40583             //if (tbd) {
40584             //    tbd.setSize(size.width, size.height - thd.getHeight());
40585             //}
40586              
40587             grid.autoSize();
40588         }
40589     },
40590      
40591     
40592     
40593     beforeSlide : function(){
40594         this.grid.getView().scroller.clip();
40595     },
40596     
40597     afterSlide : function(){
40598         this.grid.getView().scroller.unclip();
40599     },
40600     
40601     destroy : function(){
40602         this.grid.destroy();
40603         delete this.grid;
40604         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40605     }
40606 });
40607
40608 /**
40609  * @class Roo.bootstrap.panel.Nest
40610  * @extends Roo.bootstrap.panel.Content
40611  * @constructor
40612  * Create a new Panel, that can contain a layout.Border.
40613  * 
40614  * 
40615  * @param {Roo.BorderLayout} layout The layout for this panel
40616  * @param {String/Object} config A string to set only the title or a config object
40617  */
40618 Roo.bootstrap.panel.Nest = function(config)
40619 {
40620     // construct with only one argument..
40621     /* FIXME - implement nicer consturctors
40622     if (layout.layout) {
40623         config = layout;
40624         layout = config.layout;
40625         delete config.layout;
40626     }
40627     if (layout.xtype && !layout.getEl) {
40628         // then layout needs constructing..
40629         layout = Roo.factory(layout, Roo);
40630     }
40631     */
40632     
40633     config.el =  config.layout.getEl();
40634     
40635     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40636     
40637     config.layout.monitorWindowResize = false; // turn off autosizing
40638     this.layout = config.layout;
40639     this.layout.getEl().addClass("roo-layout-nested-layout");
40640     this.layout.parent = this;
40641     
40642     
40643     
40644     
40645 };
40646
40647 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40648
40649     setSize : function(width, height){
40650         if(!this.ignoreResize(width, height)){
40651             var size = this.adjustForComponents(width, height);
40652             var el = this.layout.getEl();
40653             if (size.height < 1) {
40654                 el.setWidth(size.width);   
40655             } else {
40656                 el.setSize(size.width, size.height);
40657             }
40658             var touch = el.dom.offsetWidth;
40659             this.layout.layout();
40660             // ie requires a double layout on the first pass
40661             if(Roo.isIE && !this.initialized){
40662                 this.initialized = true;
40663                 this.layout.layout();
40664             }
40665         }
40666     },
40667     
40668     // activate all subpanels if not currently active..
40669     
40670     setActiveState : function(active){
40671         this.active = active;
40672         this.setActiveClass(active);
40673         
40674         if(!active){
40675             this.fireEvent("deactivate", this);
40676             return;
40677         }
40678         
40679         this.fireEvent("activate", this);
40680         // not sure if this should happen before or after..
40681         if (!this.layout) {
40682             return; // should not happen..
40683         }
40684         var reg = false;
40685         for (var r in this.layout.regions) {
40686             reg = this.layout.getRegion(r);
40687             if (reg.getActivePanel()) {
40688                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40689                 reg.setActivePanel(reg.getActivePanel());
40690                 continue;
40691             }
40692             if (!reg.panels.length) {
40693                 continue;
40694             }
40695             reg.showPanel(reg.getPanel(0));
40696         }
40697         
40698         
40699         
40700         
40701     },
40702     
40703     /**
40704      * Returns the nested BorderLayout for this panel
40705      * @return {Roo.BorderLayout} 
40706      */
40707     getLayout : function(){
40708         return this.layout;
40709     },
40710     
40711      /**
40712      * Adds a xtype elements to the layout of the nested panel
40713      * <pre><code>
40714
40715 panel.addxtype({
40716        xtype : 'ContentPanel',
40717        region: 'west',
40718        items: [ .... ]
40719    }
40720 );
40721
40722 panel.addxtype({
40723         xtype : 'NestedLayoutPanel',
40724         region: 'west',
40725         layout: {
40726            center: { },
40727            west: { }   
40728         },
40729         items : [ ... list of content panels or nested layout panels.. ]
40730    }
40731 );
40732 </code></pre>
40733      * @param {Object} cfg Xtype definition of item to add.
40734      */
40735     addxtype : function(cfg) {
40736         return this.layout.addxtype(cfg);
40737     
40738     }
40739 });/*
40740  * Based on:
40741  * Ext JS Library 1.1.1
40742  * Copyright(c) 2006-2007, Ext JS, LLC.
40743  *
40744  * Originally Released Under LGPL - original licence link has changed is not relivant.
40745  *
40746  * Fork - LGPL
40747  * <script type="text/javascript">
40748  */
40749 /**
40750  * @class Roo.TabPanel
40751  * @extends Roo.util.Observable
40752  * A lightweight tab container.
40753  * <br><br>
40754  * Usage:
40755  * <pre><code>
40756 // basic tabs 1, built from existing content
40757 var tabs = new Roo.TabPanel("tabs1");
40758 tabs.addTab("script", "View Script");
40759 tabs.addTab("markup", "View Markup");
40760 tabs.activate("script");
40761
40762 // more advanced tabs, built from javascript
40763 var jtabs = new Roo.TabPanel("jtabs");
40764 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40765
40766 // set up the UpdateManager
40767 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40768 var updater = tab2.getUpdateManager();
40769 updater.setDefaultUrl("ajax1.htm");
40770 tab2.on('activate', updater.refresh, updater, true);
40771
40772 // Use setUrl for Ajax loading
40773 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40774 tab3.setUrl("ajax2.htm", null, true);
40775
40776 // Disabled tab
40777 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40778 tab4.disable();
40779
40780 jtabs.activate("jtabs-1");
40781  * </code></pre>
40782  * @constructor
40783  * Create a new TabPanel.
40784  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40785  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40786  */
40787 Roo.bootstrap.panel.Tabs = function(config){
40788     /**
40789     * The container element for this TabPanel.
40790     * @type Roo.Element
40791     */
40792     this.el = Roo.get(config.el);
40793     delete config.el;
40794     if(config){
40795         if(typeof config == "boolean"){
40796             this.tabPosition = config ? "bottom" : "top";
40797         }else{
40798             Roo.apply(this, config);
40799         }
40800     }
40801     
40802     if(this.tabPosition == "bottom"){
40803         // if tabs are at the bottom = create the body first.
40804         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40805         this.el.addClass("roo-tabs-bottom");
40806     }
40807     // next create the tabs holders
40808     
40809     if (this.tabPosition == "west"){
40810         
40811         var reg = this.region; // fake it..
40812         while (reg) {
40813             if (!reg.mgr.parent) {
40814                 break;
40815             }
40816             reg = reg.mgr.parent.region;
40817         }
40818         Roo.log("got nest?");
40819         Roo.log(reg);
40820         if (reg.mgr.getRegion('west')) {
40821             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40822             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40823             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40824             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40825             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40826         
40827             
40828         }
40829         
40830         
40831     } else {
40832      
40833         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40834         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40835         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40836         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40837     }
40838     
40839     
40840     if(Roo.isIE){
40841         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40842     }
40843     
40844     // finally - if tabs are at the top, then create the body last..
40845     if(this.tabPosition != "bottom"){
40846         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40847          * @type Roo.Element
40848          */
40849         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40850         this.el.addClass("roo-tabs-top");
40851     }
40852     this.items = [];
40853
40854     this.bodyEl.setStyle("position", "relative");
40855
40856     this.active = null;
40857     this.activateDelegate = this.activate.createDelegate(this);
40858
40859     this.addEvents({
40860         /**
40861          * @event tabchange
40862          * Fires when the active tab changes
40863          * @param {Roo.TabPanel} this
40864          * @param {Roo.TabPanelItem} activePanel The new active tab
40865          */
40866         "tabchange": true,
40867         /**
40868          * @event beforetabchange
40869          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40870          * @param {Roo.TabPanel} this
40871          * @param {Object} e Set cancel to true on this object to cancel the tab change
40872          * @param {Roo.TabPanelItem} tab The tab being changed to
40873          */
40874         "beforetabchange" : true
40875     });
40876
40877     Roo.EventManager.onWindowResize(this.onResize, this);
40878     this.cpad = this.el.getPadding("lr");
40879     this.hiddenCount = 0;
40880
40881
40882     // toolbar on the tabbar support...
40883     if (this.toolbar) {
40884         alert("no toolbar support yet");
40885         this.toolbar  = false;
40886         /*
40887         var tcfg = this.toolbar;
40888         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40889         this.toolbar = new Roo.Toolbar(tcfg);
40890         if (Roo.isSafari) {
40891             var tbl = tcfg.container.child('table', true);
40892             tbl.setAttribute('width', '100%');
40893         }
40894         */
40895         
40896     }
40897    
40898
40899
40900     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40901 };
40902
40903 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40904     /*
40905      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40906      */
40907     tabPosition : "top",
40908     /*
40909      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40910      */
40911     currentTabWidth : 0,
40912     /*
40913      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40914      */
40915     minTabWidth : 40,
40916     /*
40917      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40918      */
40919     maxTabWidth : 250,
40920     /*
40921      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40922      */
40923     preferredTabWidth : 175,
40924     /*
40925      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40926      */
40927     resizeTabs : false,
40928     /*
40929      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40930      */
40931     monitorResize : true,
40932     /*
40933      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40934      */
40935     toolbar : false,  // set by caller..
40936     
40937     region : false, /// set by caller
40938     
40939     disableTooltips : true, // not used yet...
40940
40941     /**
40942      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40943      * @param {String} id The id of the div to use <b>or create</b>
40944      * @param {String} text The text for the tab
40945      * @param {String} content (optional) Content to put in the TabPanelItem body
40946      * @param {Boolean} closable (optional) True to create a close icon on the tab
40947      * @return {Roo.TabPanelItem} The created TabPanelItem
40948      */
40949     addTab : function(id, text, content, closable, tpl)
40950     {
40951         var item = new Roo.bootstrap.panel.TabItem({
40952             panel: this,
40953             id : id,
40954             text : text,
40955             closable : closable,
40956             tpl : tpl
40957         });
40958         this.addTabItem(item);
40959         if(content){
40960             item.setContent(content);
40961         }
40962         return item;
40963     },
40964
40965     /**
40966      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40967      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40968      * @return {Roo.TabPanelItem}
40969      */
40970     getTab : function(id){
40971         return this.items[id];
40972     },
40973
40974     /**
40975      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40976      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40977      */
40978     hideTab : function(id){
40979         var t = this.items[id];
40980         if(!t.isHidden()){
40981            t.setHidden(true);
40982            this.hiddenCount++;
40983            this.autoSizeTabs();
40984         }
40985     },
40986
40987     /**
40988      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40989      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40990      */
40991     unhideTab : function(id){
40992         var t = this.items[id];
40993         if(t.isHidden()){
40994            t.setHidden(false);
40995            this.hiddenCount--;
40996            this.autoSizeTabs();
40997         }
40998     },
40999
41000     /**
41001      * Adds an existing {@link Roo.TabPanelItem}.
41002      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41003      */
41004     addTabItem : function(item)
41005     {
41006         this.items[item.id] = item;
41007         this.items.push(item);
41008         this.autoSizeTabs();
41009       //  if(this.resizeTabs){
41010     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41011   //         this.autoSizeTabs();
41012 //        }else{
41013 //            item.autoSize();
41014        // }
41015     },
41016
41017     /**
41018      * Removes a {@link Roo.TabPanelItem}.
41019      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41020      */
41021     removeTab : function(id){
41022         var items = this.items;
41023         var tab = items[id];
41024         if(!tab) { return; }
41025         var index = items.indexOf(tab);
41026         if(this.active == tab && items.length > 1){
41027             var newTab = this.getNextAvailable(index);
41028             if(newTab) {
41029                 newTab.activate();
41030             }
41031         }
41032         this.stripEl.dom.removeChild(tab.pnode.dom);
41033         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41034             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41035         }
41036         items.splice(index, 1);
41037         delete this.items[tab.id];
41038         tab.fireEvent("close", tab);
41039         tab.purgeListeners();
41040         this.autoSizeTabs();
41041     },
41042
41043     getNextAvailable : function(start){
41044         var items = this.items;
41045         var index = start;
41046         // look for a next tab that will slide over to
41047         // replace the one being removed
41048         while(index < items.length){
41049             var item = items[++index];
41050             if(item && !item.isHidden()){
41051                 return item;
41052             }
41053         }
41054         // if one isn't found select the previous tab (on the left)
41055         index = start;
41056         while(index >= 0){
41057             var item = items[--index];
41058             if(item && !item.isHidden()){
41059                 return item;
41060             }
41061         }
41062         return null;
41063     },
41064
41065     /**
41066      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41067      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41068      */
41069     disableTab : function(id){
41070         var tab = this.items[id];
41071         if(tab && this.active != tab){
41072             tab.disable();
41073         }
41074     },
41075
41076     /**
41077      * Enables a {@link Roo.TabPanelItem} that is disabled.
41078      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41079      */
41080     enableTab : function(id){
41081         var tab = this.items[id];
41082         tab.enable();
41083     },
41084
41085     /**
41086      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41087      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41088      * @return {Roo.TabPanelItem} The TabPanelItem.
41089      */
41090     activate : function(id)
41091     {
41092         //Roo.log('activite:'  + id);
41093         
41094         var tab = this.items[id];
41095         if(!tab){
41096             return null;
41097         }
41098         if(tab == this.active || tab.disabled){
41099             return tab;
41100         }
41101         var e = {};
41102         this.fireEvent("beforetabchange", this, e, tab);
41103         if(e.cancel !== true && !tab.disabled){
41104             if(this.active){
41105                 this.active.hide();
41106             }
41107             this.active = this.items[id];
41108             this.active.show();
41109             this.fireEvent("tabchange", this, this.active);
41110         }
41111         return tab;
41112     },
41113
41114     /**
41115      * Gets the active {@link Roo.TabPanelItem}.
41116      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41117      */
41118     getActiveTab : function(){
41119         return this.active;
41120     },
41121
41122     /**
41123      * Updates the tab body element to fit the height of the container element
41124      * for overflow scrolling
41125      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41126      */
41127     syncHeight : function(targetHeight){
41128         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41129         var bm = this.bodyEl.getMargins();
41130         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41131         this.bodyEl.setHeight(newHeight);
41132         return newHeight;
41133     },
41134
41135     onResize : function(){
41136         if(this.monitorResize){
41137             this.autoSizeTabs();
41138         }
41139     },
41140
41141     /**
41142      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41143      */
41144     beginUpdate : function(){
41145         this.updating = true;
41146     },
41147
41148     /**
41149      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41150      */
41151     endUpdate : function(){
41152         this.updating = false;
41153         this.autoSizeTabs();
41154     },
41155
41156     /**
41157      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41158      */
41159     autoSizeTabs : function()
41160     {
41161         var count = this.items.length;
41162         var vcount = count - this.hiddenCount;
41163         
41164         if (vcount < 2) {
41165             this.stripEl.hide();
41166         } else {
41167             this.stripEl.show();
41168         }
41169         
41170         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41171             return;
41172         }
41173         
41174         
41175         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41176         var availWidth = Math.floor(w / vcount);
41177         var b = this.stripBody;
41178         if(b.getWidth() > w){
41179             var tabs = this.items;
41180             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41181             if(availWidth < this.minTabWidth){
41182                 /*if(!this.sleft){    // incomplete scrolling code
41183                     this.createScrollButtons();
41184                 }
41185                 this.showScroll();
41186                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41187             }
41188         }else{
41189             if(this.currentTabWidth < this.preferredTabWidth){
41190                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41191             }
41192         }
41193     },
41194
41195     /**
41196      * Returns the number of tabs in this TabPanel.
41197      * @return {Number}
41198      */
41199      getCount : function(){
41200          return this.items.length;
41201      },
41202
41203     /**
41204      * Resizes all the tabs to the passed width
41205      * @param {Number} The new width
41206      */
41207     setTabWidth : function(width){
41208         this.currentTabWidth = width;
41209         for(var i = 0, len = this.items.length; i < len; i++) {
41210                 if(!this.items[i].isHidden()) {
41211                 this.items[i].setWidth(width);
41212             }
41213         }
41214     },
41215
41216     /**
41217      * Destroys this TabPanel
41218      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41219      */
41220     destroy : function(removeEl){
41221         Roo.EventManager.removeResizeListener(this.onResize, this);
41222         for(var i = 0, len = this.items.length; i < len; i++){
41223             this.items[i].purgeListeners();
41224         }
41225         if(removeEl === true){
41226             this.el.update("");
41227             this.el.remove();
41228         }
41229     },
41230     
41231     createStrip : function(container)
41232     {
41233         var strip = document.createElement("nav");
41234         strip.className = Roo.bootstrap.version == 4 ?
41235             "navbar-light bg-light" : 
41236             "navbar navbar-default"; //"x-tabs-wrap";
41237         container.appendChild(strip);
41238         return strip;
41239     },
41240     
41241     createStripList : function(strip)
41242     {
41243         // div wrapper for retard IE
41244         // returns the "tr" element.
41245         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41246         //'<div class="x-tabs-strip-wrap">'+
41247           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41248           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41249         return strip.firstChild; //.firstChild.firstChild.firstChild;
41250     },
41251     createBody : function(container)
41252     {
41253         var body = document.createElement("div");
41254         Roo.id(body, "tab-body");
41255         //Roo.fly(body).addClass("x-tabs-body");
41256         Roo.fly(body).addClass("tab-content");
41257         container.appendChild(body);
41258         return body;
41259     },
41260     createItemBody :function(bodyEl, id){
41261         var body = Roo.getDom(id);
41262         if(!body){
41263             body = document.createElement("div");
41264             body.id = id;
41265         }
41266         //Roo.fly(body).addClass("x-tabs-item-body");
41267         Roo.fly(body).addClass("tab-pane");
41268          bodyEl.insertBefore(body, bodyEl.firstChild);
41269         return body;
41270     },
41271     /** @private */
41272     createStripElements :  function(stripEl, text, closable, tpl)
41273     {
41274         var td = document.createElement("li"); // was td..
41275         td.className = 'nav-item';
41276         
41277         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41278         
41279         
41280         stripEl.appendChild(td);
41281         /*if(closable){
41282             td.className = "x-tabs-closable";
41283             if(!this.closeTpl){
41284                 this.closeTpl = new Roo.Template(
41285                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41286                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41287                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41288                 );
41289             }
41290             var el = this.closeTpl.overwrite(td, {"text": text});
41291             var close = el.getElementsByTagName("div")[0];
41292             var inner = el.getElementsByTagName("em")[0];
41293             return {"el": el, "close": close, "inner": inner};
41294         } else {
41295         */
41296         // not sure what this is..
41297 //            if(!this.tabTpl){
41298                 //this.tabTpl = new Roo.Template(
41299                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41300                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41301                 //);
41302 //                this.tabTpl = new Roo.Template(
41303 //                   '<a href="#">' +
41304 //                   '<span unselectable="on"' +
41305 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41306 //                            ' >{text}</span></a>'
41307 //                );
41308 //                
41309 //            }
41310
41311
41312             var template = tpl || this.tabTpl || false;
41313             
41314             if(!template){
41315                 template =  new Roo.Template(
41316                         Roo.bootstrap.version == 4 ? 
41317                             (
41318                                 '<a class="nav-link" href="#" unselectable="on"' +
41319                                      (this.disableTooltips ? '' : ' title="{text}"') +
41320                                      ' >{text}</a>'
41321                             ) : (
41322                                 '<a class="nav-link" href="#">' +
41323                                 '<span unselectable="on"' +
41324                                          (this.disableTooltips ? '' : ' title="{text}"') +
41325                                     ' >{text}</span></a>'
41326                             )
41327                 );
41328             }
41329             
41330             switch (typeof(template)) {
41331                 case 'object' :
41332                     break;
41333                 case 'string' :
41334                     template = new Roo.Template(template);
41335                     break;
41336                 default :
41337                     break;
41338             }
41339             
41340             var el = template.overwrite(td, {"text": text});
41341             
41342             var inner = el.getElementsByTagName("span")[0];
41343             
41344             return {"el": el, "inner": inner};
41345             
41346     }
41347         
41348     
41349 });
41350
41351 /**
41352  * @class Roo.TabPanelItem
41353  * @extends Roo.util.Observable
41354  * Represents an individual item (tab plus body) in a TabPanel.
41355  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41356  * @param {String} id The id of this TabPanelItem
41357  * @param {String} text The text for the tab of this TabPanelItem
41358  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41359  */
41360 Roo.bootstrap.panel.TabItem = function(config){
41361     /**
41362      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41363      * @type Roo.TabPanel
41364      */
41365     this.tabPanel = config.panel;
41366     /**
41367      * The id for this TabPanelItem
41368      * @type String
41369      */
41370     this.id = config.id;
41371     /** @private */
41372     this.disabled = false;
41373     /** @private */
41374     this.text = config.text;
41375     /** @private */
41376     this.loaded = false;
41377     this.closable = config.closable;
41378
41379     /**
41380      * The body element for this TabPanelItem.
41381      * @type Roo.Element
41382      */
41383     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41384     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41385     this.bodyEl.setStyle("display", "block");
41386     this.bodyEl.setStyle("zoom", "1");
41387     //this.hideAction();
41388
41389     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41390     /** @private */
41391     this.el = Roo.get(els.el);
41392     this.inner = Roo.get(els.inner, true);
41393      this.textEl = Roo.bootstrap.version == 4 ?
41394         this.el : Roo.get(this.el.dom.firstChild, true);
41395
41396     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41397     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41398
41399     
41400 //    this.el.on("mousedown", this.onTabMouseDown, this);
41401     this.el.on("click", this.onTabClick, this);
41402     /** @private */
41403     if(config.closable){
41404         var c = Roo.get(els.close, true);
41405         c.dom.title = this.closeText;
41406         c.addClassOnOver("close-over");
41407         c.on("click", this.closeClick, this);
41408      }
41409
41410     this.addEvents({
41411          /**
41412          * @event activate
41413          * Fires when this tab becomes the active tab.
41414          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41415          * @param {Roo.TabPanelItem} this
41416          */
41417         "activate": true,
41418         /**
41419          * @event beforeclose
41420          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41421          * @param {Roo.TabPanelItem} this
41422          * @param {Object} e Set cancel to true on this object to cancel the close.
41423          */
41424         "beforeclose": true,
41425         /**
41426          * @event close
41427          * Fires when this tab is closed.
41428          * @param {Roo.TabPanelItem} this
41429          */
41430          "close": true,
41431         /**
41432          * @event deactivate
41433          * Fires when this tab is no longer the active tab.
41434          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41435          * @param {Roo.TabPanelItem} this
41436          */
41437          "deactivate" : true
41438     });
41439     this.hidden = false;
41440
41441     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41442 };
41443
41444 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41445            {
41446     purgeListeners : function(){
41447        Roo.util.Observable.prototype.purgeListeners.call(this);
41448        this.el.removeAllListeners();
41449     },
41450     /**
41451      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41452      */
41453     show : function(){
41454         this.status_node.addClass("active");
41455         this.showAction();
41456         if(Roo.isOpera){
41457             this.tabPanel.stripWrap.repaint();
41458         }
41459         this.fireEvent("activate", this.tabPanel, this);
41460     },
41461
41462     /**
41463      * Returns true if this tab is the active tab.
41464      * @return {Boolean}
41465      */
41466     isActive : function(){
41467         return this.tabPanel.getActiveTab() == this;
41468     },
41469
41470     /**
41471      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41472      */
41473     hide : function(){
41474         this.status_node.removeClass("active");
41475         this.hideAction();
41476         this.fireEvent("deactivate", this.tabPanel, this);
41477     },
41478
41479     hideAction : function(){
41480         this.bodyEl.hide();
41481         this.bodyEl.setStyle("position", "absolute");
41482         this.bodyEl.setLeft("-20000px");
41483         this.bodyEl.setTop("-20000px");
41484     },
41485
41486     showAction : function(){
41487         this.bodyEl.setStyle("position", "relative");
41488         this.bodyEl.setTop("");
41489         this.bodyEl.setLeft("");
41490         this.bodyEl.show();
41491     },
41492
41493     /**
41494      * Set the tooltip for the tab.
41495      * @param {String} tooltip The tab's tooltip
41496      */
41497     setTooltip : function(text){
41498         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41499             this.textEl.dom.qtip = text;
41500             this.textEl.dom.removeAttribute('title');
41501         }else{
41502             this.textEl.dom.title = text;
41503         }
41504     },
41505
41506     onTabClick : function(e){
41507         e.preventDefault();
41508         this.tabPanel.activate(this.id);
41509     },
41510
41511     onTabMouseDown : function(e){
41512         e.preventDefault();
41513         this.tabPanel.activate(this.id);
41514     },
41515 /*
41516     getWidth : function(){
41517         return this.inner.getWidth();
41518     },
41519
41520     setWidth : function(width){
41521         var iwidth = width - this.linode.getPadding("lr");
41522         this.inner.setWidth(iwidth);
41523         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41524         this.linode.setWidth(width);
41525     },
41526 */
41527     /**
41528      * Show or hide the tab
41529      * @param {Boolean} hidden True to hide or false to show.
41530      */
41531     setHidden : function(hidden){
41532         this.hidden = hidden;
41533         this.linode.setStyle("display", hidden ? "none" : "");
41534     },
41535
41536     /**
41537      * Returns true if this tab is "hidden"
41538      * @return {Boolean}
41539      */
41540     isHidden : function(){
41541         return this.hidden;
41542     },
41543
41544     /**
41545      * Returns the text for this tab
41546      * @return {String}
41547      */
41548     getText : function(){
41549         return this.text;
41550     },
41551     /*
41552     autoSize : function(){
41553         //this.el.beginMeasure();
41554         this.textEl.setWidth(1);
41555         /*
41556          *  #2804 [new] Tabs in Roojs
41557          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41558          */
41559         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41560         //this.el.endMeasure();
41561     //},
41562
41563     /**
41564      * Sets the text for the tab (Note: this also sets the tooltip text)
41565      * @param {String} text The tab's text and tooltip
41566      */
41567     setText : function(text){
41568         this.text = text;
41569         this.textEl.update(text);
41570         this.setTooltip(text);
41571         //if(!this.tabPanel.resizeTabs){
41572         //    this.autoSize();
41573         //}
41574     },
41575     /**
41576      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41577      */
41578     activate : function(){
41579         this.tabPanel.activate(this.id);
41580     },
41581
41582     /**
41583      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41584      */
41585     disable : function(){
41586         if(this.tabPanel.active != this){
41587             this.disabled = true;
41588             this.status_node.addClass("disabled");
41589         }
41590     },
41591
41592     /**
41593      * Enables this TabPanelItem if it was previously disabled.
41594      */
41595     enable : function(){
41596         this.disabled = false;
41597         this.status_node.removeClass("disabled");
41598     },
41599
41600     /**
41601      * Sets the content for this TabPanelItem.
41602      * @param {String} content The content
41603      * @param {Boolean} loadScripts true to look for and load scripts
41604      */
41605     setContent : function(content, loadScripts){
41606         this.bodyEl.update(content, loadScripts);
41607     },
41608
41609     /**
41610      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41611      * @return {Roo.UpdateManager} The UpdateManager
41612      */
41613     getUpdateManager : function(){
41614         return this.bodyEl.getUpdateManager();
41615     },
41616
41617     /**
41618      * Set a URL to be used to load the content for this TabPanelItem.
41619      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41620      * @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)
41621      * @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)
41622      * @return {Roo.UpdateManager} The UpdateManager
41623      */
41624     setUrl : function(url, params, loadOnce){
41625         if(this.refreshDelegate){
41626             this.un('activate', this.refreshDelegate);
41627         }
41628         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41629         this.on("activate", this.refreshDelegate);
41630         return this.bodyEl.getUpdateManager();
41631     },
41632
41633     /** @private */
41634     _handleRefresh : function(url, params, loadOnce){
41635         if(!loadOnce || !this.loaded){
41636             var updater = this.bodyEl.getUpdateManager();
41637             updater.update(url, params, this._setLoaded.createDelegate(this));
41638         }
41639     },
41640
41641     /**
41642      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41643      *   Will fail silently if the setUrl method has not been called.
41644      *   This does not activate the panel, just updates its content.
41645      */
41646     refresh : function(){
41647         if(this.refreshDelegate){
41648            this.loaded = false;
41649            this.refreshDelegate();
41650         }
41651     },
41652
41653     /** @private */
41654     _setLoaded : function(){
41655         this.loaded = true;
41656     },
41657
41658     /** @private */
41659     closeClick : function(e){
41660         var o = {};
41661         e.stopEvent();
41662         this.fireEvent("beforeclose", this, o);
41663         if(o.cancel !== true){
41664             this.tabPanel.removeTab(this.id);
41665         }
41666     },
41667     /**
41668      * The text displayed in the tooltip for the close icon.
41669      * @type String
41670      */
41671     closeText : "Close this tab"
41672 });
41673 /**
41674 *    This script refer to:
41675 *    Title: International Telephone Input
41676 *    Author: Jack O'Connor
41677 *    Code version:  v12.1.12
41678 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41679 **/
41680
41681 Roo.bootstrap.PhoneInputData = function() {
41682     var d = [
41683       [
41684         "Afghanistan (‫افغانستان‬‎)",
41685         "af",
41686         "93"
41687       ],
41688       [
41689         "Albania (Shqipëri)",
41690         "al",
41691         "355"
41692       ],
41693       [
41694         "Algeria (‫الجزائر‬‎)",
41695         "dz",
41696         "213"
41697       ],
41698       [
41699         "American Samoa",
41700         "as",
41701         "1684"
41702       ],
41703       [
41704         "Andorra",
41705         "ad",
41706         "376"
41707       ],
41708       [
41709         "Angola",
41710         "ao",
41711         "244"
41712       ],
41713       [
41714         "Anguilla",
41715         "ai",
41716         "1264"
41717       ],
41718       [
41719         "Antigua and Barbuda",
41720         "ag",
41721         "1268"
41722       ],
41723       [
41724         "Argentina",
41725         "ar",
41726         "54"
41727       ],
41728       [
41729         "Armenia (Հայաստան)",
41730         "am",
41731         "374"
41732       ],
41733       [
41734         "Aruba",
41735         "aw",
41736         "297"
41737       ],
41738       [
41739         "Australia",
41740         "au",
41741         "61",
41742         0
41743       ],
41744       [
41745         "Austria (Österreich)",
41746         "at",
41747         "43"
41748       ],
41749       [
41750         "Azerbaijan (Azərbaycan)",
41751         "az",
41752         "994"
41753       ],
41754       [
41755         "Bahamas",
41756         "bs",
41757         "1242"
41758       ],
41759       [
41760         "Bahrain (‫البحرين‬‎)",
41761         "bh",
41762         "973"
41763       ],
41764       [
41765         "Bangladesh (বাংলাদেশ)",
41766         "bd",
41767         "880"
41768       ],
41769       [
41770         "Barbados",
41771         "bb",
41772         "1246"
41773       ],
41774       [
41775         "Belarus (Беларусь)",
41776         "by",
41777         "375"
41778       ],
41779       [
41780         "Belgium (België)",
41781         "be",
41782         "32"
41783       ],
41784       [
41785         "Belize",
41786         "bz",
41787         "501"
41788       ],
41789       [
41790         "Benin (Bénin)",
41791         "bj",
41792         "229"
41793       ],
41794       [
41795         "Bermuda",
41796         "bm",
41797         "1441"
41798       ],
41799       [
41800         "Bhutan (འབྲུག)",
41801         "bt",
41802         "975"
41803       ],
41804       [
41805         "Bolivia",
41806         "bo",
41807         "591"
41808       ],
41809       [
41810         "Bosnia and Herzegovina (Босна и Херцеговина)",
41811         "ba",
41812         "387"
41813       ],
41814       [
41815         "Botswana",
41816         "bw",
41817         "267"
41818       ],
41819       [
41820         "Brazil (Brasil)",
41821         "br",
41822         "55"
41823       ],
41824       [
41825         "British Indian Ocean Territory",
41826         "io",
41827         "246"
41828       ],
41829       [
41830         "British Virgin Islands",
41831         "vg",
41832         "1284"
41833       ],
41834       [
41835         "Brunei",
41836         "bn",
41837         "673"
41838       ],
41839       [
41840         "Bulgaria (България)",
41841         "bg",
41842         "359"
41843       ],
41844       [
41845         "Burkina Faso",
41846         "bf",
41847         "226"
41848       ],
41849       [
41850         "Burundi (Uburundi)",
41851         "bi",
41852         "257"
41853       ],
41854       [
41855         "Cambodia (កម្ពុជា)",
41856         "kh",
41857         "855"
41858       ],
41859       [
41860         "Cameroon (Cameroun)",
41861         "cm",
41862         "237"
41863       ],
41864       [
41865         "Canada",
41866         "ca",
41867         "1",
41868         1,
41869         ["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"]
41870       ],
41871       [
41872         "Cape Verde (Kabu Verdi)",
41873         "cv",
41874         "238"
41875       ],
41876       [
41877         "Caribbean Netherlands",
41878         "bq",
41879         "599",
41880         1
41881       ],
41882       [
41883         "Cayman Islands",
41884         "ky",
41885         "1345"
41886       ],
41887       [
41888         "Central African Republic (République centrafricaine)",
41889         "cf",
41890         "236"
41891       ],
41892       [
41893         "Chad (Tchad)",
41894         "td",
41895         "235"
41896       ],
41897       [
41898         "Chile",
41899         "cl",
41900         "56"
41901       ],
41902       [
41903         "China (中国)",
41904         "cn",
41905         "86"
41906       ],
41907       [
41908         "Christmas Island",
41909         "cx",
41910         "61",
41911         2
41912       ],
41913       [
41914         "Cocos (Keeling) Islands",
41915         "cc",
41916         "61",
41917         1
41918       ],
41919       [
41920         "Colombia",
41921         "co",
41922         "57"
41923       ],
41924       [
41925         "Comoros (‫جزر القمر‬‎)",
41926         "km",
41927         "269"
41928       ],
41929       [
41930         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41931         "cd",
41932         "243"
41933       ],
41934       [
41935         "Congo (Republic) (Congo-Brazzaville)",
41936         "cg",
41937         "242"
41938       ],
41939       [
41940         "Cook Islands",
41941         "ck",
41942         "682"
41943       ],
41944       [
41945         "Costa Rica",
41946         "cr",
41947         "506"
41948       ],
41949       [
41950         "Côte d’Ivoire",
41951         "ci",
41952         "225"
41953       ],
41954       [
41955         "Croatia (Hrvatska)",
41956         "hr",
41957         "385"
41958       ],
41959       [
41960         "Cuba",
41961         "cu",
41962         "53"
41963       ],
41964       [
41965         "Curaçao",
41966         "cw",
41967         "599",
41968         0
41969       ],
41970       [
41971         "Cyprus (Κύπρος)",
41972         "cy",
41973         "357"
41974       ],
41975       [
41976         "Czech Republic (Česká republika)",
41977         "cz",
41978         "420"
41979       ],
41980       [
41981         "Denmark (Danmark)",
41982         "dk",
41983         "45"
41984       ],
41985       [
41986         "Djibouti",
41987         "dj",
41988         "253"
41989       ],
41990       [
41991         "Dominica",
41992         "dm",
41993         "1767"
41994       ],
41995       [
41996         "Dominican Republic (República Dominicana)",
41997         "do",
41998         "1",
41999         2,
42000         ["809", "829", "849"]
42001       ],
42002       [
42003         "Ecuador",
42004         "ec",
42005         "593"
42006       ],
42007       [
42008         "Egypt (‫مصر‬‎)",
42009         "eg",
42010         "20"
42011       ],
42012       [
42013         "El Salvador",
42014         "sv",
42015         "503"
42016       ],
42017       [
42018         "Equatorial Guinea (Guinea Ecuatorial)",
42019         "gq",
42020         "240"
42021       ],
42022       [
42023         "Eritrea",
42024         "er",
42025         "291"
42026       ],
42027       [
42028         "Estonia (Eesti)",
42029         "ee",
42030         "372"
42031       ],
42032       [
42033         "Ethiopia",
42034         "et",
42035         "251"
42036       ],
42037       [
42038         "Falkland Islands (Islas Malvinas)",
42039         "fk",
42040         "500"
42041       ],
42042       [
42043         "Faroe Islands (Føroyar)",
42044         "fo",
42045         "298"
42046       ],
42047       [
42048         "Fiji",
42049         "fj",
42050         "679"
42051       ],
42052       [
42053         "Finland (Suomi)",
42054         "fi",
42055         "358",
42056         0
42057       ],
42058       [
42059         "France",
42060         "fr",
42061         "33"
42062       ],
42063       [
42064         "French Guiana (Guyane française)",
42065         "gf",
42066         "594"
42067       ],
42068       [
42069         "French Polynesia (Polynésie française)",
42070         "pf",
42071         "689"
42072       ],
42073       [
42074         "Gabon",
42075         "ga",
42076         "241"
42077       ],
42078       [
42079         "Gambia",
42080         "gm",
42081         "220"
42082       ],
42083       [
42084         "Georgia (საქართველო)",
42085         "ge",
42086         "995"
42087       ],
42088       [
42089         "Germany (Deutschland)",
42090         "de",
42091         "49"
42092       ],
42093       [
42094         "Ghana (Gaana)",
42095         "gh",
42096         "233"
42097       ],
42098       [
42099         "Gibraltar",
42100         "gi",
42101         "350"
42102       ],
42103       [
42104         "Greece (Ελλάδα)",
42105         "gr",
42106         "30"
42107       ],
42108       [
42109         "Greenland (Kalaallit Nunaat)",
42110         "gl",
42111         "299"
42112       ],
42113       [
42114         "Grenada",
42115         "gd",
42116         "1473"
42117       ],
42118       [
42119         "Guadeloupe",
42120         "gp",
42121         "590",
42122         0
42123       ],
42124       [
42125         "Guam",
42126         "gu",
42127         "1671"
42128       ],
42129       [
42130         "Guatemala",
42131         "gt",
42132         "502"
42133       ],
42134       [
42135         "Guernsey",
42136         "gg",
42137         "44",
42138         1
42139       ],
42140       [
42141         "Guinea (Guinée)",
42142         "gn",
42143         "224"
42144       ],
42145       [
42146         "Guinea-Bissau (Guiné Bissau)",
42147         "gw",
42148         "245"
42149       ],
42150       [
42151         "Guyana",
42152         "gy",
42153         "592"
42154       ],
42155       [
42156         "Haiti",
42157         "ht",
42158         "509"
42159       ],
42160       [
42161         "Honduras",
42162         "hn",
42163         "504"
42164       ],
42165       [
42166         "Hong Kong (香港)",
42167         "hk",
42168         "852"
42169       ],
42170       [
42171         "Hungary (Magyarország)",
42172         "hu",
42173         "36"
42174       ],
42175       [
42176         "Iceland (Ísland)",
42177         "is",
42178         "354"
42179       ],
42180       [
42181         "India (भारत)",
42182         "in",
42183         "91"
42184       ],
42185       [
42186         "Indonesia",
42187         "id",
42188         "62"
42189       ],
42190       [
42191         "Iran (‫ایران‬‎)",
42192         "ir",
42193         "98"
42194       ],
42195       [
42196         "Iraq (‫العراق‬‎)",
42197         "iq",
42198         "964"
42199       ],
42200       [
42201         "Ireland",
42202         "ie",
42203         "353"
42204       ],
42205       [
42206         "Isle of Man",
42207         "im",
42208         "44",
42209         2
42210       ],
42211       [
42212         "Israel (‫ישראל‬‎)",
42213         "il",
42214         "972"
42215       ],
42216       [
42217         "Italy (Italia)",
42218         "it",
42219         "39",
42220         0
42221       ],
42222       [
42223         "Jamaica",
42224         "jm",
42225         "1876"
42226       ],
42227       [
42228         "Japan (日本)",
42229         "jp",
42230         "81"
42231       ],
42232       [
42233         "Jersey",
42234         "je",
42235         "44",
42236         3
42237       ],
42238       [
42239         "Jordan (‫الأردن‬‎)",
42240         "jo",
42241         "962"
42242       ],
42243       [
42244         "Kazakhstan (Казахстан)",
42245         "kz",
42246         "7",
42247         1
42248       ],
42249       [
42250         "Kenya",
42251         "ke",
42252         "254"
42253       ],
42254       [
42255         "Kiribati",
42256         "ki",
42257         "686"
42258       ],
42259       [
42260         "Kosovo",
42261         "xk",
42262         "383"
42263       ],
42264       [
42265         "Kuwait (‫الكويت‬‎)",
42266         "kw",
42267         "965"
42268       ],
42269       [
42270         "Kyrgyzstan (Кыргызстан)",
42271         "kg",
42272         "996"
42273       ],
42274       [
42275         "Laos (ລາວ)",
42276         "la",
42277         "856"
42278       ],
42279       [
42280         "Latvia (Latvija)",
42281         "lv",
42282         "371"
42283       ],
42284       [
42285         "Lebanon (‫لبنان‬‎)",
42286         "lb",
42287         "961"
42288       ],
42289       [
42290         "Lesotho",
42291         "ls",
42292         "266"
42293       ],
42294       [
42295         "Liberia",
42296         "lr",
42297         "231"
42298       ],
42299       [
42300         "Libya (‫ليبيا‬‎)",
42301         "ly",
42302         "218"
42303       ],
42304       [
42305         "Liechtenstein",
42306         "li",
42307         "423"
42308       ],
42309       [
42310         "Lithuania (Lietuva)",
42311         "lt",
42312         "370"
42313       ],
42314       [
42315         "Luxembourg",
42316         "lu",
42317         "352"
42318       ],
42319       [
42320         "Macau (澳門)",
42321         "mo",
42322         "853"
42323       ],
42324       [
42325         "Macedonia (FYROM) (Македонија)",
42326         "mk",
42327         "389"
42328       ],
42329       [
42330         "Madagascar (Madagasikara)",
42331         "mg",
42332         "261"
42333       ],
42334       [
42335         "Malawi",
42336         "mw",
42337         "265"
42338       ],
42339       [
42340         "Malaysia",
42341         "my",
42342         "60"
42343       ],
42344       [
42345         "Maldives",
42346         "mv",
42347         "960"
42348       ],
42349       [
42350         "Mali",
42351         "ml",
42352         "223"
42353       ],
42354       [
42355         "Malta",
42356         "mt",
42357         "356"
42358       ],
42359       [
42360         "Marshall Islands",
42361         "mh",
42362         "692"
42363       ],
42364       [
42365         "Martinique",
42366         "mq",
42367         "596"
42368       ],
42369       [
42370         "Mauritania (‫موريتانيا‬‎)",
42371         "mr",
42372         "222"
42373       ],
42374       [
42375         "Mauritius (Moris)",
42376         "mu",
42377         "230"
42378       ],
42379       [
42380         "Mayotte",
42381         "yt",
42382         "262",
42383         1
42384       ],
42385       [
42386         "Mexico (México)",
42387         "mx",
42388         "52"
42389       ],
42390       [
42391         "Micronesia",
42392         "fm",
42393         "691"
42394       ],
42395       [
42396         "Moldova (Republica Moldova)",
42397         "md",
42398         "373"
42399       ],
42400       [
42401         "Monaco",
42402         "mc",
42403         "377"
42404       ],
42405       [
42406         "Mongolia (Монгол)",
42407         "mn",
42408         "976"
42409       ],
42410       [
42411         "Montenegro (Crna Gora)",
42412         "me",
42413         "382"
42414       ],
42415       [
42416         "Montserrat",
42417         "ms",
42418         "1664"
42419       ],
42420       [
42421         "Morocco (‫المغرب‬‎)",
42422         "ma",
42423         "212",
42424         0
42425       ],
42426       [
42427         "Mozambique (Moçambique)",
42428         "mz",
42429         "258"
42430       ],
42431       [
42432         "Myanmar (Burma) (မြန်မာ)",
42433         "mm",
42434         "95"
42435       ],
42436       [
42437         "Namibia (Namibië)",
42438         "na",
42439         "264"
42440       ],
42441       [
42442         "Nauru",
42443         "nr",
42444         "674"
42445       ],
42446       [
42447         "Nepal (नेपाल)",
42448         "np",
42449         "977"
42450       ],
42451       [
42452         "Netherlands (Nederland)",
42453         "nl",
42454         "31"
42455       ],
42456       [
42457         "New Caledonia (Nouvelle-Calédonie)",
42458         "nc",
42459         "687"
42460       ],
42461       [
42462         "New Zealand",
42463         "nz",
42464         "64"
42465       ],
42466       [
42467         "Nicaragua",
42468         "ni",
42469         "505"
42470       ],
42471       [
42472         "Niger (Nijar)",
42473         "ne",
42474         "227"
42475       ],
42476       [
42477         "Nigeria",
42478         "ng",
42479         "234"
42480       ],
42481       [
42482         "Niue",
42483         "nu",
42484         "683"
42485       ],
42486       [
42487         "Norfolk Island",
42488         "nf",
42489         "672"
42490       ],
42491       [
42492         "North Korea (조선 민주주의 인민 공화국)",
42493         "kp",
42494         "850"
42495       ],
42496       [
42497         "Northern Mariana Islands",
42498         "mp",
42499         "1670"
42500       ],
42501       [
42502         "Norway (Norge)",
42503         "no",
42504         "47",
42505         0
42506       ],
42507       [
42508         "Oman (‫عُمان‬‎)",
42509         "om",
42510         "968"
42511       ],
42512       [
42513         "Pakistan (‫پاکستان‬‎)",
42514         "pk",
42515         "92"
42516       ],
42517       [
42518         "Palau",
42519         "pw",
42520         "680"
42521       ],
42522       [
42523         "Palestine (‫فلسطين‬‎)",
42524         "ps",
42525         "970"
42526       ],
42527       [
42528         "Panama (Panamá)",
42529         "pa",
42530         "507"
42531       ],
42532       [
42533         "Papua New Guinea",
42534         "pg",
42535         "675"
42536       ],
42537       [
42538         "Paraguay",
42539         "py",
42540         "595"
42541       ],
42542       [
42543         "Peru (Perú)",
42544         "pe",
42545         "51"
42546       ],
42547       [
42548         "Philippines",
42549         "ph",
42550         "63"
42551       ],
42552       [
42553         "Poland (Polska)",
42554         "pl",
42555         "48"
42556       ],
42557       [
42558         "Portugal",
42559         "pt",
42560         "351"
42561       ],
42562       [
42563         "Puerto Rico",
42564         "pr",
42565         "1",
42566         3,
42567         ["787", "939"]
42568       ],
42569       [
42570         "Qatar (‫قطر‬‎)",
42571         "qa",
42572         "974"
42573       ],
42574       [
42575         "Réunion (La Réunion)",
42576         "re",
42577         "262",
42578         0
42579       ],
42580       [
42581         "Romania (România)",
42582         "ro",
42583         "40"
42584       ],
42585       [
42586         "Russia (Россия)",
42587         "ru",
42588         "7",
42589         0
42590       ],
42591       [
42592         "Rwanda",
42593         "rw",
42594         "250"
42595       ],
42596       [
42597         "Saint Barthélemy",
42598         "bl",
42599         "590",
42600         1
42601       ],
42602       [
42603         "Saint Helena",
42604         "sh",
42605         "290"
42606       ],
42607       [
42608         "Saint Kitts and Nevis",
42609         "kn",
42610         "1869"
42611       ],
42612       [
42613         "Saint Lucia",
42614         "lc",
42615         "1758"
42616       ],
42617       [
42618         "Saint Martin (Saint-Martin (partie française))",
42619         "mf",
42620         "590",
42621         2
42622       ],
42623       [
42624         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42625         "pm",
42626         "508"
42627       ],
42628       [
42629         "Saint Vincent and the Grenadines",
42630         "vc",
42631         "1784"
42632       ],
42633       [
42634         "Samoa",
42635         "ws",
42636         "685"
42637       ],
42638       [
42639         "San Marino",
42640         "sm",
42641         "378"
42642       ],
42643       [
42644         "São Tomé and Príncipe (São Tomé e Príncipe)",
42645         "st",
42646         "239"
42647       ],
42648       [
42649         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42650         "sa",
42651         "966"
42652       ],
42653       [
42654         "Senegal (Sénégal)",
42655         "sn",
42656         "221"
42657       ],
42658       [
42659         "Serbia (Србија)",
42660         "rs",
42661         "381"
42662       ],
42663       [
42664         "Seychelles",
42665         "sc",
42666         "248"
42667       ],
42668       [
42669         "Sierra Leone",
42670         "sl",
42671         "232"
42672       ],
42673       [
42674         "Singapore",
42675         "sg",
42676         "65"
42677       ],
42678       [
42679         "Sint Maarten",
42680         "sx",
42681         "1721"
42682       ],
42683       [
42684         "Slovakia (Slovensko)",
42685         "sk",
42686         "421"
42687       ],
42688       [
42689         "Slovenia (Slovenija)",
42690         "si",
42691         "386"
42692       ],
42693       [
42694         "Solomon Islands",
42695         "sb",
42696         "677"
42697       ],
42698       [
42699         "Somalia (Soomaaliya)",
42700         "so",
42701         "252"
42702       ],
42703       [
42704         "South Africa",
42705         "za",
42706         "27"
42707       ],
42708       [
42709         "South Korea (대한민국)",
42710         "kr",
42711         "82"
42712       ],
42713       [
42714         "South Sudan (‫جنوب السودان‬‎)",
42715         "ss",
42716         "211"
42717       ],
42718       [
42719         "Spain (España)",
42720         "es",
42721         "34"
42722       ],
42723       [
42724         "Sri Lanka (ශ්‍රී ලංකාව)",
42725         "lk",
42726         "94"
42727       ],
42728       [
42729         "Sudan (‫السودان‬‎)",
42730         "sd",
42731         "249"
42732       ],
42733       [
42734         "Suriname",
42735         "sr",
42736         "597"
42737       ],
42738       [
42739         "Svalbard and Jan Mayen",
42740         "sj",
42741         "47",
42742         1
42743       ],
42744       [
42745         "Swaziland",
42746         "sz",
42747         "268"
42748       ],
42749       [
42750         "Sweden (Sverige)",
42751         "se",
42752         "46"
42753       ],
42754       [
42755         "Switzerland (Schweiz)",
42756         "ch",
42757         "41"
42758       ],
42759       [
42760         "Syria (‫سوريا‬‎)",
42761         "sy",
42762         "963"
42763       ],
42764       [
42765         "Taiwan (台灣)",
42766         "tw",
42767         "886"
42768       ],
42769       [
42770         "Tajikistan",
42771         "tj",
42772         "992"
42773       ],
42774       [
42775         "Tanzania",
42776         "tz",
42777         "255"
42778       ],
42779       [
42780         "Thailand (ไทย)",
42781         "th",
42782         "66"
42783       ],
42784       [
42785         "Timor-Leste",
42786         "tl",
42787         "670"
42788       ],
42789       [
42790         "Togo",
42791         "tg",
42792         "228"
42793       ],
42794       [
42795         "Tokelau",
42796         "tk",
42797         "690"
42798       ],
42799       [
42800         "Tonga",
42801         "to",
42802         "676"
42803       ],
42804       [
42805         "Trinidad and Tobago",
42806         "tt",
42807         "1868"
42808       ],
42809       [
42810         "Tunisia (‫تونس‬‎)",
42811         "tn",
42812         "216"
42813       ],
42814       [
42815         "Turkey (Türkiye)",
42816         "tr",
42817         "90"
42818       ],
42819       [
42820         "Turkmenistan",
42821         "tm",
42822         "993"
42823       ],
42824       [
42825         "Turks and Caicos Islands",
42826         "tc",
42827         "1649"
42828       ],
42829       [
42830         "Tuvalu",
42831         "tv",
42832         "688"
42833       ],
42834       [
42835         "U.S. Virgin Islands",
42836         "vi",
42837         "1340"
42838       ],
42839       [
42840         "Uganda",
42841         "ug",
42842         "256"
42843       ],
42844       [
42845         "Ukraine (Україна)",
42846         "ua",
42847         "380"
42848       ],
42849       [
42850         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42851         "ae",
42852         "971"
42853       ],
42854       [
42855         "United Kingdom",
42856         "gb",
42857         "44",
42858         0
42859       ],
42860       [
42861         "United States",
42862         "us",
42863         "1",
42864         0
42865       ],
42866       [
42867         "Uruguay",
42868         "uy",
42869         "598"
42870       ],
42871       [
42872         "Uzbekistan (Oʻzbekiston)",
42873         "uz",
42874         "998"
42875       ],
42876       [
42877         "Vanuatu",
42878         "vu",
42879         "678"
42880       ],
42881       [
42882         "Vatican City (Città del Vaticano)",
42883         "va",
42884         "39",
42885         1
42886       ],
42887       [
42888         "Venezuela",
42889         "ve",
42890         "58"
42891       ],
42892       [
42893         "Vietnam (Việt Nam)",
42894         "vn",
42895         "84"
42896       ],
42897       [
42898         "Wallis and Futuna (Wallis-et-Futuna)",
42899         "wf",
42900         "681"
42901       ],
42902       [
42903         "Western Sahara (‫الصحراء الغربية‬‎)",
42904         "eh",
42905         "212",
42906         1
42907       ],
42908       [
42909         "Yemen (‫اليمن‬‎)",
42910         "ye",
42911         "967"
42912       ],
42913       [
42914         "Zambia",
42915         "zm",
42916         "260"
42917       ],
42918       [
42919         "Zimbabwe",
42920         "zw",
42921         "263"
42922       ],
42923       [
42924         "Åland Islands",
42925         "ax",
42926         "358",
42927         1
42928       ]
42929   ];
42930   
42931   return d;
42932 }/**
42933 *    This script refer to:
42934 *    Title: International Telephone Input
42935 *    Author: Jack O'Connor
42936 *    Code version:  v12.1.12
42937 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42938 **/
42939
42940 /**
42941  * @class Roo.bootstrap.PhoneInput
42942  * @extends Roo.bootstrap.TriggerField
42943  * An input with International dial-code selection
42944  
42945  * @cfg {String} defaultDialCode default '+852'
42946  * @cfg {Array} preferedCountries default []
42947   
42948  * @constructor
42949  * Create a new PhoneInput.
42950  * @param {Object} config Configuration options
42951  */
42952
42953 Roo.bootstrap.PhoneInput = function(config) {
42954     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42955 };
42956
42957 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42958         
42959         listWidth: undefined,
42960         
42961         selectedClass: 'active',
42962         
42963         invalidClass : "has-warning",
42964         
42965         validClass: 'has-success',
42966         
42967         allowed: '0123456789',
42968         
42969         max_length: 15,
42970         
42971         /**
42972          * @cfg {String} defaultDialCode The default dial code when initializing the input
42973          */
42974         defaultDialCode: '+852',
42975         
42976         /**
42977          * @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
42978          */
42979         preferedCountries: false,
42980         
42981         getAutoCreate : function()
42982         {
42983             var data = Roo.bootstrap.PhoneInputData();
42984             var align = this.labelAlign || this.parentLabelAlign();
42985             var id = Roo.id();
42986             
42987             this.allCountries = [];
42988             this.dialCodeMapping = [];
42989             
42990             for (var i = 0; i < data.length; i++) {
42991               var c = data[i];
42992               this.allCountries[i] = {
42993                 name: c[0],
42994                 iso2: c[1],
42995                 dialCode: c[2],
42996                 priority: c[3] || 0,
42997                 areaCodes: c[4] || null
42998               };
42999               this.dialCodeMapping[c[2]] = {
43000                   name: c[0],
43001                   iso2: c[1],
43002                   priority: c[3] || 0,
43003                   areaCodes: c[4] || null
43004               };
43005             }
43006             
43007             var cfg = {
43008                 cls: 'form-group',
43009                 cn: []
43010             };
43011             
43012             var input =  {
43013                 tag: 'input',
43014                 id : id,
43015                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43016                 maxlength: this.max_length,
43017                 cls : 'form-control tel-input',
43018                 autocomplete: 'new-password'
43019             };
43020             
43021             var hiddenInput = {
43022                 tag: 'input',
43023                 type: 'hidden',
43024                 cls: 'hidden-tel-input'
43025             };
43026             
43027             if (this.name) {
43028                 hiddenInput.name = this.name;
43029             }
43030             
43031             if (this.disabled) {
43032                 input.disabled = true;
43033             }
43034             
43035             var flag_container = {
43036                 tag: 'div',
43037                 cls: 'flag-box',
43038                 cn: [
43039                     {
43040                         tag: 'div',
43041                         cls: 'flag'
43042                     },
43043                     {
43044                         tag: 'div',
43045                         cls: 'caret'
43046                     }
43047                 ]
43048             };
43049             
43050             var box = {
43051                 tag: 'div',
43052                 cls: this.hasFeedback ? 'has-feedback' : '',
43053                 cn: [
43054                     hiddenInput,
43055                     input,
43056                     {
43057                         tag: 'input',
43058                         cls: 'dial-code-holder',
43059                         disabled: true
43060                     }
43061                 ]
43062             };
43063             
43064             var container = {
43065                 cls: 'roo-select2-container input-group',
43066                 cn: [
43067                     flag_container,
43068                     box
43069                 ]
43070             };
43071             
43072             if (this.fieldLabel.length) {
43073                 var indicator = {
43074                     tag: 'i',
43075                     tooltip: 'This field is required'
43076                 };
43077                 
43078                 var label = {
43079                     tag: 'label',
43080                     'for':  id,
43081                     cls: 'control-label',
43082                     cn: []
43083                 };
43084                 
43085                 var label_text = {
43086                     tag: 'span',
43087                     html: this.fieldLabel
43088                 };
43089                 
43090                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43091                 label.cn = [
43092                     indicator,
43093                     label_text
43094                 ];
43095                 
43096                 if(this.indicatorpos == 'right') {
43097                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43098                     label.cn = [
43099                         label_text,
43100                         indicator
43101                     ];
43102                 }
43103                 
43104                 if(align == 'left') {
43105                     container = {
43106                         tag: 'div',
43107                         cn: [
43108                             container
43109                         ]
43110                     };
43111                     
43112                     if(this.labelWidth > 12){
43113                         label.style = "width: " + this.labelWidth + 'px';
43114                     }
43115                     if(this.labelWidth < 13 && this.labelmd == 0){
43116                         this.labelmd = this.labelWidth;
43117                     }
43118                     if(this.labellg > 0){
43119                         label.cls += ' col-lg-' + this.labellg;
43120                         input.cls += ' col-lg-' + (12 - this.labellg);
43121                     }
43122                     if(this.labelmd > 0){
43123                         label.cls += ' col-md-' + this.labelmd;
43124                         container.cls += ' col-md-' + (12 - this.labelmd);
43125                     }
43126                     if(this.labelsm > 0){
43127                         label.cls += ' col-sm-' + this.labelsm;
43128                         container.cls += ' col-sm-' + (12 - this.labelsm);
43129                     }
43130                     if(this.labelxs > 0){
43131                         label.cls += ' col-xs-' + this.labelxs;
43132                         container.cls += ' col-xs-' + (12 - this.labelxs);
43133                     }
43134                 }
43135             }
43136             
43137             cfg.cn = [
43138                 label,
43139                 container
43140             ];
43141             
43142             var settings = this;
43143             
43144             ['xs','sm','md','lg'].map(function(size){
43145                 if (settings[size]) {
43146                     cfg.cls += ' col-' + size + '-' + settings[size];
43147                 }
43148             });
43149             
43150             this.store = new Roo.data.Store({
43151                 proxy : new Roo.data.MemoryProxy({}),
43152                 reader : new Roo.data.JsonReader({
43153                     fields : [
43154                         {
43155                             'name' : 'name',
43156                             'type' : 'string'
43157                         },
43158                         {
43159                             'name' : 'iso2',
43160                             'type' : 'string'
43161                         },
43162                         {
43163                             'name' : 'dialCode',
43164                             'type' : 'string'
43165                         },
43166                         {
43167                             'name' : 'priority',
43168                             'type' : 'string'
43169                         },
43170                         {
43171                             'name' : 'areaCodes',
43172                             'type' : 'string'
43173                         }
43174                     ]
43175                 })
43176             });
43177             
43178             if(!this.preferedCountries) {
43179                 this.preferedCountries = [
43180                     'hk',
43181                     'gb',
43182                     'us'
43183                 ];
43184             }
43185             
43186             var p = this.preferedCountries.reverse();
43187             
43188             if(p) {
43189                 for (var i = 0; i < p.length; i++) {
43190                     for (var j = 0; j < this.allCountries.length; j++) {
43191                         if(this.allCountries[j].iso2 == p[i]) {
43192                             var t = this.allCountries[j];
43193                             this.allCountries.splice(j,1);
43194                             this.allCountries.unshift(t);
43195                         }
43196                     } 
43197                 }
43198             }
43199             
43200             this.store.proxy.data = {
43201                 success: true,
43202                 data: this.allCountries
43203             };
43204             
43205             return cfg;
43206         },
43207         
43208         initEvents : function()
43209         {
43210             this.createList();
43211             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43212             
43213             this.indicator = this.indicatorEl();
43214             this.flag = this.flagEl();
43215             this.dialCodeHolder = this.dialCodeHolderEl();
43216             
43217             this.trigger = this.el.select('div.flag-box',true).first();
43218             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43219             
43220             var _this = this;
43221             
43222             (function(){
43223                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43224                 _this.list.setWidth(lw);
43225             }).defer(100);
43226             
43227             this.list.on('mouseover', this.onViewOver, this);
43228             this.list.on('mousemove', this.onViewMove, this);
43229             this.inputEl().on("keyup", this.onKeyUp, this);
43230             this.inputEl().on("keypress", this.onKeyPress, this);
43231             
43232             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43233
43234             this.view = new Roo.View(this.list, this.tpl, {
43235                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43236             });
43237             
43238             this.view.on('click', this.onViewClick, this);
43239             this.setValue(this.defaultDialCode);
43240         },
43241         
43242         onTriggerClick : function(e)
43243         {
43244             Roo.log('trigger click');
43245             if(this.disabled){
43246                 return;
43247             }
43248             
43249             if(this.isExpanded()){
43250                 this.collapse();
43251                 this.hasFocus = false;
43252             }else {
43253                 this.store.load({});
43254                 this.hasFocus = true;
43255                 this.expand();
43256             }
43257         },
43258         
43259         isExpanded : function()
43260         {
43261             return this.list.isVisible();
43262         },
43263         
43264         collapse : function()
43265         {
43266             if(!this.isExpanded()){
43267                 return;
43268             }
43269             this.list.hide();
43270             Roo.get(document).un('mousedown', this.collapseIf, this);
43271             Roo.get(document).un('mousewheel', this.collapseIf, this);
43272             this.fireEvent('collapse', this);
43273             this.validate();
43274         },
43275         
43276         expand : function()
43277         {
43278             Roo.log('expand');
43279
43280             if(this.isExpanded() || !this.hasFocus){
43281                 return;
43282             }
43283             
43284             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43285             this.list.setWidth(lw);
43286             
43287             this.list.show();
43288             this.restrictHeight();
43289             
43290             Roo.get(document).on('mousedown', this.collapseIf, this);
43291             Roo.get(document).on('mousewheel', this.collapseIf, this);
43292             
43293             this.fireEvent('expand', this);
43294         },
43295         
43296         restrictHeight : function()
43297         {
43298             this.list.alignTo(this.inputEl(), this.listAlign);
43299             this.list.alignTo(this.inputEl(), this.listAlign);
43300         },
43301         
43302         onViewOver : function(e, t)
43303         {
43304             if(this.inKeyMode){
43305                 return;
43306             }
43307             var item = this.view.findItemFromChild(t);
43308             
43309             if(item){
43310                 var index = this.view.indexOf(item);
43311                 this.select(index, false);
43312             }
43313         },
43314
43315         // private
43316         onViewClick : function(view, doFocus, el, e)
43317         {
43318             var index = this.view.getSelectedIndexes()[0];
43319             
43320             var r = this.store.getAt(index);
43321             
43322             if(r){
43323                 this.onSelect(r, index);
43324             }
43325             if(doFocus !== false && !this.blockFocus){
43326                 this.inputEl().focus();
43327             }
43328         },
43329         
43330         onViewMove : function(e, t)
43331         {
43332             this.inKeyMode = false;
43333         },
43334         
43335         select : function(index, scrollIntoView)
43336         {
43337             this.selectedIndex = index;
43338             this.view.select(index);
43339             if(scrollIntoView !== false){
43340                 var el = this.view.getNode(index);
43341                 if(el){
43342                     this.list.scrollChildIntoView(el, false);
43343                 }
43344             }
43345         },
43346         
43347         createList : function()
43348         {
43349             this.list = Roo.get(document.body).createChild({
43350                 tag: 'ul',
43351                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43352                 style: 'display:none'
43353             });
43354             
43355             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43356         },
43357         
43358         collapseIf : function(e)
43359         {
43360             var in_combo  = e.within(this.el);
43361             var in_list =  e.within(this.list);
43362             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43363             
43364             if (in_combo || in_list || is_list) {
43365                 return;
43366             }
43367             this.collapse();
43368         },
43369         
43370         onSelect : function(record, index)
43371         {
43372             if(this.fireEvent('beforeselect', this, record, index) !== false){
43373                 
43374                 this.setFlagClass(record.data.iso2);
43375                 this.setDialCode(record.data.dialCode);
43376                 this.hasFocus = false;
43377                 this.collapse();
43378                 this.fireEvent('select', this, record, index);
43379             }
43380         },
43381         
43382         flagEl : function()
43383         {
43384             var flag = this.el.select('div.flag',true).first();
43385             if(!flag){
43386                 return false;
43387             }
43388             return flag;
43389         },
43390         
43391         dialCodeHolderEl : function()
43392         {
43393             var d = this.el.select('input.dial-code-holder',true).first();
43394             if(!d){
43395                 return false;
43396             }
43397             return d;
43398         },
43399         
43400         setDialCode : function(v)
43401         {
43402             this.dialCodeHolder.dom.value = '+'+v;
43403         },
43404         
43405         setFlagClass : function(n)
43406         {
43407             this.flag.dom.className = 'flag '+n;
43408         },
43409         
43410         getValue : function()
43411         {
43412             var v = this.inputEl().getValue();
43413             if(this.dialCodeHolder) {
43414                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43415             }
43416             return v;
43417         },
43418         
43419         setValue : function(v)
43420         {
43421             var d = this.getDialCode(v);
43422             
43423             //invalid dial code
43424             if(v.length == 0 || !d || d.length == 0) {
43425                 if(this.rendered){
43426                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43427                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43428                 }
43429                 return;
43430             }
43431             
43432             //valid dial code
43433             this.setFlagClass(this.dialCodeMapping[d].iso2);
43434             this.setDialCode(d);
43435             this.inputEl().dom.value = v.replace('+'+d,'');
43436             this.hiddenEl().dom.value = this.getValue();
43437             
43438             this.validate();
43439         },
43440         
43441         getDialCode : function(v)
43442         {
43443             v = v ||  '';
43444             
43445             if (v.length == 0) {
43446                 return this.dialCodeHolder.dom.value;
43447             }
43448             
43449             var dialCode = "";
43450             if (v.charAt(0) != "+") {
43451                 return false;
43452             }
43453             var numericChars = "";
43454             for (var i = 1; i < v.length; i++) {
43455               var c = v.charAt(i);
43456               if (!isNaN(c)) {
43457                 numericChars += c;
43458                 if (this.dialCodeMapping[numericChars]) {
43459                   dialCode = v.substr(1, i);
43460                 }
43461                 if (numericChars.length == 4) {
43462                   break;
43463                 }
43464               }
43465             }
43466             return dialCode;
43467         },
43468         
43469         reset : function()
43470         {
43471             this.setValue(this.defaultDialCode);
43472             this.validate();
43473         },
43474         
43475         hiddenEl : function()
43476         {
43477             return this.el.select('input.hidden-tel-input',true).first();
43478         },
43479         
43480         // after setting val
43481         onKeyUp : function(e){
43482             this.setValue(this.getValue());
43483         },
43484         
43485         onKeyPress : function(e){
43486             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43487                 e.stopEvent();
43488             }
43489         }
43490         
43491 });
43492 /**
43493  * @class Roo.bootstrap.MoneyField
43494  * @extends Roo.bootstrap.ComboBox
43495  * Bootstrap MoneyField class
43496  * 
43497  * @constructor
43498  * Create a new MoneyField.
43499  * @param {Object} config Configuration options
43500  */
43501
43502 Roo.bootstrap.MoneyField = function(config) {
43503     
43504     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43505     
43506 };
43507
43508 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43509     
43510     /**
43511      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43512      */
43513     allowDecimals : true,
43514     /**
43515      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43516      */
43517     decimalSeparator : ".",
43518     /**
43519      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43520      */
43521     decimalPrecision : 0,
43522     /**
43523      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43524      */
43525     allowNegative : true,
43526     /**
43527      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43528      */
43529     allowZero: true,
43530     /**
43531      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43532      */
43533     minValue : Number.NEGATIVE_INFINITY,
43534     /**
43535      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43536      */
43537     maxValue : Number.MAX_VALUE,
43538     /**
43539      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43540      */
43541     minText : "The minimum value for this field is {0}",
43542     /**
43543      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43544      */
43545     maxText : "The maximum value for this field is {0}",
43546     /**
43547      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43548      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43549      */
43550     nanText : "{0} is not a valid number",
43551     /**
43552      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43553      */
43554     castInt : true,
43555     /**
43556      * @cfg {String} defaults currency of the MoneyField
43557      * value should be in lkey
43558      */
43559     defaultCurrency : false,
43560     /**
43561      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43562      */
43563     thousandsDelimiter : false,
43564     /**
43565      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43566      */
43567     max_length: false,
43568     
43569     inputlg : 9,
43570     inputmd : 9,
43571     inputsm : 9,
43572     inputxs : 6,
43573     
43574     store : false,
43575     
43576     getAutoCreate : function()
43577     {
43578         var align = this.labelAlign || this.parentLabelAlign();
43579         
43580         var id = Roo.id();
43581
43582         var cfg = {
43583             cls: 'form-group',
43584             cn: []
43585         };
43586
43587         var input =  {
43588             tag: 'input',
43589             id : id,
43590             cls : 'form-control roo-money-amount-input',
43591             autocomplete: 'new-password'
43592         };
43593         
43594         var hiddenInput = {
43595             tag: 'input',
43596             type: 'hidden',
43597             id: Roo.id(),
43598             cls: 'hidden-number-input'
43599         };
43600         
43601         if(this.max_length) {
43602             input.maxlength = this.max_length; 
43603         }
43604         
43605         if (this.name) {
43606             hiddenInput.name = this.name;
43607         }
43608
43609         if (this.disabled) {
43610             input.disabled = true;
43611         }
43612
43613         var clg = 12 - this.inputlg;
43614         var cmd = 12 - this.inputmd;
43615         var csm = 12 - this.inputsm;
43616         var cxs = 12 - this.inputxs;
43617         
43618         var container = {
43619             tag : 'div',
43620             cls : 'row roo-money-field',
43621             cn : [
43622                 {
43623                     tag : 'div',
43624                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43625                     cn : [
43626                         {
43627                             tag : 'div',
43628                             cls: 'roo-select2-container input-group',
43629                             cn: [
43630                                 {
43631                                     tag : 'input',
43632                                     cls : 'form-control roo-money-currency-input',
43633                                     autocomplete: 'new-password',
43634                                     readOnly : 1,
43635                                     name : this.currencyName
43636                                 },
43637                                 {
43638                                     tag :'span',
43639                                     cls : 'input-group-addon',
43640                                     cn : [
43641                                         {
43642                                             tag: 'span',
43643                                             cls: 'caret'
43644                                         }
43645                                     ]
43646                                 }
43647                             ]
43648                         }
43649                     ]
43650                 },
43651                 {
43652                     tag : 'div',
43653                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43654                     cn : [
43655                         {
43656                             tag: 'div',
43657                             cls: this.hasFeedback ? 'has-feedback' : '',
43658                             cn: [
43659                                 input
43660                             ]
43661                         }
43662                     ]
43663                 }
43664             ]
43665             
43666         };
43667         
43668         if (this.fieldLabel.length) {
43669             var indicator = {
43670                 tag: 'i',
43671                 tooltip: 'This field is required'
43672             };
43673
43674             var label = {
43675                 tag: 'label',
43676                 'for':  id,
43677                 cls: 'control-label',
43678                 cn: []
43679             };
43680
43681             var label_text = {
43682                 tag: 'span',
43683                 html: this.fieldLabel
43684             };
43685
43686             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43687             label.cn = [
43688                 indicator,
43689                 label_text
43690             ];
43691
43692             if(this.indicatorpos == 'right') {
43693                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43694                 label.cn = [
43695                     label_text,
43696                     indicator
43697                 ];
43698             }
43699
43700             if(align == 'left') {
43701                 container = {
43702                     tag: 'div',
43703                     cn: [
43704                         container
43705                     ]
43706                 };
43707
43708                 if(this.labelWidth > 12){
43709                     label.style = "width: " + this.labelWidth + 'px';
43710                 }
43711                 if(this.labelWidth < 13 && this.labelmd == 0){
43712                     this.labelmd = this.labelWidth;
43713                 }
43714                 if(this.labellg > 0){
43715                     label.cls += ' col-lg-' + this.labellg;
43716                     input.cls += ' col-lg-' + (12 - this.labellg);
43717                 }
43718                 if(this.labelmd > 0){
43719                     label.cls += ' col-md-' + this.labelmd;
43720                     container.cls += ' col-md-' + (12 - this.labelmd);
43721                 }
43722                 if(this.labelsm > 0){
43723                     label.cls += ' col-sm-' + this.labelsm;
43724                     container.cls += ' col-sm-' + (12 - this.labelsm);
43725                 }
43726                 if(this.labelxs > 0){
43727                     label.cls += ' col-xs-' + this.labelxs;
43728                     container.cls += ' col-xs-' + (12 - this.labelxs);
43729                 }
43730             }
43731         }
43732
43733         cfg.cn = [
43734             label,
43735             container,
43736             hiddenInput
43737         ];
43738         
43739         var settings = this;
43740
43741         ['xs','sm','md','lg'].map(function(size){
43742             if (settings[size]) {
43743                 cfg.cls += ' col-' + size + '-' + settings[size];
43744             }
43745         });
43746         
43747         return cfg;
43748     },
43749     
43750     initEvents : function()
43751     {
43752         this.indicator = this.indicatorEl();
43753         
43754         this.initCurrencyEvent();
43755         
43756         this.initNumberEvent();
43757     },
43758     
43759     initCurrencyEvent : function()
43760     {
43761         if (!this.store) {
43762             throw "can not find store for combo";
43763         }
43764         
43765         this.store = Roo.factory(this.store, Roo.data);
43766         this.store.parent = this;
43767         
43768         this.createList();
43769         
43770         this.triggerEl = this.el.select('.input-group-addon', true).first();
43771         
43772         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43773         
43774         var _this = this;
43775         
43776         (function(){
43777             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43778             _this.list.setWidth(lw);
43779         }).defer(100);
43780         
43781         this.list.on('mouseover', this.onViewOver, this);
43782         this.list.on('mousemove', this.onViewMove, this);
43783         this.list.on('scroll', this.onViewScroll, this);
43784         
43785         if(!this.tpl){
43786             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43787         }
43788         
43789         this.view = new Roo.View(this.list, this.tpl, {
43790             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43791         });
43792         
43793         this.view.on('click', this.onViewClick, this);
43794         
43795         this.store.on('beforeload', this.onBeforeLoad, this);
43796         this.store.on('load', this.onLoad, this);
43797         this.store.on('loadexception', this.onLoadException, this);
43798         
43799         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43800             "up" : function(e){
43801                 this.inKeyMode = true;
43802                 this.selectPrev();
43803             },
43804
43805             "down" : function(e){
43806                 if(!this.isExpanded()){
43807                     this.onTriggerClick();
43808                 }else{
43809                     this.inKeyMode = true;
43810                     this.selectNext();
43811                 }
43812             },
43813
43814             "enter" : function(e){
43815                 this.collapse();
43816                 
43817                 if(this.fireEvent("specialkey", this, e)){
43818                     this.onViewClick(false);
43819                 }
43820                 
43821                 return true;
43822             },
43823
43824             "esc" : function(e){
43825                 this.collapse();
43826             },
43827
43828             "tab" : function(e){
43829                 this.collapse();
43830                 
43831                 if(this.fireEvent("specialkey", this, e)){
43832                     this.onViewClick(false);
43833                 }
43834                 
43835                 return true;
43836             },
43837
43838             scope : this,
43839
43840             doRelay : function(foo, bar, hname){
43841                 if(hname == 'down' || this.scope.isExpanded()){
43842                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43843                 }
43844                 return true;
43845             },
43846
43847             forceKeyDown: true
43848         });
43849         
43850         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43851         
43852     },
43853     
43854     initNumberEvent : function(e)
43855     {
43856         this.inputEl().on("keydown" , this.fireKey,  this);
43857         this.inputEl().on("focus", this.onFocus,  this);
43858         this.inputEl().on("blur", this.onBlur,  this);
43859         
43860         this.inputEl().relayEvent('keyup', this);
43861         
43862         if(this.indicator){
43863             this.indicator.addClass('invisible');
43864         }
43865  
43866         this.originalValue = this.getValue();
43867         
43868         if(this.validationEvent == 'keyup'){
43869             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43870             this.inputEl().on('keyup', this.filterValidation, this);
43871         }
43872         else if(this.validationEvent !== false){
43873             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43874         }
43875         
43876         if(this.selectOnFocus){
43877             this.on("focus", this.preFocus, this);
43878             
43879         }
43880         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43881             this.inputEl().on("keypress", this.filterKeys, this);
43882         } else {
43883             this.inputEl().relayEvent('keypress', this);
43884         }
43885         
43886         var allowed = "0123456789";
43887         
43888         if(this.allowDecimals){
43889             allowed += this.decimalSeparator;
43890         }
43891         
43892         if(this.allowNegative){
43893             allowed += "-";
43894         }
43895         
43896         if(this.thousandsDelimiter) {
43897             allowed += ",";
43898         }
43899         
43900         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43901         
43902         var keyPress = function(e){
43903             
43904             var k = e.getKey();
43905             
43906             var c = e.getCharCode();
43907             
43908             if(
43909                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43910                     allowed.indexOf(String.fromCharCode(c)) === -1
43911             ){
43912                 e.stopEvent();
43913                 return;
43914             }
43915             
43916             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43917                 return;
43918             }
43919             
43920             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43921                 e.stopEvent();
43922             }
43923         };
43924         
43925         this.inputEl().on("keypress", keyPress, this);
43926         
43927     },
43928     
43929     onTriggerClick : function(e)
43930     {   
43931         if(this.disabled){
43932             return;
43933         }
43934         
43935         this.page = 0;
43936         this.loadNext = false;
43937         
43938         if(this.isExpanded()){
43939             this.collapse();
43940             return;
43941         }
43942         
43943         this.hasFocus = true;
43944         
43945         if(this.triggerAction == 'all') {
43946             this.doQuery(this.allQuery, true);
43947             return;
43948         }
43949         
43950         this.doQuery(this.getRawValue());
43951     },
43952     
43953     getCurrency : function()
43954     {   
43955         var v = this.currencyEl().getValue();
43956         
43957         return v;
43958     },
43959     
43960     restrictHeight : function()
43961     {
43962         this.list.alignTo(this.currencyEl(), this.listAlign);
43963         this.list.alignTo(this.currencyEl(), this.listAlign);
43964     },
43965     
43966     onViewClick : function(view, doFocus, el, e)
43967     {
43968         var index = this.view.getSelectedIndexes()[0];
43969         
43970         var r = this.store.getAt(index);
43971         
43972         if(r){
43973             this.onSelect(r, index);
43974         }
43975     },
43976     
43977     onSelect : function(record, index){
43978         
43979         if(this.fireEvent('beforeselect', this, record, index) !== false){
43980         
43981             this.setFromCurrencyData(index > -1 ? record.data : false);
43982             
43983             this.collapse();
43984             
43985             this.fireEvent('select', this, record, index);
43986         }
43987     },
43988     
43989     setFromCurrencyData : function(o)
43990     {
43991         var currency = '';
43992         
43993         this.lastCurrency = o;
43994         
43995         if (this.currencyField) {
43996             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43997         } else {
43998             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43999         }
44000         
44001         this.lastSelectionText = currency;
44002         
44003         //setting default currency
44004         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44005             this.setCurrency(this.defaultCurrency);
44006             return;
44007         }
44008         
44009         this.setCurrency(currency);
44010     },
44011     
44012     setFromData : function(o)
44013     {
44014         var c = {};
44015         
44016         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44017         
44018         this.setFromCurrencyData(c);
44019         
44020         var value = '';
44021         
44022         if (this.name) {
44023             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44024         } else {
44025             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44026         }
44027         
44028         this.setValue(value);
44029         
44030     },
44031     
44032     setCurrency : function(v)
44033     {   
44034         this.currencyValue = v;
44035         
44036         if(this.rendered){
44037             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44038             this.validate();
44039         }
44040     },
44041     
44042     setValue : function(v)
44043     {
44044         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44045         
44046         this.value = v;
44047         
44048         if(this.rendered){
44049             
44050             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44051             
44052             this.inputEl().dom.value = (v == '') ? '' :
44053                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44054             
44055             if(!this.allowZero && v === '0') {
44056                 this.hiddenEl().dom.value = '';
44057                 this.inputEl().dom.value = '';
44058             }
44059             
44060             this.validate();
44061         }
44062     },
44063     
44064     getRawValue : function()
44065     {
44066         var v = this.inputEl().getValue();
44067         
44068         return v;
44069     },
44070     
44071     getValue : function()
44072     {
44073         return this.fixPrecision(this.parseValue(this.getRawValue()));
44074     },
44075     
44076     parseValue : function(value)
44077     {
44078         if(this.thousandsDelimiter) {
44079             value += "";
44080             r = new RegExp(",", "g");
44081             value = value.replace(r, "");
44082         }
44083         
44084         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44085         return isNaN(value) ? '' : value;
44086         
44087     },
44088     
44089     fixPrecision : function(value)
44090     {
44091         if(this.thousandsDelimiter) {
44092             value += "";
44093             r = new RegExp(",", "g");
44094             value = value.replace(r, "");
44095         }
44096         
44097         var nan = isNaN(value);
44098         
44099         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44100             return nan ? '' : value;
44101         }
44102         return parseFloat(value).toFixed(this.decimalPrecision);
44103     },
44104     
44105     decimalPrecisionFcn : function(v)
44106     {
44107         return Math.floor(v);
44108     },
44109     
44110     validateValue : function(value)
44111     {
44112         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44113             return false;
44114         }
44115         
44116         var num = this.parseValue(value);
44117         
44118         if(isNaN(num)){
44119             this.markInvalid(String.format(this.nanText, value));
44120             return false;
44121         }
44122         
44123         if(num < this.minValue){
44124             this.markInvalid(String.format(this.minText, this.minValue));
44125             return false;
44126         }
44127         
44128         if(num > this.maxValue){
44129             this.markInvalid(String.format(this.maxText, this.maxValue));
44130             return false;
44131         }
44132         
44133         return true;
44134     },
44135     
44136     validate : function()
44137     {
44138         if(this.disabled || this.allowBlank){
44139             this.markValid();
44140             return true;
44141         }
44142         
44143         var currency = this.getCurrency();
44144         
44145         if(this.validateValue(this.getRawValue()) && currency.length){
44146             this.markValid();
44147             return true;
44148         }
44149         
44150         this.markInvalid();
44151         return false;
44152     },
44153     
44154     getName: function()
44155     {
44156         return this.name;
44157     },
44158     
44159     beforeBlur : function()
44160     {
44161         if(!this.castInt){
44162             return;
44163         }
44164         
44165         var v = this.parseValue(this.getRawValue());
44166         
44167         if(v || v == 0){
44168             this.setValue(v);
44169         }
44170     },
44171     
44172     onBlur : function()
44173     {
44174         this.beforeBlur();
44175         
44176         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44177             //this.el.removeClass(this.focusClass);
44178         }
44179         
44180         this.hasFocus = false;
44181         
44182         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44183             this.validate();
44184         }
44185         
44186         var v = this.getValue();
44187         
44188         if(String(v) !== String(this.startValue)){
44189             this.fireEvent('change', this, v, this.startValue);
44190         }
44191         
44192         this.fireEvent("blur", this);
44193     },
44194     
44195     inputEl : function()
44196     {
44197         return this.el.select('.roo-money-amount-input', true).first();
44198     },
44199     
44200     currencyEl : function()
44201     {
44202         return this.el.select('.roo-money-currency-input', true).first();
44203     },
44204     
44205     hiddenEl : function()
44206     {
44207         return this.el.select('input.hidden-number-input',true).first();
44208     }
44209     
44210 });/**
44211  * @class Roo.bootstrap.BezierSignature
44212  * @extends Roo.bootstrap.Component
44213  * Bootstrap BezierSignature class
44214  * This script refer to:
44215  *    Title: Signature Pad
44216  *    Author: szimek
44217  *    Availability: https://github.com/szimek/signature_pad
44218  *
44219  * @constructor
44220  * Create a new BezierSignature
44221  * @param {Object} config The config object
44222  */
44223
44224 Roo.bootstrap.BezierSignature = function(config){
44225     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44226     this.addEvents({
44227         "resize" : true
44228     });
44229 };
44230
44231 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44232 {
44233      
44234     curve_data: [],
44235     
44236     is_empty: true,
44237     
44238     mouse_btn_down: true,
44239     
44240     /**
44241      * @cfg {int} canvas height
44242      */
44243     canvas_height: '200px',
44244     
44245     /**
44246      * @cfg {float|function} Radius of a single dot.
44247      */ 
44248     dot_size: false,
44249     
44250     /**
44251      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44252      */
44253     min_width: 0.5,
44254     
44255     /**
44256      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44257      */
44258     max_width: 2.5,
44259     
44260     /**
44261      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44262      */
44263     throttle: 16,
44264     
44265     /**
44266      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44267      */
44268     min_distance: 5,
44269     
44270     /**
44271      * @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.
44272      */
44273     bg_color: 'rgba(0, 0, 0, 0)',
44274     
44275     /**
44276      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44277      */
44278     dot_color: 'black',
44279     
44280     /**
44281      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44282      */ 
44283     velocity_filter_weight: 0.7,
44284     
44285     /**
44286      * @cfg {function} Callback when stroke begin. 
44287      */
44288     onBegin: false,
44289     
44290     /**
44291      * @cfg {function} Callback when stroke end.
44292      */
44293     onEnd: false,
44294     
44295     getAutoCreate : function()
44296     {
44297         var cls = 'roo-signature column';
44298         
44299         if(this.cls){
44300             cls += ' ' + this.cls;
44301         }
44302         
44303         var col_sizes = [
44304             'lg',
44305             'md',
44306             'sm',
44307             'xs'
44308         ];
44309         
44310         for(var i = 0; i < col_sizes.length; i++) {
44311             if(this[col_sizes[i]]) {
44312                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44313             }
44314         }
44315         
44316         var cfg = {
44317             tag: 'div',
44318             cls: cls,
44319             cn: [
44320                 {
44321                     tag: 'div',
44322                     cls: 'roo-signature-body',
44323                     cn: [
44324                         {
44325                             tag: 'canvas',
44326                             cls: 'roo-signature-body-canvas',
44327                             height: this.canvas_height,
44328                             width: this.canvas_width
44329                         }
44330                     ]
44331                 },
44332                 {
44333                     tag: 'input',
44334                     type: 'file',
44335                     style: 'display: none'
44336                 }
44337             ]
44338         };
44339         
44340         return cfg;
44341     },
44342     
44343     initEvents: function() 
44344     {
44345         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44346         
44347         var canvas = this.canvasEl();
44348         
44349         // mouse && touch event swapping...
44350         canvas.dom.style.touchAction = 'none';
44351         canvas.dom.style.msTouchAction = 'none';
44352         
44353         this.mouse_btn_down = false;
44354         canvas.on('mousedown', this._handleMouseDown, this);
44355         canvas.on('mousemove', this._handleMouseMove, this);
44356         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44357         
44358         if (window.PointerEvent) {
44359             canvas.on('pointerdown', this._handleMouseDown, this);
44360             canvas.on('pointermove', this._handleMouseMove, this);
44361             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44362         }
44363         
44364         if ('ontouchstart' in window) {
44365             canvas.on('touchstart', this._handleTouchStart, this);
44366             canvas.on('touchmove', this._handleTouchMove, this);
44367             canvas.on('touchend', this._handleTouchEnd, this);
44368         }
44369         
44370         Roo.EventManager.onWindowResize(this.resize, this, true);
44371         
44372         // file input event
44373         this.fileEl().on('change', this.uploadImage, this);
44374         
44375         this.clear();
44376         
44377         this.resize();
44378     },
44379     
44380     resize: function(){
44381         
44382         var canvas = this.canvasEl().dom;
44383         var ctx = this.canvasElCtx();
44384         var img_data = false;
44385         
44386         if(canvas.width > 0) {
44387             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44388         }
44389         // setting canvas width will clean img data
44390         canvas.width = 0;
44391         
44392         var style = window.getComputedStyle ? 
44393             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44394             
44395         var padding_left = parseInt(style.paddingLeft) || 0;
44396         var padding_right = parseInt(style.paddingRight) || 0;
44397         
44398         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44399         
44400         if(img_data) {
44401             ctx.putImageData(img_data, 0, 0);
44402         }
44403     },
44404     
44405     _handleMouseDown: function(e)
44406     {
44407         if (e.browserEvent.which === 1) {
44408             this.mouse_btn_down = true;
44409             this.strokeBegin(e);
44410         }
44411     },
44412     
44413     _handleMouseMove: function (e)
44414     {
44415         if (this.mouse_btn_down) {
44416             this.strokeMoveUpdate(e);
44417         }
44418     },
44419     
44420     _handleMouseUp: function (e)
44421     {
44422         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44423             this.mouse_btn_down = false;
44424             this.strokeEnd(e);
44425         }
44426     },
44427     
44428     _handleTouchStart: function (e) {
44429         
44430         e.preventDefault();
44431         if (e.browserEvent.targetTouches.length === 1) {
44432             // var touch = e.browserEvent.changedTouches[0];
44433             // this.strokeBegin(touch);
44434             
44435              this.strokeBegin(e); // assume e catching the correct xy...
44436         }
44437     },
44438     
44439     _handleTouchMove: function (e) {
44440         e.preventDefault();
44441         // var touch = event.targetTouches[0];
44442         // _this._strokeMoveUpdate(touch);
44443         this.strokeMoveUpdate(e);
44444     },
44445     
44446     _handleTouchEnd: function (e) {
44447         var wasCanvasTouched = e.target === this.canvasEl().dom;
44448         if (wasCanvasTouched) {
44449             e.preventDefault();
44450             // var touch = event.changedTouches[0];
44451             // _this._strokeEnd(touch);
44452             this.strokeEnd(e);
44453         }
44454     },
44455     
44456     reset: function () {
44457         this._lastPoints = [];
44458         this._lastVelocity = 0;
44459         this._lastWidth = (this.min_width + this.max_width) / 2;
44460         this.canvasElCtx().fillStyle = this.dot_color;
44461     },
44462     
44463     strokeMoveUpdate: function(e)
44464     {
44465         this.strokeUpdate(e);
44466         
44467         if (this.throttle) {
44468             this.throttleStroke(this.strokeUpdate, this.throttle);
44469         }
44470         else {
44471             this.strokeUpdate(e);
44472         }
44473     },
44474     
44475     strokeBegin: function(e)
44476     {
44477         var newPointGroup = {
44478             color: this.dot_color,
44479             points: []
44480         };
44481         
44482         if (typeof this.onBegin === 'function') {
44483             this.onBegin(e);
44484         }
44485         
44486         this.curve_data.push(newPointGroup);
44487         this.reset();
44488         this.strokeUpdate(e);
44489     },
44490     
44491     strokeUpdate: function(e)
44492     {
44493         var rect = this.canvasEl().dom.getBoundingClientRect();
44494         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44495         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44496         var lastPoints = lastPointGroup.points;
44497         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44498         var isLastPointTooClose = lastPoint
44499             ? point.distanceTo(lastPoint) <= this.min_distance
44500             : false;
44501         var color = lastPointGroup.color;
44502         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44503             var curve = this.addPoint(point);
44504             if (!lastPoint) {
44505                 this.drawDot({color: color, point: point});
44506             }
44507             else if (curve) {
44508                 this.drawCurve({color: color, curve: curve});
44509             }
44510             lastPoints.push({
44511                 time: point.time,
44512                 x: point.x,
44513                 y: point.y
44514             });
44515         }
44516     },
44517     
44518     strokeEnd: function(e)
44519     {
44520         this.strokeUpdate(e);
44521         if (typeof this.onEnd === 'function') {
44522             this.onEnd(e);
44523         }
44524     },
44525     
44526     addPoint:  function (point) {
44527         var _lastPoints = this._lastPoints;
44528         _lastPoints.push(point);
44529         if (_lastPoints.length > 2) {
44530             if (_lastPoints.length === 3) {
44531                 _lastPoints.unshift(_lastPoints[0]);
44532             }
44533             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44534             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44535             _lastPoints.shift();
44536             return curve;
44537         }
44538         return null;
44539     },
44540     
44541     calculateCurveWidths: function (startPoint, endPoint) {
44542         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44543             (1 - this.velocity_filter_weight) * this._lastVelocity;
44544
44545         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44546         var widths = {
44547             end: newWidth,
44548             start: this._lastWidth
44549         };
44550         
44551         this._lastVelocity = velocity;
44552         this._lastWidth = newWidth;
44553         return widths;
44554     },
44555     
44556     drawDot: function (_a) {
44557         var color = _a.color, point = _a.point;
44558         var ctx = this.canvasElCtx();
44559         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44560         ctx.beginPath();
44561         this.drawCurveSegment(point.x, point.y, width);
44562         ctx.closePath();
44563         ctx.fillStyle = color;
44564         ctx.fill();
44565     },
44566     
44567     drawCurve: function (_a) {
44568         var color = _a.color, curve = _a.curve;
44569         var ctx = this.canvasElCtx();
44570         var widthDelta = curve.endWidth - curve.startWidth;
44571         var drawSteps = Math.floor(curve.length()) * 2;
44572         ctx.beginPath();
44573         ctx.fillStyle = color;
44574         for (var i = 0; i < drawSteps; i += 1) {
44575         var t = i / drawSteps;
44576         var tt = t * t;
44577         var ttt = tt * t;
44578         var u = 1 - t;
44579         var uu = u * u;
44580         var uuu = uu * u;
44581         var x = uuu * curve.startPoint.x;
44582         x += 3 * uu * t * curve.control1.x;
44583         x += 3 * u * tt * curve.control2.x;
44584         x += ttt * curve.endPoint.x;
44585         var y = uuu * curve.startPoint.y;
44586         y += 3 * uu * t * curve.control1.y;
44587         y += 3 * u * tt * curve.control2.y;
44588         y += ttt * curve.endPoint.y;
44589         var width = curve.startWidth + ttt * widthDelta;
44590         this.drawCurveSegment(x, y, width);
44591         }
44592         ctx.closePath();
44593         ctx.fill();
44594     },
44595     
44596     drawCurveSegment: function (x, y, width) {
44597         var ctx = this.canvasElCtx();
44598         ctx.moveTo(x, y);
44599         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44600         this.is_empty = false;
44601     },
44602     
44603     clear: function()
44604     {
44605         var ctx = this.canvasElCtx();
44606         var canvas = this.canvasEl().dom;
44607         ctx.fillStyle = this.bg_color;
44608         ctx.clearRect(0, 0, canvas.width, canvas.height);
44609         ctx.fillRect(0, 0, canvas.width, canvas.height);
44610         this.curve_data = [];
44611         this.reset();
44612         this.is_empty = true;
44613     },
44614     
44615     fileEl: function()
44616     {
44617         return  this.el.select('input',true).first();
44618     },
44619     
44620     canvasEl: function()
44621     {
44622         return this.el.select('canvas',true).first();
44623     },
44624     
44625     canvasElCtx: function()
44626     {
44627         return this.el.select('canvas',true).first().dom.getContext('2d');
44628     },
44629     
44630     getImage: function(type)
44631     {
44632         if(this.is_empty) {
44633             return false;
44634         }
44635         
44636         // encryption ?
44637         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44638     },
44639     
44640     drawFromImage: function(img_src)
44641     {
44642         var img = new Image();
44643         
44644         img.onload = function(){
44645             this.canvasElCtx().drawImage(img, 0, 0);
44646         }.bind(this);
44647         
44648         img.src = img_src;
44649         
44650         this.is_empty = false;
44651     },
44652     
44653     selectImage: function()
44654     {
44655         this.fileEl().dom.click();
44656     },
44657     
44658     uploadImage: function(e)
44659     {
44660         var reader = new FileReader();
44661         
44662         reader.onload = function(e){
44663             var img = new Image();
44664             img.onload = function(){
44665                 this.reset();
44666                 this.canvasElCtx().drawImage(img, 0, 0);
44667             }.bind(this);
44668             img.src = e.target.result;
44669         }.bind(this);
44670         
44671         reader.readAsDataURL(e.target.files[0]);
44672     },
44673     
44674     // Bezier Point Constructor
44675     Point: (function () {
44676         function Point(x, y, time) {
44677             this.x = x;
44678             this.y = y;
44679             this.time = time || Date.now();
44680         }
44681         Point.prototype.distanceTo = function (start) {
44682             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44683         };
44684         Point.prototype.equals = function (other) {
44685             return this.x === other.x && this.y === other.y && this.time === other.time;
44686         };
44687         Point.prototype.velocityFrom = function (start) {
44688             return this.time !== start.time
44689             ? this.distanceTo(start) / (this.time - start.time)
44690             : 0;
44691         };
44692         return Point;
44693     }()),
44694     
44695     
44696     // Bezier Constructor
44697     Bezier: (function () {
44698         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44699             this.startPoint = startPoint;
44700             this.control2 = control2;
44701             this.control1 = control1;
44702             this.endPoint = endPoint;
44703             this.startWidth = startWidth;
44704             this.endWidth = endWidth;
44705         }
44706         Bezier.fromPoints = function (points, widths, scope) {
44707             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44708             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44709             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44710         };
44711         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44712             var dx1 = s1.x - s2.x;
44713             var dy1 = s1.y - s2.y;
44714             var dx2 = s2.x - s3.x;
44715             var dy2 = s2.y - s3.y;
44716             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44717             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44718             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44719             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44720             var dxm = m1.x - m2.x;
44721             var dym = m1.y - m2.y;
44722             var k = l2 / (l1 + l2);
44723             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44724             var tx = s2.x - cm.x;
44725             var ty = s2.y - cm.y;
44726             return {
44727                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44728                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44729             };
44730         };
44731         Bezier.prototype.length = function () {
44732             var steps = 10;
44733             var length = 0;
44734             var px;
44735             var py;
44736             for (var i = 0; i <= steps; i += 1) {
44737                 var t = i / steps;
44738                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44739                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44740                 if (i > 0) {
44741                     var xdiff = cx - px;
44742                     var ydiff = cy - py;
44743                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44744                 }
44745                 px = cx;
44746                 py = cy;
44747             }
44748             return length;
44749         };
44750         Bezier.prototype.point = function (t, start, c1, c2, end) {
44751             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44752             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44753             + (3.0 * c2 * (1.0 - t) * t * t)
44754             + (end * t * t * t);
44755         };
44756         return Bezier;
44757     }()),
44758     
44759     throttleStroke: function(fn, wait) {
44760       if (wait === void 0) { wait = 250; }
44761       var previous = 0;
44762       var timeout = null;
44763       var result;
44764       var storedContext;
44765       var storedArgs;
44766       var later = function () {
44767           previous = Date.now();
44768           timeout = null;
44769           result = fn.apply(storedContext, storedArgs);
44770           if (!timeout) {
44771               storedContext = null;
44772               storedArgs = [];
44773           }
44774       };
44775       return function wrapper() {
44776           var args = [];
44777           for (var _i = 0; _i < arguments.length; _i++) {
44778               args[_i] = arguments[_i];
44779           }
44780           var now = Date.now();
44781           var remaining = wait - (now - previous);
44782           storedContext = this;
44783           storedArgs = args;
44784           if (remaining <= 0 || remaining > wait) {
44785               if (timeout) {
44786                   clearTimeout(timeout);
44787                   timeout = null;
44788               }
44789               previous = now;
44790               result = fn.apply(storedContext, storedArgs);
44791               if (!timeout) {
44792                   storedContext = null;
44793                   storedArgs = [];
44794               }
44795           }
44796           else if (!timeout) {
44797               timeout = window.setTimeout(later, remaining);
44798           }
44799           return result;
44800       };
44801   }
44802   
44803 });
44804
44805  
44806
44807