fix popover issue
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
366     {
367         return Roo.get(document.body);
368     },
369     
370     /**
371      * Fetch the element to display the tooltip on.
372      * @return {Roo.Element} defaults to this.el
373      */
374     tooltipEl : function()
375     {
376         return this.el;
377     },
378         
379     addxtype  : function(tree,cntr)
380     {
381         var cn = this;
382         
383         cn = Roo.factory(tree);
384         //Roo.log(['addxtype', cn]);
385            
386         cn.parentType = this.xtype; //??
387         cn.parentId = this.id;
388         
389         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390         if (typeof(cn.container_method) == 'string') {
391             cntr = cn.container_method;
392         }
393         
394         
395         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
396         
397         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
398         
399         var build_from_html =  Roo.XComponent.build_from_html;
400           
401         var is_body  = (tree.xtype == 'Body') ;
402           
403         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
404           
405         var self_cntr_el = Roo.get(this[cntr](false));
406         
407         // do not try and build conditional elements 
408         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
409             return false;
410         }
411         
412         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414                 return this.addxtypeChild(tree,cntr, is_body);
415             }
416             
417             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
418                 
419             if(echild){
420                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
421             }
422             
423             Roo.log('skipping render');
424             return cn;
425             
426         }
427         
428         var ret = false;
429         if (!build_from_html) {
430             return false;
431         }
432         
433         // this i think handles overlaying multiple children of the same type
434         // with the sam eelement.. - which might be buggy..
435         while (true) {
436             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437             
438             if (!echild) {
439                 break;
440             }
441             
442             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
443                 break;
444             }
445             
446             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
447         }
448        
449         return ret;
450     },
451     
452     
453     addxtypeChild : function (tree, cntr, is_body)
454     {
455         Roo.debug && Roo.log('addxtypeChild:' + cntr);
456         var cn = this;
457         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
458         
459         
460         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461                     (typeof(tree['flexy:foreach']) != 'undefined');
462           
463     
464         
465         skip_children = false;
466         // render the element if it's not BODY.
467         if (!is_body) {
468             
469             // if parent was disabled, then do not try and create the children..
470             if(!this[cntr](true)){
471                 tree.items = [];
472                 return tree;
473             }
474            
475             cn = Roo.factory(tree);
476            
477             cn.parentType = this.xtype; //??
478             cn.parentId = this.id;
479             
480             var build_from_html =  Roo.XComponent.build_from_html;
481             
482             
483             // does the container contain child eleemnts with 'xtype' attributes.
484             // that match this xtype..
485             // note - when we render we create these as well..
486             // so we should check to see if body has xtype set.
487             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
488                
489                 var self_cntr_el = Roo.get(this[cntr](false));
490                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
491                 if (echild) { 
492                     //Roo.log(Roo.XComponent.build_from_html);
493                     //Roo.log("got echild:");
494                     //Roo.log(echild);
495                 }
496                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497                 // and are not displayed -this causes this to use up the wrong element when matching.
498                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
499                 
500                 
501                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503                   
504                   
505                   
506                     cn.el = echild;
507                   //  Roo.log("GOT");
508                     //echild.dom.removeAttribute('xtype');
509                 } else {
510                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511                     Roo.debug && Roo.log(self_cntr_el);
512                     Roo.debug && Roo.log(echild);
513                     Roo.debug && Roo.log(cn);
514                 }
515             }
516            
517             
518            
519             // if object has flexy:if - then it may or may not be rendered.
520             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
521                 // skip a flexy if element.
522                 Roo.debug && Roo.log('skipping render');
523                 Roo.debug && Roo.log(tree);
524                 if (!cn.el) {
525                     Roo.debug && Roo.log('skipping all children');
526                     skip_children = true;
527                 }
528                 
529              } else {
530                  
531                 // actually if flexy:foreach is found, we really want to create 
532                 // multiple copies here...
533                 //Roo.log('render');
534                 //Roo.log(this[cntr]());
535                 // some elements do not have render methods.. like the layouts...
536                 /*
537                 if(this[cntr](true) === false){
538                     cn.items = [];
539                     return cn;
540                 }
541                 */
542                 cn.render && cn.render(this[cntr](true));
543                 
544              }
545             // then add the element..
546         }
547          
548         // handle the kids..
549         
550         var nitems = [];
551         /*
552         if (typeof (tree.menu) != 'undefined') {
553             tree.menu.parentType = cn.xtype;
554             tree.menu.triggerEl = cn.el;
555             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
556             
557         }
558         */
559         if (!tree.items || !tree.items.length) {
560             cn.items = nitems;
561             //Roo.log(["no children", this]);
562             
563             return cn;
564         }
565          
566         var items = tree.items;
567         delete tree.items;
568         
569         //Roo.log(items.length);
570             // add the items..
571         if (!skip_children) {    
572             for(var i =0;i < items.length;i++) {
573               //  Roo.log(['add child', items[i]]);
574                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575             }
576         }
577         
578         cn.items = nitems;
579         
580         //Roo.log("fire childrenrendered");
581         
582         cn.fireEvent('childrenrendered', this);
583         
584         return cn;
585     },
586     
587     /**
588      * Set the element that will be used to show or hide
589      */
590     setVisibilityEl : function(el)
591     {
592         this.visibilityEl = el;
593     },
594     
595      /**
596      * Get the element that will be used to show or hide
597      */
598     getVisibilityEl : function()
599     {
600         if (typeof(this.visibilityEl) == 'object') {
601             return this.visibilityEl;
602         }
603         
604         if (typeof(this.visibilityEl) == 'string') {
605             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
606         }
607         
608         return this.getEl();
609     },
610     
611     /**
612      * Show a component - removes 'hidden' class
613      */
614     show : function()
615     {
616         if(!this.getVisibilityEl()){
617             return;
618         }
619          
620         this.getVisibilityEl().removeClass(['hidden','d-none']);
621         
622         this.fireEvent('show', this);
623         
624         
625     },
626     /**
627      * Hide a component - adds 'hidden' class
628      */
629     hide: function()
630     {
631         if(!this.getVisibilityEl()){
632             return;
633         }
634         
635         this.getVisibilityEl().addClass(['hidden','d-none']);
636         
637         this.fireEvent('hide', this);
638         
639     }
640 });
641
642  /*
643  * - LGPL
644  *
645  * element
646  * 
647  */
648
649 /**
650  * @class Roo.bootstrap.Element
651  * @extends Roo.bootstrap.Component
652  * Bootstrap Element class
653  * @cfg {String} html contents of the element
654  * @cfg {String} tag tag of the element
655  * @cfg {String} cls class of the element
656  * @cfg {Boolean} preventDefault (true|false) default false
657  * @cfg {Boolean} clickable (true|false) default false
658  * @cfg {String} role default blank - set to button to force cursor pointer
659  
660  * 
661  * @constructor
662  * Create a new Element
663  * @param {Object} config The config object
664  */
665
666 Roo.bootstrap.Element = function(config){
667     Roo.bootstrap.Element.superclass.constructor.call(this, config);
668     
669     this.addEvents({
670         // raw events
671         /**
672          * @event click
673          * When a element is chick
674          * @param {Roo.bootstrap.Element} this
675          * @param {Roo.EventObject} e
676          */
677         "click" : true 
678         
679       
680     });
681 };
682
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
684     
685     tag: 'div',
686     cls: '',
687     html: '',
688     preventDefault: false, 
689     clickable: false,
690     tapedTwice : false,
691     role : false,
692     
693     getAutoCreate : function(){
694         
695         var cfg = {
696             tag: this.tag,
697             // cls: this.cls, double assign in parent class Component.js :: onRender
698             html: this.html
699         };
700         if (this.role !== false) {
701             cfg.role = this.role;
702         }
703         
704         return cfg;
705     },
706     
707     initEvents: function() 
708     {
709         Roo.bootstrap.Element.superclass.initEvents.call(this);
710         
711         if(this.clickable){
712             this.el.on('click', this.onClick, this);
713         }
714         
715         
716     },
717     
718     onClick : function(e)
719     {
720         if(this.preventDefault){
721             e.preventDefault();
722         }
723         
724         this.fireEvent('click', this, e); // why was this double click before?
725     },
726     
727     
728     
729
730     
731     
732     getValue : function()
733     {
734         return this.el.dom.innerHTML;
735     },
736     
737     setValue : function(value)
738     {
739         this.el.dom.innerHTML = value;
740     }
741    
742 });
743
744  
745
746  /*
747  * - LGPL
748  *
749  * dropable area
750  * 
751  */
752
753 /**
754  * @class Roo.bootstrap.DropTarget
755  * @extends Roo.bootstrap.Element
756  * Bootstrap DropTarget class
757  
758  * @cfg {string} name dropable name
759  * 
760  * @constructor
761  * Create a new Dropable Area
762  * @param {Object} config The config object
763  */
764
765 Roo.bootstrap.DropTarget = function(config){
766     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
767     
768     this.addEvents({
769         // raw events
770         /**
771          * @event click
772          * When a element is chick
773          * @param {Roo.bootstrap.Element} this
774          * @param {Roo.EventObject} e
775          */
776         "drop" : true
777     });
778 };
779
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
781     
782     
783     getAutoCreate : function(){
784         
785          
786     },
787     
788     initEvents: function() 
789     {
790         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
792             ddGroup: this.name,
793             listeners : {
794                 drop : this.dragDrop.createDelegate(this),
795                 enter : this.dragEnter.createDelegate(this),
796                 out : this.dragOut.createDelegate(this),
797                 over : this.dragOver.createDelegate(this)
798             }
799             
800         });
801         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
802     },
803     
804     dragDrop : function(source,e,data)
805     {
806         // user has to decide how to impliment this.
807         Roo.log('drop');
808         Roo.log(this);
809         //this.fireEvent('drop', this, source, e ,data);
810         return false;
811     },
812     
813     dragEnter : function(n, dd, e, data)
814     {
815         // probably want to resize the element to match the dropped element..
816         Roo.log("enter");
817         this.originalSize = this.el.getSize();
818         this.el.setSize( n.el.getSize());
819         this.dropZone.DDM.refreshCache(this.name);
820         Roo.log([n, dd, e, data]);
821     },
822     
823     dragOut : function(value)
824     {
825         // resize back to normal
826         Roo.log("out");
827         this.el.setSize(this.originalSize);
828         this.dropZone.resetConstraints();
829     },
830     
831     dragOver : function()
832     {
833         // ??? do nothing?
834     }
835    
836 });
837
838  
839
840  /*
841  * - LGPL
842  *
843  * Body
844  *
845  */
846
847 /**
848  * @class Roo.bootstrap.Body
849  * @extends Roo.bootstrap.Component
850  * Bootstrap Body class
851  *
852  * @constructor
853  * Create a new body
854  * @param {Object} config The config object
855  */
856
857 Roo.bootstrap.Body = function(config){
858
859     config = config || {};
860
861     Roo.bootstrap.Body.superclass.constructor.call(this, config);
862     this.el = Roo.get(config.el ? config.el : document.body );
863     if (this.cls && this.cls.length) {
864         Roo.get(document.body).addClass(this.cls);
865     }
866 };
867
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
869
870     is_body : true,// just to make sure it's constructed?
871
872         autoCreate : {
873         cls: 'container'
874     },
875     onRender : function(ct, position)
876     {
877        /* Roo.log("Roo.bootstrap.Body - onRender");
878         if (this.cls && this.cls.length) {
879             Roo.get(document.body).addClass(this.cls);
880         }
881         // style??? xttr???
882         */
883     }
884
885
886
887
888 });
889 /*
890  * - LGPL
891  *
892  * button group
893  * 
894  */
895
896
897 /**
898  * @class Roo.bootstrap.ButtonGroup
899  * @extends Roo.bootstrap.Component
900  * Bootstrap ButtonGroup class
901  * @cfg {String} size lg | sm | xs (default empty normal)
902  * @cfg {String} align vertical | justified  (default none)
903  * @cfg {String} direction up | down (default down)
904  * @cfg {Boolean} toolbar false | true
905  * @cfg {Boolean} btn true | false
906  * 
907  * 
908  * @constructor
909  * Create a new Input
910  * @param {Object} config The config object
911  */
912
913 Roo.bootstrap.ButtonGroup = function(config){
914     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
915 };
916
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
918     
919     size: '',
920     align: '',
921     direction: '',
922     toolbar: false,
923     btn: true,
924
925     getAutoCreate : function(){
926         var cfg = {
927             cls: 'btn-group',
928             html : null
929         };
930         
931         cfg.html = this.html || cfg.html;
932         
933         if (this.toolbar) {
934             cfg = {
935                 cls: 'btn-toolbar',
936                 html: null
937             };
938             
939             return cfg;
940         }
941         
942         if (['vertical','justified'].indexOf(this.align)!==-1) {
943             cfg.cls = 'btn-group-' + this.align;
944             
945             if (this.align == 'justified') {
946                 console.log(this.items);
947             }
948         }
949         
950         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951             cfg.cls += ' btn-group-' + this.size;
952         }
953         
954         if (this.direction == 'up') {
955             cfg.cls += ' dropup' ;
956         }
957         
958         return cfg;
959     },
960     /**
961      * Add a button to the group (similar to NavItem API.)
962      */
963     addItem : function(cfg)
964     {
965         var cn = new Roo.bootstrap.Button(cfg);
966         //this.register(cn);
967         cn.parentId = this.id;
968         cn.onRender(this.el, null);
969         return cn;
970     }
971    
972 });
973
974  /*
975  * - LGPL
976  *
977  * button
978  * 
979  */
980
981 /**
982  * @class Roo.bootstrap.Button
983  * @extends Roo.bootstrap.Component
984  * Bootstrap Button class
985  * @cfg {String} html The button content
986  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989  * @cfg {String} size (lg|sm|xs)
990  * @cfg {String} tag (a|input|submit)
991  * @cfg {String} href empty or href
992  * @cfg {Boolean} disabled default false;
993  * @cfg {Boolean} isClose default false;
994  * @cfg {String} glyphicon depricated - use fa
995  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996  * @cfg {String} badge text for badge
997  * @cfg {String} theme (default|glow)  
998  * @cfg {Boolean} inverse dark themed version
999  * @cfg {Boolean} toggle is it a slidy toggle button
1000  * @cfg {Boolean} pressed   default null - if the button ahs active state
1001  * @cfg {String} ontext text for on slidy toggle state
1002  * @cfg {String} offtext text for off slidy toggle state
1003  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1004  * @cfg {Boolean} removeClass remove the standard class..
1005  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1006  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1007  * 
1008  * @constructor
1009  * Create a new button
1010  * @param {Object} config The config object
1011  */
1012
1013
1014 Roo.bootstrap.Button = function(config){
1015     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1016     
1017     this.addEvents({
1018         // raw events
1019         /**
1020          * @event click
1021          * When a button is pressed
1022          * @param {Roo.bootstrap.Button} btn
1023          * @param {Roo.EventObject} e
1024          */
1025         "click" : true,
1026         /**
1027          * @event dblclick
1028          * When a button is double clicked
1029          * @param {Roo.bootstrap.Button} btn
1030          * @param {Roo.EventObject} e
1031          */
1032         "dblclick" : true,
1033          /**
1034          * @event toggle
1035          * After the button has been toggles
1036          * @param {Roo.bootstrap.Button} btn
1037          * @param {Roo.EventObject} e
1038          * @param {boolean} pressed (also available as button.pressed)
1039          */
1040         "toggle" : true
1041     });
1042 };
1043
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1045     html: false,
1046     active: false,
1047     weight: '',
1048     badge_weight: '',
1049     outline : false,
1050     size: '',
1051     tag: 'button',
1052     href: '',
1053     disabled: false,
1054     isClose: false,
1055     glyphicon: '',
1056     fa: '',
1057     badge: '',
1058     theme: 'default',
1059     inverse: false,
1060     
1061     toggle: false,
1062     ontext: 'ON',
1063     offtext: 'OFF',
1064     defaulton: true,
1065     preventDefault: true,
1066     removeClass: false,
1067     name: false,
1068     target: false,
1069     group : false,
1070      
1071     pressed : null,
1072      
1073     
1074     getAutoCreate : function(){
1075         
1076         var cfg = {
1077             tag : 'button',
1078             cls : 'roo-button',
1079             html: ''
1080         };
1081         
1082         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084             this.tag = 'button';
1085         } else {
1086             cfg.tag = this.tag;
1087         }
1088         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1089         
1090         if (this.toggle == true) {
1091             cfg={
1092                 tag: 'div',
1093                 cls: 'slider-frame roo-button',
1094                 cn: [
1095                     {
1096                         tag: 'span',
1097                         'data-on-text':'ON',
1098                         'data-off-text':'OFF',
1099                         cls: 'slider-button',
1100                         html: this.offtext
1101                     }
1102                 ]
1103             };
1104             // why are we validating the weights?
1105             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106                 cfg.cls +=  ' ' + this.weight;
1107             }
1108             
1109             return cfg;
1110         }
1111         
1112         if (this.isClose) {
1113             cfg.cls += ' close';
1114             
1115             cfg["aria-hidden"] = true;
1116             
1117             cfg.html = "&times;";
1118             
1119             return cfg;
1120         }
1121              
1122         
1123         if (this.theme==='default') {
1124             cfg.cls = 'btn roo-button';
1125             
1126             //if (this.parentType != 'Navbar') {
1127             this.weight = this.weight.length ?  this.weight : 'default';
1128             //}
1129             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1130                 
1131                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133                 cfg.cls += ' btn-' + outline + weight;
1134                 if (this.weight == 'default') {
1135                     // BC
1136                     cfg.cls += ' btn-' + this.weight;
1137                 }
1138             }
1139         } else if (this.theme==='glow') {
1140             
1141             cfg.tag = 'a';
1142             cfg.cls = 'btn-glow roo-button';
1143             
1144             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1145                 
1146                 cfg.cls += ' ' + this.weight;
1147             }
1148         }
1149    
1150         
1151         if (this.inverse) {
1152             this.cls += ' inverse';
1153         }
1154         
1155         
1156         if (this.active || this.pressed === true) {
1157             cfg.cls += ' active';
1158         }
1159         
1160         if (this.disabled) {
1161             cfg.disabled = 'disabled';
1162         }
1163         
1164         if (this.items) {
1165             Roo.log('changing to ul' );
1166             cfg.tag = 'ul';
1167             this.glyphicon = 'caret';
1168             if (Roo.bootstrap.version == 4) {
1169                 this.fa = 'caret-down';
1170             }
1171             
1172         }
1173         
1174         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1175          
1176         //gsRoo.log(this.parentType);
1177         if (this.parentType === 'Navbar' && !this.parent().bar) {
1178             Roo.log('changing to li?');
1179             
1180             cfg.tag = 'li';
1181             
1182             cfg.cls = '';
1183             cfg.cn =  [{
1184                 tag : 'a',
1185                 cls : 'roo-button',
1186                 html : this.html,
1187                 href : this.href || '#'
1188             }];
1189             if (this.menu) {
1190                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1191                 cfg.cls += ' dropdown';
1192             }   
1193             
1194             delete cfg.html;
1195             
1196         }
1197         
1198        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1199         
1200         if (this.glyphicon) {
1201             cfg.html = ' ' + cfg.html;
1202             
1203             cfg.cn = [
1204                 {
1205                     tag: 'span',
1206                     cls: 'glyphicon glyphicon-' + this.glyphicon
1207                 }
1208             ];
1209         }
1210         if (this.fa) {
1211             cfg.html = ' ' + cfg.html;
1212             
1213             cfg.cn = [
1214                 {
1215                     tag: 'i',
1216                     cls: 'fa fas fa-' + this.fa
1217                 }
1218             ];
1219         }
1220         
1221         if (this.badge) {
1222             cfg.html += ' ';
1223             
1224             cfg.tag = 'a';
1225             
1226 //            cfg.cls='btn roo-button';
1227             
1228             cfg.href=this.href;
1229             
1230             var value = cfg.html;
1231             
1232             if(this.glyphicon){
1233                 value = {
1234                     tag: 'span',
1235                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1236                     html: this.html
1237                 };
1238             }
1239             if(this.fa){
1240                 value = {
1241                     tag: 'i',
1242                     cls: 'fa fas fa-' + this.fa,
1243                     html: this.html
1244                 };
1245             }
1246             
1247             var bw = this.badge_weight.length ? this.badge_weight :
1248                 (this.weight.length ? this.weight : 'secondary');
1249             bw = bw == 'default' ? 'secondary' : bw;
1250             
1251             cfg.cn = [
1252                 value,
1253                 {
1254                     tag: 'span',
1255                     cls: 'badge badge-' + bw,
1256                     html: this.badge
1257                 }
1258             ];
1259             
1260             cfg.html='';
1261         }
1262         
1263         if (this.menu) {
1264             cfg.cls += ' dropdown';
1265             cfg.html = typeof(cfg.html) != 'undefined' ?
1266                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1267         }
1268         
1269         if (cfg.tag !== 'a' && this.href !== '') {
1270             throw "Tag must be a to set href.";
1271         } else if (this.href.length > 0) {
1272             cfg.href = this.href;
1273         }
1274         
1275         if(this.removeClass){
1276             cfg.cls = '';
1277         }
1278         
1279         if(this.target){
1280             cfg.target = this.target;
1281         }
1282         
1283         return cfg;
1284     },
1285     initEvents: function() {
1286        // Roo.log('init events?');
1287 //        Roo.log(this.el.dom);
1288         // add the menu...
1289         
1290         if (typeof (this.menu) != 'undefined') {
1291             this.menu.parentType = this.xtype;
1292             this.menu.triggerEl = this.el;
1293             this.addxtype(Roo.apply({}, this.menu));
1294         }
1295
1296
1297         if (this.el.hasClass('roo-button')) {
1298              this.el.on('click', this.onClick, this);
1299              this.el.on('dblclick', this.onDblClick, this);
1300         } else {
1301              this.el.select('.roo-button').on('click', this.onClick, this);
1302              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1303              
1304         }
1305         // why?
1306         if(this.removeClass){
1307             this.el.on('click', this.onClick, this);
1308         }
1309         
1310         if (this.group === true) {
1311              if (this.pressed === false || this.pressed === true) {
1312                 // nothing
1313             } else {
1314                 this.pressed = false;
1315                 this.setActive(this.pressed);
1316             }
1317             
1318         }
1319         
1320         this.el.enableDisplayMode();
1321         
1322     },
1323     onClick : function(e)
1324     {
1325         if (this.disabled) {
1326             return;
1327         }
1328         
1329         Roo.log('button on click ');
1330         if(this.preventDefault){
1331             e.preventDefault();
1332         }
1333         
1334         if (this.group) {
1335             if (this.pressed) {
1336                 // do nothing -
1337                 return;
1338             }
1339             this.setActive(true);
1340             var pi = this.parent().items;
1341             for (var i = 0;i < pi.length;i++) {
1342                 if (this == pi[i]) {
1343                     continue;
1344                 }
1345                 if (pi[i].el.hasClass('roo-button')) {
1346                     pi[i].setActive(false);
1347                 }
1348             }
1349             this.fireEvent('click', this, e);            
1350             return;
1351         }
1352         
1353         if (this.pressed === true || this.pressed === false) {
1354             this.toggleActive(e);
1355         }
1356         
1357         
1358         this.fireEvent('click', this, e);
1359     },
1360     onDblClick: function(e)
1361     {
1362         if (this.disabled) {
1363             return;
1364         }
1365         if(this.preventDefault){
1366             e.preventDefault();
1367         }
1368         this.fireEvent('dblclick', this, e);
1369     },
1370     /**
1371      * Enables this button
1372      */
1373     enable : function()
1374     {
1375         this.disabled = false;
1376         this.el.removeClass('disabled');
1377         this.el.dom.removeAttribute("disabled");
1378     },
1379     
1380     /**
1381      * Disable this button
1382      */
1383     disable : function()
1384     {
1385         this.disabled = true;
1386         this.el.addClass('disabled');
1387         this.el.attr("disabled", "disabled")
1388     },
1389      /**
1390      * sets the active state on/off, 
1391      * @param {Boolean} state (optional) Force a particular state
1392      */
1393     setActive : function(v) {
1394         
1395         this.el[v ? 'addClass' : 'removeClass']('active');
1396         this.pressed = v;
1397     },
1398      /**
1399      * toggles the current active state 
1400      */
1401     toggleActive : function(e)
1402     {
1403         this.setActive(!this.pressed); // this modifies pressed...
1404         this.fireEvent('toggle', this, e, this.pressed);
1405     },
1406      /**
1407      * get the current active state
1408      * @return {boolean} true if it's active
1409      */
1410     isActive : function()
1411     {
1412         return this.el.hasClass('active');
1413     },
1414     /**
1415      * set the text of the first selected button
1416      */
1417     setText : function(str)
1418     {
1419         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1420     },
1421     /**
1422      * get the text of the first selected button
1423      */
1424     getText : function()
1425     {
1426         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1427     },
1428     
1429     setWeight : function(str)
1430     {
1431         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1433         this.weight = str;
1434         var outline = this.outline ? 'outline-' : '';
1435         if (str == 'default') {
1436             this.el.addClass('btn-default btn-outline-secondary');        
1437             return;
1438         }
1439         this.el.addClass('btn-' + outline + str);        
1440     }
1441     
1442     
1443 });
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1445
1446 Roo.bootstrap.Button.weights = [
1447     'default',
1448     'secondary' ,
1449     'primary',
1450     'success',
1451     'info',
1452     'warning',
1453     'danger',
1454     'link',
1455     'light',
1456     'dark'              
1457    
1458 ];/*
1459  * - LGPL
1460  *
1461  * column
1462  * 
1463  */
1464
1465 /**
1466  * @class Roo.bootstrap.Column
1467  * @extends Roo.bootstrap.Component
1468  * Bootstrap Column class
1469  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1477  *
1478  * 
1479  * @cfg {Boolean} hidden (true|false) hide the element
1480  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481  * @cfg {String} fa (ban|check|...) font awesome icon
1482  * @cfg {Number} fasize (1|2|....) font awsome size
1483
1484  * @cfg {String} icon (info-sign|check|...) glyphicon name
1485
1486  * @cfg {String} html content of column.
1487  * 
1488  * @constructor
1489  * Create a new Column
1490  * @param {Object} config The config object
1491  */
1492
1493 Roo.bootstrap.Column = function(config){
1494     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1495 };
1496
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1498     
1499     xs: false,
1500     sm: false,
1501     md: false,
1502     lg: false,
1503     xsoff: false,
1504     smoff: false,
1505     mdoff: false,
1506     lgoff: false,
1507     html: '',
1508     offset: 0,
1509     alert: false,
1510     fa: false,
1511     icon : false,
1512     hidden : false,
1513     fasize : 1,
1514     
1515     getAutoCreate : function(){
1516         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1517         
1518         cfg = {
1519             tag: 'div',
1520             cls: 'column'
1521         };
1522         
1523         var settings=this;
1524         var sizes =   ['xs','sm','md','lg'];
1525         sizes.map(function(size ,ix){
1526             //Roo.log( size + ':' + settings[size]);
1527             
1528             if (settings[size+'off'] !== false) {
1529                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1530             }
1531             
1532             if (settings[size] === false) {
1533                 return;
1534             }
1535             
1536             if (!settings[size]) { // 0 = hidden
1537                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1538                 // bootsrap4
1539                 for (var i = ix; i > -1; i--) {
1540                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1541                 }
1542                 
1543                 
1544                 return;
1545             }
1546             cfg.cls += ' col-' + size + '-' + settings[size] + (
1547                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1548             );
1549             
1550         });
1551         
1552         if (this.hidden) {
1553             cfg.cls += ' hidden';
1554         }
1555         
1556         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557             cfg.cls +=' alert alert-' + this.alert;
1558         }
1559         
1560         
1561         if (this.html.length) {
1562             cfg.html = this.html;
1563         }
1564         if (this.fa) {
1565             var fasize = '';
1566             if (this.fasize > 1) {
1567                 fasize = ' fa-' + this.fasize + 'x';
1568             }
1569             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1570             
1571             
1572         }
1573         if (this.icon) {
1574             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1575         }
1576         
1577         return cfg;
1578     }
1579    
1580 });
1581
1582  
1583
1584  /*
1585  * - LGPL
1586  *
1587  * page container.
1588  * 
1589  */
1590
1591
1592 /**
1593  * @class Roo.bootstrap.Container
1594  * @extends Roo.bootstrap.Component
1595  * Bootstrap Container class
1596  * @cfg {Boolean} jumbotron is it a jumbotron element
1597  * @cfg {String} html content of element
1598  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1600  * @cfg {String} header content of header (for panel)
1601  * @cfg {String} footer content of footer (for panel)
1602  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603  * @cfg {String} tag (header|aside|section) type of HTML tag.
1604  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605  * @cfg {String} fa font awesome icon
1606  * @cfg {String} icon (info-sign|check|...) glyphicon name
1607  * @cfg {Boolean} hidden (true|false) hide the element
1608  * @cfg {Boolean} expandable (true|false) default false
1609  * @cfg {Boolean} expanded (true|false) default true
1610  * @cfg {String} rheader contet on the right of header
1611  * @cfg {Boolean} clickable (true|false) default false
1612
1613  *     
1614  * @constructor
1615  * Create a new Container
1616  * @param {Object} config The config object
1617  */
1618
1619 Roo.bootstrap.Container = function(config){
1620     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1621     
1622     this.addEvents({
1623         // raw events
1624          /**
1625          * @event expand
1626          * After the panel has been expand
1627          * 
1628          * @param {Roo.bootstrap.Container} this
1629          */
1630         "expand" : true,
1631         /**
1632          * @event collapse
1633          * After the panel has been collapsed
1634          * 
1635          * @param {Roo.bootstrap.Container} this
1636          */
1637         "collapse" : true,
1638         /**
1639          * @event click
1640          * When a element is chick
1641          * @param {Roo.bootstrap.Container} this
1642          * @param {Roo.EventObject} e
1643          */
1644         "click" : true
1645     });
1646 };
1647
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1649     
1650     jumbotron : false,
1651     well: '',
1652     panel : '',
1653     header: '',
1654     footer : '',
1655     sticky: '',
1656     tag : false,
1657     alert : false,
1658     fa: false,
1659     icon : false,
1660     expandable : false,
1661     rheader : '',
1662     expanded : true,
1663     clickable: false,
1664   
1665      
1666     getChildContainer : function() {
1667         
1668         if(!this.el){
1669             return false;
1670         }
1671         
1672         if (this.panel.length) {
1673             return this.el.select('.panel-body',true).first();
1674         }
1675         
1676         return this.el;
1677     },
1678     
1679     
1680     getAutoCreate : function(){
1681         
1682         var cfg = {
1683             tag : this.tag || 'div',
1684             html : '',
1685             cls : ''
1686         };
1687         if (this.jumbotron) {
1688             cfg.cls = 'jumbotron';
1689         }
1690         
1691         
1692         
1693         // - this is applied by the parent..
1694         //if (this.cls) {
1695         //    cfg.cls = this.cls + '';
1696         //}
1697         
1698         if (this.sticky.length) {
1699             
1700             var bd = Roo.get(document.body);
1701             if (!bd.hasClass('bootstrap-sticky')) {
1702                 bd.addClass('bootstrap-sticky');
1703                 Roo.select('html',true).setStyle('height', '100%');
1704             }
1705              
1706             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1707         }
1708         
1709         
1710         if (this.well.length) {
1711             switch (this.well) {
1712                 case 'lg':
1713                 case 'sm':
1714                     cfg.cls +=' well well-' +this.well;
1715                     break;
1716                 default:
1717                     cfg.cls +=' well';
1718                     break;
1719             }
1720         }
1721         
1722         if (this.hidden) {
1723             cfg.cls += ' hidden';
1724         }
1725         
1726         
1727         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728             cfg.cls +=' alert alert-' + this.alert;
1729         }
1730         
1731         var body = cfg;
1732         
1733         if (this.panel.length) {
1734             cfg.cls += ' panel panel-' + this.panel;
1735             cfg.cn = [];
1736             if (this.header.length) {
1737                 
1738                 var h = [];
1739                 
1740                 if(this.expandable){
1741                     
1742                     cfg.cls = cfg.cls + ' expandable';
1743                     
1744                     h.push({
1745                         tag: 'i',
1746                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1747                     });
1748                     
1749                 }
1750                 
1751                 h.push(
1752                     {
1753                         tag: 'span',
1754                         cls : 'panel-title',
1755                         html : (this.expandable ? '&nbsp;' : '') + this.header
1756                     },
1757                     {
1758                         tag: 'span',
1759                         cls: 'panel-header-right',
1760                         html: this.rheader
1761                     }
1762                 );
1763                 
1764                 cfg.cn.push({
1765                     cls : 'panel-heading',
1766                     style : this.expandable ? 'cursor: pointer' : '',
1767                     cn : h
1768                 });
1769                 
1770             }
1771             
1772             body = false;
1773             cfg.cn.push({
1774                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1775                 html : this.html
1776             });
1777             
1778             
1779             if (this.footer.length) {
1780                 cfg.cn.push({
1781                     cls : 'panel-footer',
1782                     html : this.footer
1783                     
1784                 });
1785             }
1786             
1787         }
1788         
1789         if (body) {
1790             body.html = this.html || cfg.html;
1791             // prefix with the icons..
1792             if (this.fa) {
1793                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1794             }
1795             if (this.icon) {
1796                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1797             }
1798             
1799             
1800         }
1801         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802             cfg.cls =  'container';
1803         }
1804         
1805         return cfg;
1806     },
1807     
1808     initEvents: function() 
1809     {
1810         if(this.expandable){
1811             var headerEl = this.headerEl();
1812         
1813             if(headerEl){
1814                 headerEl.on('click', this.onToggleClick, this);
1815             }
1816         }
1817         
1818         if(this.clickable){
1819             this.el.on('click', this.onClick, this);
1820         }
1821         
1822     },
1823     
1824     onToggleClick : function()
1825     {
1826         var headerEl = this.headerEl();
1827         
1828         if(!headerEl){
1829             return;
1830         }
1831         
1832         if(this.expanded){
1833             this.collapse();
1834             return;
1835         }
1836         
1837         this.expand();
1838     },
1839     
1840     expand : function()
1841     {
1842         if(this.fireEvent('expand', this)) {
1843             
1844             this.expanded = true;
1845             
1846             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1847             
1848             this.el.select('.panel-body',true).first().removeClass('hide');
1849             
1850             var toggleEl = this.toggleEl();
1851
1852             if(!toggleEl){
1853                 return;
1854             }
1855
1856             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1857         }
1858         
1859     },
1860     
1861     collapse : function()
1862     {
1863         if(this.fireEvent('collapse', this)) {
1864             
1865             this.expanded = false;
1866             
1867             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868             this.el.select('.panel-body',true).first().addClass('hide');
1869         
1870             var toggleEl = this.toggleEl();
1871
1872             if(!toggleEl){
1873                 return;
1874             }
1875
1876             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1877         }
1878     },
1879     
1880     toggleEl : function()
1881     {
1882         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1883             return;
1884         }
1885         
1886         return this.el.select('.panel-heading .fa',true).first();
1887     },
1888     
1889     headerEl : function()
1890     {
1891         if(!this.el || !this.panel.length || !this.header.length){
1892             return;
1893         }
1894         
1895         return this.el.select('.panel-heading',true).first()
1896     },
1897     
1898     bodyEl : function()
1899     {
1900         if(!this.el || !this.panel.length){
1901             return;
1902         }
1903         
1904         return this.el.select('.panel-body',true).first()
1905     },
1906     
1907     titleEl : function()
1908     {
1909         if(!this.el || !this.panel.length || !this.header.length){
1910             return;
1911         }
1912         
1913         return this.el.select('.panel-title',true).first();
1914     },
1915     
1916     setTitle : function(v)
1917     {
1918         var titleEl = this.titleEl();
1919         
1920         if(!titleEl){
1921             return;
1922         }
1923         
1924         titleEl.dom.innerHTML = v;
1925     },
1926     
1927     getTitle : function()
1928     {
1929         
1930         var titleEl = this.titleEl();
1931         
1932         if(!titleEl){
1933             return '';
1934         }
1935         
1936         return titleEl.dom.innerHTML;
1937     },
1938     
1939     setRightTitle : function(v)
1940     {
1941         var t = this.el.select('.panel-header-right',true).first();
1942         
1943         if(!t){
1944             return;
1945         }
1946         
1947         t.dom.innerHTML = v;
1948     },
1949     
1950     onClick : function(e)
1951     {
1952         e.preventDefault();
1953         
1954         this.fireEvent('click', this, e);
1955     }
1956 });
1957
1958  /*
1959  *  - LGPL
1960  *
1961  *  This is BS4's Card element.. - similar to our containers probably..
1962  * 
1963  */
1964 /**
1965  * @class Roo.bootstrap.Card
1966  * @extends Roo.bootstrap.Component
1967  * Bootstrap Card class
1968  *
1969  *
1970  * possible... may not be implemented..
1971  * @cfg {String} header_image  src url of image.
1972  * @cfg {String|Object} header
1973  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1975  * 
1976  * @cfg {String} title
1977  * @cfg {String} subtitle
1978  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979  * @cfg {String} footer
1980  
1981  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1982  * 
1983  * @cfg {String} margin (0|1|2|3|4|5|auto)
1984  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1990  *
1991  * @cfg {String} padding (0|1|2|3|4|5)
1992  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994  * @cfg {String} padding_left (0|1|2|3|4|5)
1995  * @cfg {String} padding_right (0|1|2|3|4|5)
1996  * @cfg {String} padding_x (0|1|2|3|4|5)
1997  * @cfg {String} padding_y (0|1|2|3|4|5)
1998  *
1999  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004  
2005  * @config {Boolean} dragable  if this card can be dragged.
2006  * @config {String} drag_group  group for drag
2007  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2008  * @config {String} drop_group  group for drag
2009  * 
2010  * @config {Boolean} collapsable can the body be collapsed.
2011  * @config {Boolean} collapsed is the body collapsed when rendered...
2012  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013  * @config {Boolean} rotated is the body rotated when rendered...
2014  * 
2015  * @constructor
2016  * Create a new Container
2017  * @param {Object} config The config object
2018  */
2019
2020 Roo.bootstrap.Card = function(config){
2021     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2022     
2023     this.addEvents({
2024          // raw events
2025         /**
2026          * @event drop
2027          * When a element a card is dropped
2028          * @param {Roo.bootstrap.Card} this
2029          *
2030          * 
2031          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032          * @param {String} position 'above' or 'below'
2033          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2034         
2035          */
2036         'drop' : true,
2037          /**
2038          * @event rotate
2039          * When a element a card is rotate
2040          * @param {Roo.bootstrap.Card} this
2041          * @param {Roo.Element} n the node being dropped?
2042          * @param {Boolean} rotate status
2043          */
2044         'rotate' : true,
2045         /**
2046          * @event cardover
2047          * When a card element is dragged over ready to drop (return false to block dropable)
2048          * @param {Roo.bootstrap.Card} this
2049          * @param {Object} data from dragdrop 
2050          */
2051          'cardover' : true
2052          
2053     });
2054 };
2055
2056
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2058     
2059     
2060     weight : '',
2061     
2062     margin: '', /// may be better in component?
2063     margin_top: '', 
2064     margin_bottom: '', 
2065     margin_left: '',
2066     margin_right: '',
2067     margin_x: '',
2068     margin_y: '',
2069     
2070     padding : '',
2071     padding_top: '', 
2072     padding_bottom: '', 
2073     padding_left: '',
2074     padding_right: '',
2075     padding_x: '',
2076     padding_y: '',
2077     
2078     display: '', 
2079     display_xs: '', 
2080     display_sm: '', 
2081     display_lg: '',
2082     display_xl: '',
2083  
2084     header_image  : '',
2085     header : '',
2086     header_size : 0,
2087     title : '',
2088     subtitle : '',
2089     html : '',
2090     footer: '',
2091
2092     collapsable : false,
2093     collapsed : false,
2094     rotateable : false,
2095     rotated : false,
2096     
2097     dragable : false,
2098     drag_group : false,
2099     dropable : false,
2100     drop_group : false,
2101     childContainer : false,
2102     dropEl : false, /// the dom placeholde element that indicates drop location.
2103     containerEl: false, // body container
2104     bodyEl: false, // card-body
2105     headerContainerEl : false, //
2106     headerEl : false,
2107     header_imageEl : false,
2108     
2109     
2110     layoutCls : function()
2111     {
2112         var cls = '';
2113         var t = this;
2114         Roo.log(this.margin_bottom.length);
2115         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2117             
2118             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2120             }
2121             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2123             }
2124         });
2125         
2126         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2129             }
2130         });
2131         
2132         // more generic support?
2133         if (this.hidden) {
2134             cls += ' d-none';
2135         }
2136         
2137         return cls;
2138     },
2139  
2140        // Roo.log("Call onRender: " + this.xtype);
2141         /*  We are looking at something like this.
2142 <div class="card">
2143     <img src="..." class="card-img-top" alt="...">
2144     <div class="card-body">
2145         <h5 class="card-title">Card title</h5>
2146          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2147
2148         >> this bit is really the body...
2149         <div> << we will ad dthis in hopefully it will not break shit.
2150         
2151         ** card text does not actually have any styling...
2152         
2153             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2154         
2155         </div> <<
2156           <a href="#" class="card-link">Card link</a>
2157           
2158     </div>
2159     <div class="card-footer">
2160         <small class="text-muted">Last updated 3 mins ago</small>
2161     </div>
2162 </div>
2163          */
2164     getAutoCreate : function(){
2165         
2166         var cfg = {
2167             tag : 'div',
2168             cls : 'card',
2169             cn : [ ]
2170         };
2171         
2172         if (this.weight.length && this.weight != 'light') {
2173             cfg.cls += ' text-white';
2174         } else {
2175             cfg.cls += ' text-dark'; // need as it's nested..
2176         }
2177         if (this.weight.length) {
2178             cfg.cls += ' bg-' + this.weight;
2179         }
2180         
2181         cfg.cls += ' ' + this.layoutCls(); 
2182         
2183         var hdr = false;
2184         var hdr_ctr = false;
2185         if (this.header.length) {
2186             hdr = {
2187                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2189                 cn : []
2190             };
2191             cfg.cn.push(hdr);
2192             hdr_ctr = hdr;
2193         } else {
2194             hdr = {
2195                 tag : 'div',
2196                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2197                 cn : []
2198             };
2199             cfg.cn.push(hdr);
2200             hdr_ctr = hdr;
2201         }
2202         if (this.collapsable) {
2203             hdr_ctr = {
2204             tag : 'a',
2205             cls : 'd-block user-select-none',
2206             cn: [
2207                     {
2208                         tag: 'i',
2209                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2210                     }
2211                    
2212                 ]
2213             };
2214             hdr.cn.push(hdr_ctr);
2215         }
2216         
2217         hdr_ctr.cn.push(        {
2218             tag: 'span',
2219             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2220             html : this.header
2221         });
2222         
2223         
2224         if (this.header_image.length) {
2225             cfg.cn.push({
2226                 tag : 'img',
2227                 cls : 'card-img-top',
2228                 src: this.header_image // escape?
2229             });
2230         } else {
2231             cfg.cn.push({
2232                     tag : 'div',
2233                     cls : 'card-img-top d-none' 
2234                 });
2235         }
2236             
2237         var body = {
2238             tag : 'div',
2239             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2240             cn : []
2241         };
2242         var obody = body;
2243         if (this.collapsable || this.rotateable) {
2244             obody = {
2245                 tag: 'div',
2246                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2247                 cn : [  body ]
2248             };
2249         }
2250         
2251         cfg.cn.push(obody);
2252         
2253         if (this.title.length) {
2254             body.cn.push({
2255                 tag : 'div',
2256                 cls : 'card-title',
2257                 src: this.title // escape?
2258             });
2259         }  
2260         
2261         if (this.subtitle.length) {
2262             body.cn.push({
2263                 tag : 'div',
2264                 cls : 'card-title',
2265                 src: this.subtitle // escape?
2266             });
2267         }
2268         
2269         body.cn.push({
2270             tag : 'div',
2271             cls : 'roo-card-body-ctr'
2272         });
2273         
2274         if (this.html.length) {
2275             body.cn.push({
2276                 tag: 'div',
2277                 html : this.html
2278             });
2279         }
2280         // fixme ? handle objects?
2281         
2282         if (this.footer.length) {
2283            
2284             cfg.cn.push({
2285                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2286                 html : this.footer
2287             });
2288             
2289         } else {
2290             cfg.cn.push({cls : 'card-footer d-none'});
2291         }
2292         
2293         // footer...
2294         
2295         return cfg;
2296     },
2297     
2298     
2299     getCardHeader : function()
2300     {
2301         var  ret = this.el.select('.card-header',true).first();
2302         if (ret.hasClass('d-none')) {
2303             ret.removeClass('d-none');
2304         }
2305         
2306         return ret;
2307     },
2308     getCardFooter : function()
2309     {
2310         var  ret = this.el.select('.card-footer',true).first();
2311         if (ret.hasClass('d-none')) {
2312             ret.removeClass('d-none');
2313         }
2314         
2315         return ret;
2316     },
2317     getCardImageTop : function()
2318     {
2319         var  ret = this.header_imageEl;
2320         if (ret.hasClass('d-none')) {
2321             ret.removeClass('d-none');
2322         }
2323             
2324         return ret;
2325     },
2326     
2327     getChildContainer : function()
2328     {
2329         
2330         if(!this.el){
2331             return false;
2332         }
2333         return this.el.select('.roo-card-body-ctr',true).first();    
2334     },
2335     
2336     initEvents: function() 
2337     {
2338         this.bodyEl = this.el.select('.card-body',true).first(); 
2339         this.containerEl = this.getChildContainer();
2340         if(this.dragable){
2341             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342                     containerScroll: true,
2343                     ddGroup: this.drag_group || 'default_card_drag_group'
2344             });
2345             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2346         }
2347         if (this.dropable) {
2348             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349                 containerScroll: true,
2350                 ddGroup: this.drop_group || 'default_card_drag_group'
2351             });
2352             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2357         }
2358         
2359         if (this.collapsable) {
2360             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2361         }
2362         if (this.rotateable) {
2363             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2364         }
2365         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2366          
2367         this.footerEl = this.el.select('.card-footer',true).first();
2368         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370         this.headerEl = this.el.select('.card-header',true).first();
2371         
2372         if (this.rotated) {
2373             this.el.addClass('roo-card-rotated');
2374             this.fireEvent('rotate', this, true);
2375         }
2376         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2377         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2378         
2379     },
2380     getDragData : function(e)
2381     {
2382         var target = this.getEl();
2383         if (target) {
2384             //this.handleSelection(e);
2385             
2386             var dragData = {
2387                 source: this,
2388                 copy: false,
2389                 nodes: this.getEl(),
2390                 records: []
2391             };
2392             
2393             
2394             dragData.ddel = target.dom ;    // the div element
2395             Roo.log(target.getWidth( ));
2396             dragData.ddel.style.width = target.getWidth() + 'px';
2397             
2398             return dragData;
2399         }
2400         return false;
2401     },
2402     /**
2403     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2404     *    whole Element becomes the target, and this causes the drop gesture to append.
2405     *
2406     *    Returns an object:
2407     *     {
2408            
2409            position : 'below' or 'above'
2410            card  : relateive to card OBJECT (or true for no cards listed)
2411            items_n : relative to nth item in list
2412            card_n : relative to  nth card in list
2413     }
2414     *
2415     *    
2416     */
2417     getTargetFromEvent : function(e, dragged_card_el)
2418     {
2419         var target = e.getTarget();
2420         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421             target = target.parentNode;
2422         }
2423         
2424         var ret = {
2425             position: '',
2426             cards : [],
2427             card_n : -1,
2428             items_n : -1,
2429             card : false 
2430         };
2431         
2432         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433         // see if target is one of the 'cards'...
2434         
2435         
2436         //Roo.log(this.items.length);
2437         var pos = false;
2438         
2439         var last_card_n = 0;
2440         var cards_len  = 0;
2441         for (var i = 0;i< this.items.length;i++) {
2442             
2443             if (!this.items[i].el.hasClass('card')) {
2444                  continue;
2445             }
2446             pos = this.getDropPoint(e, this.items[i].el.dom);
2447             
2448             cards_len = ret.cards.length;
2449             //Roo.log(this.items[i].el.dom.id);
2450             ret.cards.push(this.items[i]);
2451             last_card_n  = i;
2452             if (ret.card_n < 0 && pos == 'above') {
2453                 ret.position = cards_len > 0 ? 'below' : pos;
2454                 ret.items_n = i > 0 ? i - 1 : 0;
2455                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2456                 ret.card = ret.cards[ret.card_n];
2457             }
2458         }
2459         if (!ret.cards.length) {
2460             ret.card = true;
2461             ret.position = 'below';
2462             ret.items_n;
2463             return ret;
2464         }
2465         // could not find a card.. stick it at the end..
2466         if (ret.card_n < 0) {
2467             ret.card_n = last_card_n;
2468             ret.card = ret.cards[last_card_n];
2469             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470             ret.position = 'below';
2471         }
2472         
2473         if (this.items[ret.items_n].el == dragged_card_el) {
2474             return false;
2475         }
2476         
2477         if (ret.position == 'below') {
2478             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2479             
2480             if (card_after  && card_after.el == dragged_card_el) {
2481                 return false;
2482             }
2483             return ret;
2484         }
2485         
2486         // its's after ..
2487         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2488         
2489         if (card_before  && card_before.el == dragged_card_el) {
2490             return false;
2491         }
2492         
2493         return ret;
2494     },
2495     
2496     onNodeEnter : function(n, dd, e, data){
2497         return false;
2498     },
2499     onNodeOver : function(n, dd, e, data)
2500     {
2501        
2502         var target_info = this.getTargetFromEvent(e,data.source.el);
2503         if (target_info === false) {
2504             this.dropPlaceHolder('hide');
2505             return false;
2506         }
2507         Roo.log(['getTargetFromEvent', target_info ]);
2508         
2509         
2510         if (this.fireEvent('cardover', this, [ data ]) === false) {
2511             return false;
2512         }
2513         
2514         this.dropPlaceHolder('show', target_info,data);
2515         
2516         return false; 
2517     },
2518     onNodeOut : function(n, dd, e, data){
2519         this.dropPlaceHolder('hide');
2520      
2521     },
2522     onNodeDrop : function(n, dd, e, data)
2523     {
2524         
2525         // call drop - return false if
2526         
2527         // this could actually fail - if the Network drops..
2528         // we will ignore this at present..- client should probably reload
2529         // the whole set of cards if stuff like that fails.
2530         
2531         
2532         var info = this.getTargetFromEvent(e,data.source.el);
2533         if (info === false) {
2534             return false;
2535         }
2536         this.dropPlaceHolder('hide');
2537   
2538           
2539     
2540         this.acceptCard(data.source, info.position, info.card, info.items_n);
2541         return true;
2542          
2543     },
2544     firstChildCard : function()
2545     {
2546         for (var i = 0;i< this.items.length;i++) {
2547             
2548             if (!this.items[i].el.hasClass('card')) {
2549                  continue;
2550             }
2551             return this.items[i];
2552         }
2553         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2554     },
2555     /**
2556      * accept card
2557      *
2558      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2559      */
2560     acceptCard : function(move_card,  position, next_to_card )
2561     {
2562         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2563             return false;
2564         }
2565         
2566         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2567         
2568         move_card.parent().removeCard(move_card);
2569         
2570         
2571         var dom = move_card.el.dom;
2572         dom.style.width = ''; // clear with - which is set by drag.
2573         
2574         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575             var cardel = next_to_card.el.dom;
2576             
2577             if (position == 'above' ) {
2578                 cardel.parentNode.insertBefore(dom, cardel);
2579             } else if (cardel.nextSibling) {
2580                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2581             } else {
2582                 cardel.parentNode.append(dom);
2583             }
2584         } else {
2585             // card container???
2586             this.containerEl.dom.append(dom);
2587         }
2588         
2589         //FIXME HANDLE card = true 
2590         
2591         // add this to the correct place in items.
2592         
2593         // remove Card from items.
2594         
2595        
2596         if (this.items.length) {
2597             var nitems = [];
2598             //Roo.log([info.items_n, info.position, this.items.length]);
2599             for (var i =0; i < this.items.length; i++) {
2600                 if (i == to_items_n && position == 'above') {
2601                     nitems.push(move_card);
2602                 }
2603                 nitems.push(this.items[i]);
2604                 if (i == to_items_n && position == 'below') {
2605                     nitems.push(move_card);
2606                 }
2607             }
2608             this.items = nitems;
2609             Roo.log(this.items);
2610         } else {
2611             this.items.push(move_card);
2612         }
2613         
2614         move_card.parentId = this.id;
2615         
2616         return true;
2617         
2618         
2619     },
2620     removeCard : function(c)
2621     {
2622         this.items = this.items.filter(function(e) { return e != c });
2623  
2624         var dom = c.el.dom;
2625         dom.parentNode.removeChild(dom);
2626         dom.style.width = ''; // clear with - which is set by drag.
2627         c.parentId = false;
2628         
2629     },
2630     
2631     /**    Decide whether to drop above or below a View node. */
2632     getDropPoint : function(e, n, dd)
2633     {
2634         if (dd) {
2635              return false;
2636         }
2637         if (n == this.containerEl.dom) {
2638             return "above";
2639         }
2640         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641         var c = t + (b - t) / 2;
2642         var y = Roo.lib.Event.getPageY(e);
2643         if(y <= c) {
2644             return "above";
2645         }else{
2646             return "below";
2647         }
2648     },
2649     onToggleCollapse : function(e)
2650         {
2651         if (this.collapsed) {
2652             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653             this.collapsableEl.addClass('show');
2654             this.collapsed = false;
2655             return;
2656         }
2657         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658         this.collapsableEl.removeClass('show');
2659         this.collapsed = true;
2660         
2661     
2662     },
2663     
2664     onToggleRotate : function(e)
2665     {
2666         this.collapsableEl.removeClass('show');
2667         this.footerEl.removeClass('d-none');
2668         this.el.removeClass('roo-card-rotated');
2669         this.el.removeClass('d-none');
2670         if (this.rotated) {
2671             
2672             this.collapsableEl.addClass('show');
2673             this.rotated = false;
2674             this.fireEvent('rotate', this, this.rotated);
2675             return;
2676         }
2677         this.el.addClass('roo-card-rotated');
2678         this.footerEl.addClass('d-none');
2679         this.el.select('.roo-collapsable').removeClass('show');
2680         
2681         this.rotated = true;
2682         this.fireEvent('rotate', this, this.rotated);
2683     
2684     },
2685     
2686     dropPlaceHolder: function (action, info, data)
2687     {
2688         if (this.dropEl === false) {
2689             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2690             cls : 'd-none'
2691             },true);
2692         }
2693         this.dropEl.removeClass(['d-none', 'd-block']);        
2694         if (action == 'hide') {
2695             
2696             this.dropEl.addClass('d-none');
2697             return;
2698         }
2699         // FIXME - info.card == true!!!
2700         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2701         
2702         if (info.card !== true) {
2703             var cardel = info.card.el.dom;
2704             
2705             if (info.position == 'above') {
2706                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707             } else if (cardel.nextSibling) {
2708                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2709             } else {
2710                 cardel.parentNode.append(this.dropEl.dom);
2711             }
2712         } else {
2713             // card container???
2714             this.containerEl.dom.append(this.dropEl.dom);
2715         }
2716         
2717         this.dropEl.addClass('d-block roo-card-dropzone');
2718         
2719         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2720         
2721         
2722     
2723     
2724     
2725     },
2726     setHeaderText: function(html)
2727     {
2728         this.header = html;
2729         if (this.headerContainerEl) {
2730             this.headerContainerEl.dom.innerHTML = html;
2731         }
2732     },
2733     onHeaderImageLoad : function(ev, he)
2734     {
2735         if (!this.header_image_fit_square) {
2736             return;
2737         }
2738         
2739         var hw = he.naturalHeight / he.naturalWidth;
2740         // wide image = < 0
2741         // tall image = > 1
2742         //var w = he.dom.naturalWidth;
2743         var ww = he.width;
2744         he.style.left =  0;
2745         he.style.position =  'relative';
2746         if (hw > 1) {
2747             var nw = (ww * (1/hw));
2748             Roo.get(he).setSize( ww * (1/hw),  ww);
2749             he.style.left =  ((ww - nw)/ 2) + 'px';
2750             he.style.position =  'relative';
2751         }
2752
2753     }
2754
2755     
2756 });
2757
2758 /*
2759  * - LGPL
2760  *
2761  * Card header - holder for the card header elements.
2762  * 
2763  */
2764
2765 /**
2766  * @class Roo.bootstrap.CardHeader
2767  * @extends Roo.bootstrap.Element
2768  * Bootstrap CardHeader class
2769  * @constructor
2770  * Create a new Card Header - that you can embed children into
2771  * @param {Object} config The config object
2772  */
2773
2774 Roo.bootstrap.CardHeader = function(config){
2775     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2776 };
2777
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2779     
2780     
2781     container_method : 'getCardHeader' 
2782     
2783      
2784     
2785     
2786    
2787 });
2788
2789  
2790
2791  /*
2792  * - LGPL
2793  *
2794  * Card footer - holder for the card footer elements.
2795  * 
2796  */
2797
2798 /**
2799  * @class Roo.bootstrap.CardFooter
2800  * @extends Roo.bootstrap.Element
2801  * Bootstrap CardFooter class
2802  * @constructor
2803  * Create a new Card Footer - that you can embed children into
2804  * @param {Object} config The config object
2805  */
2806
2807 Roo.bootstrap.CardFooter = function(config){
2808     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2809 };
2810
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2812     
2813     
2814     container_method : 'getCardFooter' 
2815     
2816      
2817     
2818     
2819    
2820 });
2821
2822  
2823
2824  /*
2825  * - LGPL
2826  *
2827  * Card header - holder for the card header elements.
2828  * 
2829  */
2830
2831 /**
2832  * @class Roo.bootstrap.CardImageTop
2833  * @extends Roo.bootstrap.Element
2834  * Bootstrap CardImageTop class
2835  * @constructor
2836  * Create a new Card Image Top container
2837  * @param {Object} config The config object
2838  */
2839
2840 Roo.bootstrap.CardImageTop = function(config){
2841     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2842 };
2843
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2845     
2846    
2847     container_method : 'getCardImageTop' 
2848     
2849      
2850     
2851    
2852 });
2853
2854  
2855
2856  
2857 /*
2858 * Licence: LGPL
2859 */
2860
2861 /**
2862  * @class Roo.bootstrap.ButtonUploader
2863  * @extends Roo.bootstrap.Button
2864  * Bootstrap Button Uploader class - it's a button which when you add files to it
2865  *
2866  * 
2867  * @cfg {Number} errorTimeout default 3000
2868  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2869  * @cfg {Array}  html The button text.
2870  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2871  *
2872  * @constructor
2873  * Create a new CardUploader
2874  * @param {Object} config The config object
2875  */
2876
2877 Roo.bootstrap.ButtonUploader = function(config){
2878     
2879  
2880     
2881     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2882     
2883      
2884      this.addEvents({
2885          // raw events
2886         /**
2887          * @event beforeselect
2888          * When button is pressed, before show upload files dialog is shown
2889          * @param {Roo.bootstrap.UploaderButton} this
2890          *
2891          */
2892         'beforeselect' : true,
2893          /**
2894          * @event fired when files have been selected, 
2895          * When a the download link is clicked
2896          * @param {Roo.bootstrap.UploaderButton} this
2897          * @param {Array} Array of files that have been uploaded
2898          */
2899         'uploaded' : true
2900         
2901     });
2902 };
2903  
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2905     
2906      
2907     errorTimeout : 3000,
2908      
2909     images : false,
2910    
2911     fileCollection : false,
2912     allowBlank : true,
2913     
2914     multiple : true,
2915     
2916     getAutoCreate : function()
2917     {
2918         var im = {
2919             tag: 'input',
2920             type : 'file',
2921             cls : 'd-none  roo-card-upload-selector' 
2922           
2923         };
2924         if (this.multiple) {
2925             im.multiple = 'multiple';
2926         }
2927         
2928         return  {
2929             cls :'div' ,
2930             cn : [
2931                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2932                 im
2933
2934             ]
2935         };
2936            
2937          
2938     },
2939      
2940    
2941     initEvents : function()
2942     {
2943         
2944         Roo.bootstrap.Button.prototype.initEvents.call(this);
2945         
2946         
2947         
2948         
2949         
2950         this.urlAPI = (window.createObjectURL && window) || 
2951                                 (window.URL && URL.revokeObjectURL && URL) || 
2952                                 (window.webkitURL && webkitURL);
2953                         
2954          
2955          
2956          
2957         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2958         
2959         this.selectorEl.on('change', this.onFileSelected, this);
2960          
2961          
2962        
2963     },
2964     
2965    
2966     onClick : function(e)
2967     {
2968         e.preventDefault();
2969         
2970         if ( this.fireEvent('beforeselect', this) === false) {
2971             return;
2972         }
2973          
2974         this.selectorEl.dom.click();
2975          
2976     },
2977     
2978     onFileSelected : function(e)
2979     {
2980         e.preventDefault();
2981         
2982         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2983             return;
2984         }
2985         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986         this.selectorEl.dom.value  = '';// hopefully reset..
2987         
2988         this.fireEvent('uploaded', this,  files );
2989         
2990     },
2991     
2992        
2993    
2994     
2995     /**
2996      * addCard - add an Attachment to the uploader
2997      * @param data - the data about the image to upload
2998      *
2999      * {
3000           id : 123
3001           title : "Title of file",
3002           is_uploaded : false,
3003           src : "http://.....",
3004           srcfile : { the File upload object },
3005           mimetype : file.type,
3006           preview : false,
3007           is_deleted : 0
3008           .. any other data...
3009         }
3010      *
3011      * 
3012     */
3013      
3014     reset: function()
3015     {
3016          
3017          this.selectorEl
3018     } 
3019     
3020     
3021     
3022     
3023 });
3024  /*
3025  * - LGPL
3026  *
3027  * image
3028  * 
3029  */
3030
3031
3032 /**
3033  * @class Roo.bootstrap.Img
3034  * @extends Roo.bootstrap.Component
3035  * Bootstrap Img class
3036  * @cfg {Boolean} imgResponsive false | true
3037  * @cfg {String} border rounded | circle | thumbnail
3038  * @cfg {String} src image source
3039  * @cfg {String} alt image alternative text
3040  * @cfg {String} href a tag href
3041  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042  * @cfg {String} xsUrl xs image source
3043  * @cfg {String} smUrl sm image source
3044  * @cfg {String} mdUrl md image source
3045  * @cfg {String} lgUrl lg image source
3046  * 
3047  * @constructor
3048  * Create a new Input
3049  * @param {Object} config The config object
3050  */
3051
3052 Roo.bootstrap.Img = function(config){
3053     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3054     
3055     this.addEvents({
3056         // img events
3057         /**
3058          * @event click
3059          * The img click event for the img.
3060          * @param {Roo.EventObject} e
3061          */
3062         "click" : true
3063     });
3064 };
3065
3066 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3067     
3068     imgResponsive: true,
3069     border: '',
3070     src: 'about:blank',
3071     href: false,
3072     target: false,
3073     xsUrl: '',
3074     smUrl: '',
3075     mdUrl: '',
3076     lgUrl: '',
3077
3078     getAutoCreate : function()
3079     {   
3080         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3081             return this.createSingleImg();
3082         }
3083         
3084         var cfg = {
3085             tag: 'div',
3086             cls: 'roo-image-responsive-group',
3087             cn: []
3088         };
3089         var _this = this;
3090         
3091         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3092             
3093             if(!_this[size + 'Url']){
3094                 return;
3095             }
3096             
3097             var img = {
3098                 tag: 'img',
3099                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3100                 html: _this.html || cfg.html,
3101                 src: _this[size + 'Url']
3102             };
3103             
3104             img.cls += ' roo-image-responsive-' + size;
3105             
3106             var s = ['xs', 'sm', 'md', 'lg'];
3107             
3108             s.splice(s.indexOf(size), 1);
3109             
3110             Roo.each(s, function(ss){
3111                 img.cls += ' hidden-' + ss;
3112             });
3113             
3114             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3115                 cfg.cls += ' img-' + _this.border;
3116             }
3117             
3118             if(_this.alt){
3119                 cfg.alt = _this.alt;
3120             }
3121             
3122             if(_this.href){
3123                 var a = {
3124                     tag: 'a',
3125                     href: _this.href,
3126                     cn: [
3127                         img
3128                     ]
3129                 };
3130
3131                 if(this.target){
3132                     a.target = _this.target;
3133                 }
3134             }
3135             
3136             cfg.cn.push((_this.href) ? a : img);
3137             
3138         });
3139         
3140         return cfg;
3141     },
3142     
3143     createSingleImg : function()
3144     {
3145         var cfg = {
3146             tag: 'img',
3147             cls: (this.imgResponsive) ? 'img-responsive' : '',
3148             html : null,
3149             src : 'about:blank'  // just incase src get's set to undefined?!?
3150         };
3151         
3152         cfg.html = this.html || cfg.html;
3153         
3154         cfg.src = this.src || cfg.src;
3155         
3156         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3157             cfg.cls += ' img-' + this.border;
3158         }
3159         
3160         if(this.alt){
3161             cfg.alt = this.alt;
3162         }
3163         
3164         if(this.href){
3165             var a = {
3166                 tag: 'a',
3167                 href: this.href,
3168                 cn: [
3169                     cfg
3170                 ]
3171             };
3172             
3173             if(this.target){
3174                 a.target = this.target;
3175             }
3176             
3177         }
3178         
3179         return (this.href) ? a : cfg;
3180     },
3181     
3182     initEvents: function() 
3183     {
3184         if(!this.href){
3185             this.el.on('click', this.onClick, this);
3186         }
3187         
3188     },
3189     
3190     onClick : function(e)
3191     {
3192         Roo.log('img onclick');
3193         this.fireEvent('click', this, e);
3194     },
3195     /**
3196      * Sets the url of the image - used to update it
3197      * @param {String} url the url of the image
3198      */
3199     
3200     setSrc : function(url)
3201     {
3202         this.src =  url;
3203         
3204         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205             this.el.dom.src =  url;
3206             return;
3207         }
3208         
3209         this.el.select('img', true).first().dom.src =  url;
3210     }
3211     
3212     
3213    
3214 });
3215
3216  /*
3217  * - LGPL
3218  *
3219  * image
3220  * 
3221  */
3222
3223
3224 /**
3225  * @class Roo.bootstrap.Link
3226  * @extends Roo.bootstrap.Component
3227  * Bootstrap Link Class
3228  * @cfg {String} alt image alternative text
3229  * @cfg {String} href a tag href
3230  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3231  * @cfg {String} html the content of the link.
3232  * @cfg {String} anchor name for the anchor link
3233  * @cfg {String} fa - favicon
3234
3235  * @cfg {Boolean} preventDefault (true | false) default false
3236
3237  * 
3238  * @constructor
3239  * Create a new Input
3240  * @param {Object} config The config object
3241  */
3242
3243 Roo.bootstrap.Link = function(config){
3244     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3245     
3246     this.addEvents({
3247         // img events
3248         /**
3249          * @event click
3250          * The img click event for the img.
3251          * @param {Roo.EventObject} e
3252          */
3253         "click" : true
3254     });
3255 };
3256
3257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3258     
3259     href: false,
3260     target: false,
3261     preventDefault: false,
3262     anchor : false,
3263     alt : false,
3264     fa: false,
3265
3266
3267     getAutoCreate : function()
3268     {
3269         var html = this.html || '';
3270         
3271         if (this.fa !== false) {
3272             html = '<i class="fa fa-' + this.fa + '"></i>';
3273         }
3274         var cfg = {
3275             tag: 'a'
3276         };
3277         // anchor's do not require html/href...
3278         if (this.anchor === false) {
3279             cfg.html = html;
3280             cfg.href = this.href || '#';
3281         } else {
3282             cfg.name = this.anchor;
3283             if (this.html !== false || this.fa !== false) {
3284                 cfg.html = html;
3285             }
3286             if (this.href !== false) {
3287                 cfg.href = this.href;
3288             }
3289         }
3290         
3291         if(this.alt !== false){
3292             cfg.alt = this.alt;
3293         }
3294         
3295         
3296         if(this.target !== false) {
3297             cfg.target = this.target;
3298         }
3299         
3300         return cfg;
3301     },
3302     
3303     initEvents: function() {
3304         
3305         if(!this.href || this.preventDefault){
3306             this.el.on('click', this.onClick, this);
3307         }
3308     },
3309     
3310     onClick : function(e)
3311     {
3312         if(this.preventDefault){
3313             e.preventDefault();
3314         }
3315         //Roo.log('img onclick');
3316         this.fireEvent('click', this, e);
3317     }
3318    
3319 });
3320
3321  /*
3322  * - LGPL
3323  *
3324  * header
3325  * 
3326  */
3327
3328 /**
3329  * @class Roo.bootstrap.Header
3330  * @extends Roo.bootstrap.Component
3331  * Bootstrap Header class
3332  * @cfg {String} html content of header
3333  * @cfg {Number} level (1|2|3|4|5|6) default 1
3334  * 
3335  * @constructor
3336  * Create a new Header
3337  * @param {Object} config The config object
3338  */
3339
3340
3341 Roo.bootstrap.Header  = function(config){
3342     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3343 };
3344
3345 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3346     
3347     //href : false,
3348     html : false,
3349     level : 1,
3350     
3351     
3352     
3353     getAutoCreate : function(){
3354         
3355         
3356         
3357         var cfg = {
3358             tag: 'h' + (1 *this.level),
3359             html: this.html || ''
3360         } ;
3361         
3362         return cfg;
3363     }
3364    
3365 });
3366
3367  
3368
3369  /*
3370  * Based on:
3371  * Ext JS Library 1.1.1
3372  * Copyright(c) 2006-2007, Ext JS, LLC.
3373  *
3374  * Originally Released Under LGPL - original licence link has changed is not relivant.
3375  *
3376  * Fork - LGPL
3377  * <script type="text/javascript">
3378  */
3379  
3380 /**
3381  * @class Roo.bootstrap.MenuMgr
3382  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3383  * @singleton
3384  */
3385 Roo.bootstrap.MenuMgr = function(){
3386    var menus, active, groups = {}, attached = false, lastShow = new Date();
3387
3388    // private - called when first menu is created
3389    function init(){
3390        menus = {};
3391        active = new Roo.util.MixedCollection();
3392        Roo.get(document).addKeyListener(27, function(){
3393            if(active.length > 0){
3394                hideAll();
3395            }
3396        });
3397    }
3398
3399    // private
3400    function hideAll(){
3401        if(active && active.length > 0){
3402            var c = active.clone();
3403            c.each(function(m){
3404                m.hide();
3405            });
3406        }
3407    }
3408
3409    // private
3410    function onHide(m){
3411        active.remove(m);
3412        if(active.length < 1){
3413            Roo.get(document).un("mouseup", onMouseDown);
3414             
3415            attached = false;
3416        }
3417    }
3418
3419    // private
3420    function onShow(m){
3421        var last = active.last();
3422        lastShow = new Date();
3423        active.add(m);
3424        if(!attached){
3425           Roo.get(document).on("mouseup", onMouseDown);
3426            
3427            attached = true;
3428        }
3429        if(m.parentMenu){
3430           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3431           m.parentMenu.activeChild = m;
3432        }else if(last && last.isVisible()){
3433           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3434        }
3435    }
3436
3437    // private
3438    function onBeforeHide(m){
3439        if(m.activeChild){
3440            m.activeChild.hide();
3441        }
3442        if(m.autoHideTimer){
3443            clearTimeout(m.autoHideTimer);
3444            delete m.autoHideTimer;
3445        }
3446    }
3447
3448    // private
3449    function onBeforeShow(m){
3450        var pm = m.parentMenu;
3451        if(!pm && !m.allowOtherMenus){
3452            hideAll();
3453        }else if(pm && pm.activeChild && active != m){
3454            pm.activeChild.hide();
3455        }
3456    }
3457
3458    // private this should really trigger on mouseup..
3459    function onMouseDown(e){
3460         Roo.log("on Mouse Up");
3461         
3462         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3463             Roo.log("MenuManager hideAll");
3464             hideAll();
3465             e.stopEvent();
3466         }
3467         
3468         
3469    }
3470
3471    // private
3472    function onBeforeCheck(mi, state){
3473        if(state){
3474            var g = groups[mi.group];
3475            for(var i = 0, l = g.length; i < l; i++){
3476                if(g[i] != mi){
3477                    g[i].setChecked(false);
3478                }
3479            }
3480        }
3481    }
3482
3483    return {
3484
3485        /**
3486         * Hides all menus that are currently visible
3487         */
3488        hideAll : function(){
3489             hideAll();  
3490        },
3491
3492        // private
3493        register : function(menu){
3494            if(!menus){
3495                init();
3496            }
3497            menus[menu.id] = menu;
3498            menu.on("beforehide", onBeforeHide);
3499            menu.on("hide", onHide);
3500            menu.on("beforeshow", onBeforeShow);
3501            menu.on("show", onShow);
3502            var g = menu.group;
3503            if(g && menu.events["checkchange"]){
3504                if(!groups[g]){
3505                    groups[g] = [];
3506                }
3507                groups[g].push(menu);
3508                menu.on("checkchange", onCheck);
3509            }
3510        },
3511
3512         /**
3513          * Returns a {@link Roo.menu.Menu} object
3514          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3515          * be used to generate and return a new Menu instance.
3516          */
3517        get : function(menu){
3518            if(typeof menu == "string"){ // menu id
3519                return menus[menu];
3520            }else if(menu.events){  // menu instance
3521                return menu;
3522            }
3523            /*else if(typeof menu.length == 'number'){ // array of menu items?
3524                return new Roo.bootstrap.Menu({items:menu});
3525            }else{ // otherwise, must be a config
3526                return new Roo.bootstrap.Menu(menu);
3527            }
3528            */
3529            return false;
3530        },
3531
3532        // private
3533        unregister : function(menu){
3534            delete menus[menu.id];
3535            menu.un("beforehide", onBeforeHide);
3536            menu.un("hide", onHide);
3537            menu.un("beforeshow", onBeforeShow);
3538            menu.un("show", onShow);
3539            var g = menu.group;
3540            if(g && menu.events["checkchange"]){
3541                groups[g].remove(menu);
3542                menu.un("checkchange", onCheck);
3543            }
3544        },
3545
3546        // private
3547        registerCheckable : function(menuItem){
3548            var g = menuItem.group;
3549            if(g){
3550                if(!groups[g]){
3551                    groups[g] = [];
3552                }
3553                groups[g].push(menuItem);
3554                menuItem.on("beforecheckchange", onBeforeCheck);
3555            }
3556        },
3557
3558        // private
3559        unregisterCheckable : function(menuItem){
3560            var g = menuItem.group;
3561            if(g){
3562                groups[g].remove(menuItem);
3563                menuItem.un("beforecheckchange", onBeforeCheck);
3564            }
3565        }
3566    };
3567 }();/*
3568  * - LGPL
3569  *
3570  * menu
3571  * 
3572  */
3573
3574 /**
3575  * @class Roo.bootstrap.Menu
3576  * @extends Roo.bootstrap.Component
3577  * Bootstrap Menu class - container for MenuItems
3578  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3579  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3580  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3581  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3582   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3583   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3584  
3585  * @constructor
3586  * Create a new Menu
3587  * @param {Object} config The config object
3588  */
3589
3590
3591 Roo.bootstrap.Menu = function(config){
3592     
3593     if (config.type == 'treeview') {
3594         // normally menu's are drawn attached to the document to handle layering etc..
3595         // however treeview (used by the docs menu is drawn into the parent element)
3596         this.container_method = 'getChildContainer'; 
3597     }
3598     
3599     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3600     if (this.registerMenu && this.type != 'treeview')  {
3601         Roo.bootstrap.MenuMgr.register(this);
3602     }
3603     
3604     
3605     this.addEvents({
3606         /**
3607          * @event beforeshow
3608          * Fires before this menu is displayed (return false to block)
3609          * @param {Roo.menu.Menu} this
3610          */
3611         beforeshow : true,
3612         /**
3613          * @event beforehide
3614          * Fires before this menu is hidden (return false to block)
3615          * @param {Roo.menu.Menu} this
3616          */
3617         beforehide : true,
3618         /**
3619          * @event show
3620          * Fires after this menu is displayed
3621          * @param {Roo.menu.Menu} this
3622          */
3623         show : true,
3624         /**
3625          * @event hide
3626          * Fires after this menu is hidden
3627          * @param {Roo.menu.Menu} this
3628          */
3629         hide : true,
3630         /**
3631          * @event click
3632          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3633          * @param {Roo.menu.Menu} this
3634          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3635          * @param {Roo.EventObject} e
3636          */
3637         click : true,
3638         /**
3639          * @event mouseover
3640          * Fires when the mouse is hovering over this menu
3641          * @param {Roo.menu.Menu} this
3642          * @param {Roo.EventObject} e
3643          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3644          */
3645         mouseover : true,
3646         /**
3647          * @event mouseout
3648          * Fires when the mouse exits this menu
3649          * @param {Roo.menu.Menu} this
3650          * @param {Roo.EventObject} e
3651          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3652          */
3653         mouseout : true,
3654         /**
3655          * @event itemclick
3656          * Fires when a menu item contained in this menu is clicked
3657          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3658          * @param {Roo.EventObject} e
3659          */
3660         itemclick: true
3661     });
3662     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3663 };
3664
3665 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3666     
3667    /// html : false,
3668    
3669     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3670     type: false,
3671     /**
3672      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3673      */
3674     registerMenu : true,
3675     
3676     menuItems :false, // stores the menu items..
3677     
3678     hidden:true,
3679         
3680     parentMenu : false,
3681     
3682     stopEvent : true,
3683     
3684     isLink : false,
3685     
3686     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3687     
3688     hideTrigger : false,
3689     
3690     align : 'tl-bl?',
3691     
3692     
3693     getChildContainer : function() {
3694         return this.el;  
3695     },
3696     
3697     getAutoCreate : function(){
3698          
3699         //if (['right'].indexOf(this.align)!==-1) {
3700         //    cfg.cn[1].cls += ' pull-right'
3701         //}
3702          
3703         var cfg = {
3704             tag : 'ul',
3705             cls : 'dropdown-menu shadow' ,
3706             style : 'z-index:1000'
3707             
3708         };
3709         
3710         if (this.type === 'submenu') {
3711             cfg.cls = 'submenu active';
3712         }
3713         if (this.type === 'treeview') {
3714             cfg.cls = 'treeview-menu';
3715         }
3716         
3717         return cfg;
3718     },
3719     initEvents : function() {
3720         
3721        // Roo.log("ADD event");
3722        // Roo.log(this.triggerEl.dom);
3723         if (this.triggerEl) {
3724             
3725             this.triggerEl.on('click', this.onTriggerClick, this);
3726             
3727             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3728             
3729             if (!this.hideTrigger) {
3730                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3731                     // dropdown toggle on the 'a' in BS4?
3732                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3733                 } else {
3734                     this.triggerEl.addClass('dropdown-toggle');
3735                 }
3736             }
3737         }
3738         
3739         if (Roo.isTouch) {
3740             this.el.on('touchstart'  , this.onTouch, this);
3741         }
3742         this.el.on('click' , this.onClick, this);
3743
3744         this.el.on("mouseover", this.onMouseOver, this);
3745         this.el.on("mouseout", this.onMouseOut, this);
3746         
3747     },
3748     
3749     findTargetItem : function(e)
3750     {
3751         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3752         if(!t){
3753             return false;
3754         }
3755         //Roo.log(t);         Roo.log(t.id);
3756         if(t && t.id){
3757             //Roo.log(this.menuitems);
3758             return this.menuitems.get(t.id);
3759             
3760             //return this.items.get(t.menuItemId);
3761         }
3762         
3763         return false;
3764     },
3765     
3766     onTouch : function(e) 
3767     {
3768         Roo.log("menu.onTouch");
3769         //e.stopEvent(); this make the user popdown broken
3770         this.onClick(e);
3771     },
3772     
3773     onClick : function(e)
3774     {
3775         Roo.log("menu.onClick");
3776         
3777         var t = this.findTargetItem(e);
3778         if(!t || t.isContainer){
3779             return;
3780         }
3781         Roo.log(e);
3782         /*
3783         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3784             if(t == this.activeItem && t.shouldDeactivate(e)){
3785                 this.activeItem.deactivate();
3786                 delete this.activeItem;
3787                 return;
3788             }
3789             if(t.canActivate){
3790                 this.setActiveItem(t, true);
3791             }
3792             return;
3793             
3794             
3795         }
3796         */
3797        
3798         Roo.log('pass click event');
3799         
3800         t.onClick(e);
3801         
3802         this.fireEvent("click", this, t, e);
3803         
3804         var _this = this;
3805         
3806         if(!t.href.length || t.href == '#'){
3807             (function() { _this.hide(); }).defer(100);
3808         }
3809         
3810     },
3811     
3812     onMouseOver : function(e){
3813         var t  = this.findTargetItem(e);
3814         //Roo.log(t);
3815         //if(t){
3816         //    if(t.canActivate && !t.disabled){
3817         //        this.setActiveItem(t, true);
3818         //    }
3819         //}
3820         
3821         this.fireEvent("mouseover", this, e, t);
3822     },
3823     isVisible : function(){
3824         return !this.hidden;
3825     },
3826     onMouseOut : function(e){
3827         var t  = this.findTargetItem(e);
3828         
3829         //if(t ){
3830         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3831         //        this.activeItem.deactivate();
3832         //        delete this.activeItem;
3833         //    }
3834         //}
3835         this.fireEvent("mouseout", this, e, t);
3836     },
3837     
3838     
3839     /**
3840      * Displays this menu relative to another element
3841      * @param {String/HTMLElement/Roo.Element} element The element to align to
3842      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3843      * the element (defaults to this.defaultAlign)
3844      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3845      */
3846     show : function(el, pos, parentMenu)
3847     {
3848         if (false === this.fireEvent("beforeshow", this)) {
3849             Roo.log("show canceled");
3850             return;
3851         }
3852         this.parentMenu = parentMenu;
3853         if(!this.el){
3854             this.render();
3855         }
3856         this.el.addClass('show'); // show otherwise we do not know how big we are..
3857          
3858         var xy = this.el.getAlignToXY(el, pos);
3859         
3860         // bl-tl << left align  below
3861         // tl-bl << left align 
3862         
3863         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3864             // if it goes to far to the right.. -> align left.
3865             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3866         }
3867         if(xy[0] < 0){
3868             // was left align - go right?
3869             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3870         }
3871         
3872         // goes down the bottom
3873         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3874            xy[1]  < 0 ){
3875             var a = this.align.replace('?', '').split('-');
3876             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3877             
3878         }
3879         
3880         this.showAt(  xy , parentMenu, false);
3881     },
3882      /**
3883      * Displays this menu at a specific xy position
3884      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3885      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3886      */
3887     showAt : function(xy, parentMenu, /* private: */_e){
3888         this.parentMenu = parentMenu;
3889         if(!this.el){
3890             this.render();
3891         }
3892         if(_e !== false){
3893             this.fireEvent("beforeshow", this);
3894             //xy = this.el.adjustForConstraints(xy);
3895         }
3896         
3897         //this.el.show();
3898         this.hideMenuItems();
3899         this.hidden = false;
3900         if (this.triggerEl) {
3901             this.triggerEl.addClass('open');
3902         }
3903         
3904         this.el.addClass('show');
3905         
3906         
3907         
3908         // reassign x when hitting right
3909         
3910         // reassign y when hitting bottom
3911         
3912         // but the list may align on trigger left or trigger top... should it be a properity?
3913         
3914         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3915             this.el.setXY(xy);
3916         }
3917         
3918         this.focus();
3919         this.fireEvent("show", this);
3920     },
3921     
3922     focus : function(){
3923         return;
3924         if(!this.hidden){
3925             this.doFocus.defer(50, this);
3926         }
3927     },
3928
3929     doFocus : function(){
3930         if(!this.hidden){
3931             this.focusEl.focus();
3932         }
3933     },
3934
3935     /**
3936      * Hides this menu and optionally all parent menus
3937      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3938      */
3939     hide : function(deep)
3940     {
3941         if (false === this.fireEvent("beforehide", this)) {
3942             Roo.log("hide canceled");
3943             return;
3944         }
3945         this.hideMenuItems();
3946         if(this.el && this.isVisible()){
3947            
3948             if(this.activeItem){
3949                 this.activeItem.deactivate();
3950                 this.activeItem = null;
3951             }
3952             if (this.triggerEl) {
3953                 this.triggerEl.removeClass('open');
3954             }
3955             
3956             this.el.removeClass('show');
3957             this.hidden = true;
3958             this.fireEvent("hide", this);
3959         }
3960         if(deep === true && this.parentMenu){
3961             this.parentMenu.hide(true);
3962         }
3963     },
3964     
3965     onTriggerClick : function(e)
3966     {
3967         Roo.log('trigger click');
3968         
3969         var target = e.getTarget();
3970         
3971         Roo.log(target.nodeName.toLowerCase());
3972         
3973         if(target.nodeName.toLowerCase() === 'i'){
3974             e.preventDefault();
3975         }
3976         
3977     },
3978     
3979     onTriggerPress  : function(e)
3980     {
3981         Roo.log('trigger press');
3982         //Roo.log(e.getTarget());
3983        // Roo.log(this.triggerEl.dom);
3984        
3985         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3986         var pel = Roo.get(e.getTarget());
3987         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3988             Roo.log('is treeview or dropdown?');
3989             return;
3990         }
3991         
3992         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3993             return;
3994         }
3995         
3996         if (this.isVisible()) {
3997             Roo.log('hide');
3998             this.hide();
3999         } else {
4000             Roo.log('show');
4001             
4002             this.show(this.triggerEl, this.align, false);
4003         }
4004         
4005         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4006             e.stopEvent();
4007         }
4008         
4009     },
4010        
4011     
4012     hideMenuItems : function()
4013     {
4014         Roo.log("hide Menu Items");
4015         if (!this.el) { 
4016             return;
4017         }
4018         
4019         this.el.select('.open',true).each(function(aa) {
4020             
4021             aa.removeClass('open');
4022          
4023         });
4024     },
4025     addxtypeChild : function (tree, cntr) {
4026         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4027           
4028         this.menuitems.add(comp);
4029         return comp;
4030
4031     },
4032     getEl : function()
4033     {
4034         Roo.log(this.el);
4035         return this.el;
4036     },
4037     
4038     clear : function()
4039     {
4040         this.getEl().dom.innerHTML = '';
4041         this.menuitems.clear();
4042     }
4043 });
4044
4045  
4046  /*
4047  * - LGPL
4048  *
4049  * menu item
4050  * 
4051  */
4052
4053
4054 /**
4055  * @class Roo.bootstrap.MenuItem
4056  * @extends Roo.bootstrap.Component
4057  * Bootstrap MenuItem class
4058  * @cfg {String} html the menu label
4059  * @cfg {String} href the link
4060  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4061  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4062  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4063  * @cfg {String} fa favicon to show on left of menu item.
4064  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4065  * 
4066  * 
4067  * @constructor
4068  * Create a new MenuItem
4069  * @param {Object} config The config object
4070  */
4071
4072
4073 Roo.bootstrap.MenuItem = function(config){
4074     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4075     this.addEvents({
4076         // raw events
4077         /**
4078          * @event click
4079          * The raw click event for the entire grid.
4080          * @param {Roo.bootstrap.MenuItem} this
4081          * @param {Roo.EventObject} e
4082          */
4083         "click" : true
4084     });
4085 };
4086
4087 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4088     
4089     href : false,
4090     html : false,
4091     preventDefault: false,
4092     isContainer : false,
4093     active : false,
4094     fa: false,
4095     
4096     getAutoCreate : function(){
4097         
4098         if(this.isContainer){
4099             return {
4100                 tag: 'li',
4101                 cls: 'dropdown-menu-item '
4102             };
4103         }
4104         var ctag = {
4105             tag: 'span',
4106             html: 'Link'
4107         };
4108         
4109         var anc = {
4110             tag : 'a',
4111             cls : 'dropdown-item',
4112             href : '#',
4113             cn : [  ]
4114         };
4115         
4116         if (this.fa !== false) {
4117             anc.cn.push({
4118                 tag : 'i',
4119                 cls : 'fa fa-' + this.fa
4120             });
4121         }
4122         
4123         anc.cn.push(ctag);
4124         
4125         
4126         var cfg= {
4127             tag: 'li',
4128             cls: 'dropdown-menu-item',
4129             cn: [ anc ]
4130         };
4131         if (this.parent().type == 'treeview') {
4132             cfg.cls = 'treeview-menu';
4133         }
4134         if (this.active) {
4135             cfg.cls += ' active';
4136         }
4137         
4138         
4139         
4140         anc.href = this.href || cfg.cn[0].href ;
4141         ctag.html = this.html || cfg.cn[0].html ;
4142         return cfg;
4143     },
4144     
4145     initEvents: function()
4146     {
4147         if (this.parent().type == 'treeview') {
4148             this.el.select('a').on('click', this.onClick, this);
4149         }
4150         
4151         if (this.menu) {
4152             this.menu.parentType = this.xtype;
4153             this.menu.triggerEl = this.el;
4154             this.menu = this.addxtype(Roo.apply({}, this.menu));
4155         }
4156         
4157     },
4158     onClick : function(e)
4159     {
4160         Roo.log('item on click ');
4161         
4162         if(this.preventDefault){
4163             e.preventDefault();
4164         }
4165         //this.parent().hideMenuItems();
4166         
4167         this.fireEvent('click', this, e);
4168     },
4169     getEl : function()
4170     {
4171         return this.el;
4172     } 
4173 });
4174
4175  
4176
4177  /*
4178  * - LGPL
4179  *
4180  * menu separator
4181  * 
4182  */
4183
4184
4185 /**
4186  * @class Roo.bootstrap.MenuSeparator
4187  * @extends Roo.bootstrap.Component
4188  * Bootstrap MenuSeparator class
4189  * 
4190  * @constructor
4191  * Create a new MenuItem
4192  * @param {Object} config The config object
4193  */
4194
4195
4196 Roo.bootstrap.MenuSeparator = function(config){
4197     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4198 };
4199
4200 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4201     
4202     getAutoCreate : function(){
4203         var cfg = {
4204             cls: 'divider',
4205             tag : 'li'
4206         };
4207         
4208         return cfg;
4209     }
4210    
4211 });
4212
4213  
4214
4215  
4216 /*
4217 * Licence: LGPL
4218 */
4219
4220 /**
4221  * @class Roo.bootstrap.Modal
4222  * @extends Roo.bootstrap.Component
4223  * Bootstrap Modal class
4224  * @cfg {String} title Title of dialog
4225  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4226  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4227  * @cfg {Boolean} specificTitle default false
4228  * @cfg {Array} buttons Array of buttons or standard button set..
4229  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4230  * @cfg {Boolean} animate default true
4231  * @cfg {Boolean} allow_close default true
4232  * @cfg {Boolean} fitwindow default false
4233  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4234  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4235  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4236  * @cfg {String} size (sm|lg|xl) default empty
4237  * @cfg {Number} max_width set the max width of modal
4238  * @cfg {Boolean} editableTitle can the title be edited
4239
4240  *
4241  *
4242  * @constructor
4243  * Create a new Modal Dialog
4244  * @param {Object} config The config object
4245  */
4246
4247 Roo.bootstrap.Modal = function(config){
4248     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4249     this.addEvents({
4250         // raw events
4251         /**
4252          * @event btnclick
4253          * The raw btnclick event for the button
4254          * @param {Roo.EventObject} e
4255          */
4256         "btnclick" : true,
4257         /**
4258          * @event resize
4259          * Fire when dialog resize
4260          * @param {Roo.bootstrap.Modal} this
4261          * @param {Roo.EventObject} e
4262          */
4263         "resize" : true,
4264         /**
4265          * @event titlechanged
4266          * Fire when the editable title has been changed
4267          * @param {Roo.bootstrap.Modal} this
4268          * @param {Roo.EventObject} value
4269          */
4270         "titlechanged" : true 
4271         
4272     });
4273     this.buttons = this.buttons || [];
4274
4275     if (this.tmpl) {
4276         this.tmpl = Roo.factory(this.tmpl);
4277     }
4278
4279 };
4280
4281 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4282
4283     title : 'test dialog',
4284
4285     buttons : false,
4286
4287     // set on load...
4288
4289     html: false,
4290
4291     tmp: false,
4292
4293     specificTitle: false,
4294
4295     buttonPosition: 'right',
4296
4297     allow_close : true,
4298
4299     animate : true,
4300
4301     fitwindow: false,
4302     
4303      // private
4304     dialogEl: false,
4305     bodyEl:  false,
4306     footerEl:  false,
4307     titleEl:  false,
4308     closeEl:  false,
4309
4310     size: '',
4311     
4312     max_width: 0,
4313     
4314     max_height: 0,
4315     
4316     fit_content: false,
4317     editableTitle  : false,
4318
4319     onRender : function(ct, position)
4320     {
4321         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4322
4323         if(!this.el){
4324             var cfg = Roo.apply({},  this.getAutoCreate());
4325             cfg.id = Roo.id();
4326             //if(!cfg.name){
4327             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4328             //}
4329             //if (!cfg.name.length) {
4330             //    delete cfg.name;
4331            // }
4332             if (this.cls) {
4333                 cfg.cls += ' ' + this.cls;
4334             }
4335             if (this.style) {
4336                 cfg.style = this.style;
4337             }
4338             this.el = Roo.get(document.body).createChild(cfg, position);
4339         }
4340         //var type = this.el.dom.type;
4341
4342
4343         if(this.tabIndex !== undefined){
4344             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4345         }
4346
4347         this.dialogEl = this.el.select('.modal-dialog',true).first();
4348         this.bodyEl = this.el.select('.modal-body',true).first();
4349         this.closeEl = this.el.select('.modal-header .close', true).first();
4350         this.headerEl = this.el.select('.modal-header',true).first();
4351         this.titleEl = this.el.select('.modal-title',true).first();
4352         this.footerEl = this.el.select('.modal-footer',true).first();
4353
4354         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4355         
4356         //this.el.addClass("x-dlg-modal");
4357
4358         if (this.buttons.length) {
4359             Roo.each(this.buttons, function(bb) {
4360                 var b = Roo.apply({}, bb);
4361                 b.xns = b.xns || Roo.bootstrap;
4362                 b.xtype = b.xtype || 'Button';
4363                 if (typeof(b.listeners) == 'undefined') {
4364                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4365                 }
4366
4367                 var btn = Roo.factory(b);
4368
4369                 btn.render(this.getButtonContainer());
4370
4371             },this);
4372         }
4373         // render the children.
4374         var nitems = [];
4375
4376         if(typeof(this.items) != 'undefined'){
4377             var items = this.items;
4378             delete this.items;
4379
4380             for(var i =0;i < items.length;i++) {
4381                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4382             }
4383         }
4384
4385         this.items = nitems;
4386
4387         // where are these used - they used to be body/close/footer
4388
4389
4390         this.initEvents();
4391         //this.el.addClass([this.fieldClass, this.cls]);
4392
4393     },
4394
4395     getAutoCreate : function()
4396     {
4397         // we will default to modal-body-overflow - might need to remove or make optional later.
4398         var bdy = {
4399                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4400                 html : this.html || ''
4401         };
4402
4403         var title = {
4404             tag: 'h5',
4405             cls : 'modal-title',
4406             html : this.title
4407         };
4408
4409         if(this.specificTitle){ // WTF is this?
4410             title = this.title;
4411         }
4412
4413         var header = [];
4414         if (this.allow_close && Roo.bootstrap.version == 3) {
4415             header.push({
4416                 tag: 'button',
4417                 cls : 'close',
4418                 html : '&times'
4419             });
4420         }
4421
4422         header.push(title);
4423
4424         if (this.editableTitle) {
4425             header.push({
4426                 cls: 'form-control roo-editable-title d-none',
4427                 tag: 'input',
4428                 type: 'text'
4429             });
4430         }
4431         
4432         if (this.allow_close && Roo.bootstrap.version == 4) {
4433             header.push({
4434                 tag: 'button',
4435                 cls : 'close',
4436                 html : '&times'
4437             });
4438         }
4439         
4440         var size = '';
4441
4442         if(this.size.length){
4443             size = 'modal-' + this.size;
4444         }
4445         
4446         var footer = Roo.bootstrap.version == 3 ?
4447             {
4448                 cls : 'modal-footer',
4449                 cn : [
4450                     {
4451                         tag: 'div',
4452                         cls: 'btn-' + this.buttonPosition
4453                     }
4454                 ]
4455
4456             } :
4457             {  // BS4 uses mr-auto on left buttons....
4458                 cls : 'modal-footer'
4459             };
4460
4461             
4462
4463         
4464         
4465         var modal = {
4466             cls: "modal",
4467              cn : [
4468                 {
4469                     cls: "modal-dialog " + size,
4470                     cn : [
4471                         {
4472                             cls : "modal-content",
4473                             cn : [
4474                                 {
4475                                     cls : 'modal-header',
4476                                     cn : header
4477                                 },
4478                                 bdy,
4479                                 footer
4480                             ]
4481
4482                         }
4483                     ]
4484
4485                 }
4486             ]
4487         };
4488
4489         if(this.animate){
4490             modal.cls += ' fade';
4491         }
4492
4493         return modal;
4494
4495     },
4496     getChildContainer : function() {
4497
4498          return this.bodyEl;
4499
4500     },
4501     getButtonContainer : function() {
4502         
4503          return Roo.bootstrap.version == 4 ?
4504             this.el.select('.modal-footer',true).first()
4505             : this.el.select('.modal-footer div',true).first();
4506
4507     },
4508     initEvents : function()
4509     {
4510         if (this.allow_close) {
4511             this.closeEl.on('click', this.hide, this);
4512         }
4513         Roo.EventManager.onWindowResize(this.resize, this, true);
4514         if (this.editableTitle) {
4515             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4516             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4517             this.headerEditEl.on('keyup', function(e) {
4518                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4519                         this.toggleHeaderInput(false)
4520                     }
4521                 }, this);
4522             this.headerEditEl.on('blur', function(e) {
4523                 this.toggleHeaderInput(false)
4524             },this);
4525         }
4526
4527     },
4528   
4529
4530     resize : function()
4531     {
4532         this.maskEl.setSize(
4533             Roo.lib.Dom.getViewWidth(true),
4534             Roo.lib.Dom.getViewHeight(true)
4535         );
4536         
4537         if (this.fitwindow) {
4538             
4539            this.dialogEl.setStyle( { 'max-width' : '100%' });
4540             this.setSize(
4541                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4542                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4543             );
4544             return;
4545         }
4546         
4547         if(this.max_width !== 0) {
4548             
4549             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4550             
4551             if(this.height) {
4552                 this.setSize(w, this.height);
4553                 return;
4554             }
4555             
4556             if(this.max_height) {
4557                 this.setSize(w,Math.min(
4558                     this.max_height,
4559                     Roo.lib.Dom.getViewportHeight(true) - 60
4560                 ));
4561                 
4562                 return;
4563             }
4564             
4565             if(!this.fit_content) {
4566                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4567                 return;
4568             }
4569             
4570             this.setSize(w, Math.min(
4571                 60 +
4572                 this.headerEl.getHeight() + 
4573                 this.footerEl.getHeight() + 
4574                 this.getChildHeight(this.bodyEl.dom.childNodes),
4575                 Roo.lib.Dom.getViewportHeight(true) - 60)
4576             );
4577         }
4578         
4579     },
4580
4581     setSize : function(w,h)
4582     {
4583         if (!w && !h) {
4584             return;
4585         }
4586         
4587         this.resizeTo(w,h);
4588     },
4589
4590     show : function() {
4591
4592         if (!this.rendered) {
4593             this.render();
4594         }
4595         this.toggleHeaderInput(false);
4596         //this.el.setStyle('display', 'block');
4597         this.el.removeClass('hideing');
4598         this.el.dom.style.display='block';
4599         
4600         Roo.get(document.body).addClass('modal-open');
4601  
4602         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4603             
4604             (function(){
4605                 this.el.addClass('show');
4606                 this.el.addClass('in');
4607             }).defer(50, this);
4608         }else{
4609             this.el.addClass('show');
4610             this.el.addClass('in');
4611         }
4612
4613         // not sure how we can show data in here..
4614         //if (this.tmpl) {
4615         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4616         //}
4617
4618         Roo.get(document.body).addClass("x-body-masked");
4619         
4620         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4621         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4622         this.maskEl.dom.style.display = 'block';
4623         this.maskEl.addClass('show');
4624         
4625         
4626         this.resize();
4627         
4628         this.fireEvent('show', this);
4629
4630         // set zindex here - otherwise it appears to be ignored...
4631         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4632
4633         (function () {
4634             this.items.forEach( function(e) {
4635                 e.layout ? e.layout() : false;
4636
4637             });
4638         }).defer(100,this);
4639
4640     },
4641     hide : function()
4642     {
4643         if(this.fireEvent("beforehide", this) !== false){
4644             
4645             this.maskEl.removeClass('show');
4646             
4647             this.maskEl.dom.style.display = '';
4648             Roo.get(document.body).removeClass("x-body-masked");
4649             this.el.removeClass('in');
4650             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4651
4652             if(this.animate){ // why
4653                 this.el.addClass('hideing');
4654                 this.el.removeClass('show');
4655                 (function(){
4656                     if (!this.el.hasClass('hideing')) {
4657                         return; // it's been shown again...
4658                     }
4659                     
4660                     this.el.dom.style.display='';
4661
4662                     Roo.get(document.body).removeClass('modal-open');
4663                     this.el.removeClass('hideing');
4664                 }).defer(150,this);
4665                 
4666             }else{
4667                 this.el.removeClass('show');
4668                 this.el.dom.style.display='';
4669                 Roo.get(document.body).removeClass('modal-open');
4670
4671             }
4672             this.fireEvent('hide', this);
4673         }
4674     },
4675     isVisible : function()
4676     {
4677         
4678         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4679         
4680     },
4681
4682     addButton : function(str, cb)
4683     {
4684
4685
4686         var b = Roo.apply({}, { html : str } );
4687         b.xns = b.xns || Roo.bootstrap;
4688         b.xtype = b.xtype || 'Button';
4689         if (typeof(b.listeners) == 'undefined') {
4690             b.listeners = { click : cb.createDelegate(this)  };
4691         }
4692
4693         var btn = Roo.factory(b);
4694
4695         btn.render(this.getButtonContainer());
4696
4697         return btn;
4698
4699     },
4700
4701     setDefaultButton : function(btn)
4702     {
4703         //this.el.select('.modal-footer').()
4704     },
4705
4706     resizeTo: function(w,h)
4707     {
4708         this.dialogEl.setWidth(w);
4709         
4710         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4711
4712         this.bodyEl.setHeight(h - diff);
4713         
4714         this.fireEvent('resize', this);
4715     },
4716     
4717     setContentSize  : function(w, h)
4718     {
4719
4720     },
4721     onButtonClick: function(btn,e)
4722     {
4723         //Roo.log([a,b,c]);
4724         this.fireEvent('btnclick', btn.name, e);
4725     },
4726      /**
4727      * Set the title of the Dialog
4728      * @param {String} str new Title
4729      */
4730     setTitle: function(str) {
4731         this.titleEl.dom.innerHTML = str;
4732         this.title = str;
4733     },
4734     /**
4735      * Set the body of the Dialog
4736      * @param {String} str new Title
4737      */
4738     setBody: function(str) {
4739         this.bodyEl.dom.innerHTML = str;
4740     },
4741     /**
4742      * Set the body of the Dialog using the template
4743      * @param {Obj} data - apply this data to the template and replace the body contents.
4744      */
4745     applyBody: function(obj)
4746     {
4747         if (!this.tmpl) {
4748             Roo.log("Error - using apply Body without a template");
4749             //code
4750         }
4751         this.tmpl.overwrite(this.bodyEl, obj);
4752     },
4753     
4754     getChildHeight : function(child_nodes)
4755     {
4756         if(
4757             !child_nodes ||
4758             child_nodes.length == 0
4759         ) {
4760             return 0;
4761         }
4762         
4763         var child_height = 0;
4764         
4765         for(var i = 0; i < child_nodes.length; i++) {
4766             
4767             /*
4768             * for modal with tabs...
4769             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4770                 
4771                 var layout_childs = child_nodes[i].childNodes;
4772                 
4773                 for(var j = 0; j < layout_childs.length; j++) {
4774                     
4775                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4776                         
4777                         var layout_body_childs = layout_childs[j].childNodes;
4778                         
4779                         for(var k = 0; k < layout_body_childs.length; k++) {
4780                             
4781                             if(layout_body_childs[k].classList.contains('navbar')) {
4782                                 child_height += layout_body_childs[k].offsetHeight;
4783                                 continue;
4784                             }
4785                             
4786                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4787                                 
4788                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4789                                 
4790                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4791                                     
4792                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4793                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4794                                         continue;
4795                                     }
4796                                     
4797                                 }
4798                                 
4799                             }
4800                             
4801                         }
4802                     }
4803                 }
4804                 continue;
4805             }
4806             */
4807             
4808             child_height += child_nodes[i].offsetHeight;
4809             // Roo.log(child_nodes[i].offsetHeight);
4810         }
4811         
4812         return child_height;
4813     },
4814     toggleHeaderInput : function(is_edit)
4815     {
4816         if (!this.editableTitle) {
4817             return; // not editable.
4818         }
4819         if (is_edit && this.is_header_editing) {
4820             return; // already editing..
4821         }
4822         if (is_edit) {
4823     
4824             this.headerEditEl.dom.value = this.title;
4825             this.headerEditEl.removeClass('d-none');
4826             this.headerEditEl.dom.focus();
4827             this.titleEl.addClass('d-none');
4828             
4829             this.is_header_editing = true;
4830             return
4831         }
4832         // flip back to not editing.
4833         this.title = this.headerEditEl.dom.value;
4834         this.headerEditEl.addClass('d-none');
4835         this.titleEl.removeClass('d-none');
4836         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4837         this.is_header_editing = false;
4838         this.fireEvent('titlechanged', this, this.title);
4839     
4840             
4841         
4842     }
4843
4844 });
4845
4846
4847 Roo.apply(Roo.bootstrap.Modal,  {
4848     /**
4849          * Button config that displays a single OK button
4850          * @type Object
4851          */
4852         OK :  [{
4853             name : 'ok',
4854             weight : 'primary',
4855             html : 'OK'
4856         }],
4857         /**
4858          * Button config that displays Yes and No buttons
4859          * @type Object
4860          */
4861         YESNO : [
4862             {
4863                 name  : 'no',
4864                 html : 'No'
4865             },
4866             {
4867                 name  :'yes',
4868                 weight : 'primary',
4869                 html : 'Yes'
4870             }
4871         ],
4872
4873         /**
4874          * Button config that displays OK and Cancel buttons
4875          * @type Object
4876          */
4877         OKCANCEL : [
4878             {
4879                name : 'cancel',
4880                 html : 'Cancel'
4881             },
4882             {
4883                 name : 'ok',
4884                 weight : 'primary',
4885                 html : 'OK'
4886             }
4887         ],
4888         /**
4889          * Button config that displays Yes, No and Cancel buttons
4890          * @type Object
4891          */
4892         YESNOCANCEL : [
4893             {
4894                 name : 'yes',
4895                 weight : 'primary',
4896                 html : 'Yes'
4897             },
4898             {
4899                 name : 'no',
4900                 html : 'No'
4901             },
4902             {
4903                 name : 'cancel',
4904                 html : 'Cancel'
4905             }
4906         ],
4907         
4908         zIndex : 10001
4909 });
4910
4911 /*
4912  * - LGPL
4913  *
4914  * messagebox - can be used as a replace
4915  * 
4916  */
4917 /**
4918  * @class Roo.MessageBox
4919  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4920  * Example usage:
4921  *<pre><code>
4922 // Basic alert:
4923 Roo.Msg.alert('Status', 'Changes saved successfully.');
4924
4925 // Prompt for user data:
4926 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4927     if (btn == 'ok'){
4928         // process text value...
4929     }
4930 });
4931
4932 // Show a dialog using config options:
4933 Roo.Msg.show({
4934    title:'Save Changes?',
4935    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4936    buttons: Roo.Msg.YESNOCANCEL,
4937    fn: processResult,
4938    animEl: 'elId'
4939 });
4940 </code></pre>
4941  * @singleton
4942  */
4943 Roo.bootstrap.MessageBox = function(){
4944     var dlg, opt, mask, waitTimer;
4945     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4946     var buttons, activeTextEl, bwidth;
4947
4948     
4949     // private
4950     var handleButton = function(button){
4951         dlg.hide();
4952         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4953     };
4954
4955     // private
4956     var handleHide = function(){
4957         if(opt && opt.cls){
4958             dlg.el.removeClass(opt.cls);
4959         }
4960         //if(waitTimer){
4961         //    Roo.TaskMgr.stop(waitTimer);
4962         //    waitTimer = null;
4963         //}
4964     };
4965
4966     // private
4967     var updateButtons = function(b){
4968         var width = 0;
4969         if(!b){
4970             buttons["ok"].hide();
4971             buttons["cancel"].hide();
4972             buttons["yes"].hide();
4973             buttons["no"].hide();
4974             dlg.footerEl.hide();
4975             
4976             return width;
4977         }
4978         dlg.footerEl.show();
4979         for(var k in buttons){
4980             if(typeof buttons[k] != "function"){
4981                 if(b[k]){
4982                     buttons[k].show();
4983                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4984                     width += buttons[k].el.getWidth()+15;
4985                 }else{
4986                     buttons[k].hide();
4987                 }
4988             }
4989         }
4990         return width;
4991     };
4992
4993     // private
4994     var handleEsc = function(d, k, e){
4995         if(opt && opt.closable !== false){
4996             dlg.hide();
4997         }
4998         if(e){
4999             e.stopEvent();
5000         }
5001     };
5002
5003     return {
5004         /**
5005          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5006          * @return {Roo.BasicDialog} The BasicDialog element
5007          */
5008         getDialog : function(){
5009            if(!dlg){
5010                 dlg = new Roo.bootstrap.Modal( {
5011                     //draggable: true,
5012                     //resizable:false,
5013                     //constraintoviewport:false,
5014                     //fixedcenter:true,
5015                     //collapsible : false,
5016                     //shim:true,
5017                     //modal: true,
5018                 //    width: 'auto',
5019                   //  height:100,
5020                     //buttonAlign:"center",
5021                     closeClick : function(){
5022                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5023                             handleButton("no");
5024                         }else{
5025                             handleButton("cancel");
5026                         }
5027                     }
5028                 });
5029                 dlg.render();
5030                 dlg.on("hide", handleHide);
5031                 mask = dlg.mask;
5032                 //dlg.addKeyListener(27, handleEsc);
5033                 buttons = {};
5034                 this.buttons = buttons;
5035                 var bt = this.buttonText;
5036                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5037                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5038                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5039                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5040                 //Roo.log(buttons);
5041                 bodyEl = dlg.bodyEl.createChild({
5042
5043                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5044                         '<textarea class="roo-mb-textarea"></textarea>' +
5045                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5046                 });
5047                 msgEl = bodyEl.dom.firstChild;
5048                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5049                 textboxEl.enableDisplayMode();
5050                 textboxEl.addKeyListener([10,13], function(){
5051                     if(dlg.isVisible() && opt && opt.buttons){
5052                         if(opt.buttons.ok){
5053                             handleButton("ok");
5054                         }else if(opt.buttons.yes){
5055                             handleButton("yes");
5056                         }
5057                     }
5058                 });
5059                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5060                 textareaEl.enableDisplayMode();
5061                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5062                 progressEl.enableDisplayMode();
5063                 
5064                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5065                 var pf = progressEl.dom.firstChild;
5066                 if (pf) {
5067                     pp = Roo.get(pf.firstChild);
5068                     pp.setHeight(pf.offsetHeight);
5069                 }
5070                 
5071             }
5072             return dlg;
5073         },
5074
5075         /**
5076          * Updates the message box body text
5077          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5078          * the XHTML-compliant non-breaking space character '&amp;#160;')
5079          * @return {Roo.MessageBox} This message box
5080          */
5081         updateText : function(text)
5082         {
5083             if(!dlg.isVisible() && !opt.width){
5084                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5085                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5086             }
5087             msgEl.innerHTML = text || '&#160;';
5088       
5089             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5090             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5091             var w = Math.max(
5092                     Math.min(opt.width || cw , this.maxWidth), 
5093                     Math.max(opt.minWidth || this.minWidth, bwidth)
5094             );
5095             if(opt.prompt){
5096                 activeTextEl.setWidth(w);
5097             }
5098             if(dlg.isVisible()){
5099                 dlg.fixedcenter = false;
5100             }
5101             // to big, make it scroll. = But as usual stupid IE does not support
5102             // !important..
5103             
5104             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5105                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5106                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5107             } else {
5108                 bodyEl.dom.style.height = '';
5109                 bodyEl.dom.style.overflowY = '';
5110             }
5111             if (cw > w) {
5112                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5113             } else {
5114                 bodyEl.dom.style.overflowX = '';
5115             }
5116             
5117             dlg.setContentSize(w, bodyEl.getHeight());
5118             if(dlg.isVisible()){
5119                 dlg.fixedcenter = true;
5120             }
5121             return this;
5122         },
5123
5124         /**
5125          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5126          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5127          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5128          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5129          * @return {Roo.MessageBox} This message box
5130          */
5131         updateProgress : function(value, text){
5132             if(text){
5133                 this.updateText(text);
5134             }
5135             
5136             if (pp) { // weird bug on my firefox - for some reason this is not defined
5137                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5138                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5139             }
5140             return this;
5141         },        
5142
5143         /**
5144          * Returns true if the message box is currently displayed
5145          * @return {Boolean} True if the message box is visible, else false
5146          */
5147         isVisible : function(){
5148             return dlg && dlg.isVisible();  
5149         },
5150
5151         /**
5152          * Hides the message box if it is displayed
5153          */
5154         hide : function(){
5155             if(this.isVisible()){
5156                 dlg.hide();
5157             }  
5158         },
5159
5160         /**
5161          * Displays a new message box, or reinitializes an existing message box, based on the config options
5162          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5163          * The following config object properties are supported:
5164          * <pre>
5165 Property    Type             Description
5166 ----------  ---------------  ------------------------------------------------------------------------------------
5167 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5168                                    closes (defaults to undefined)
5169 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5170                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5171 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5172                                    progress and wait dialogs will ignore this property and always hide the
5173                                    close button as they can only be closed programmatically.
5174 cls               String           A custom CSS class to apply to the message box element
5175 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5176                                    displayed (defaults to 75)
5177 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5178                                    function will be btn (the name of the button that was clicked, if applicable,
5179                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5180                                    Progress and wait dialogs will ignore this option since they do not respond to
5181                                    user actions and can only be closed programmatically, so any required function
5182                                    should be called by the same code after it closes the dialog.
5183 icon              String           A CSS class that provides a background image to be used as an icon for
5184                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5185 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5186 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5187 modal             Boolean          False to allow user interaction with the page while the message box is
5188                                    displayed (defaults to true)
5189 msg               String           A string that will replace the existing message box body text (defaults
5190                                    to the XHTML-compliant non-breaking space character '&#160;')
5191 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5192 progress          Boolean          True to display a progress bar (defaults to false)
5193 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5194 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5195 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5196 title             String           The title text
5197 value             String           The string value to set into the active textbox element if displayed
5198 wait              Boolean          True to display a progress bar (defaults to false)
5199 width             Number           The width of the dialog in pixels
5200 </pre>
5201          *
5202          * Example usage:
5203          * <pre><code>
5204 Roo.Msg.show({
5205    title: 'Address',
5206    msg: 'Please enter your address:',
5207    width: 300,
5208    buttons: Roo.MessageBox.OKCANCEL,
5209    multiline: true,
5210    fn: saveAddress,
5211    animEl: 'addAddressBtn'
5212 });
5213 </code></pre>
5214          * @param {Object} config Configuration options
5215          * @return {Roo.MessageBox} This message box
5216          */
5217         show : function(options)
5218         {
5219             
5220             // this causes nightmares if you show one dialog after another
5221             // especially on callbacks..
5222              
5223             if(this.isVisible()){
5224                 
5225                 this.hide();
5226                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5227                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5228                 Roo.log("New Dialog Message:" +  options.msg )
5229                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5230                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5231                 
5232             }
5233             var d = this.getDialog();
5234             opt = options;
5235             d.setTitle(opt.title || "&#160;");
5236             d.closeEl.setDisplayed(opt.closable !== false);
5237             activeTextEl = textboxEl;
5238             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5239             if(opt.prompt){
5240                 if(opt.multiline){
5241                     textboxEl.hide();
5242                     textareaEl.show();
5243                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5244                         opt.multiline : this.defaultTextHeight);
5245                     activeTextEl = textareaEl;
5246                 }else{
5247                     textboxEl.show();
5248                     textareaEl.hide();
5249                 }
5250             }else{
5251                 textboxEl.hide();
5252                 textareaEl.hide();
5253             }
5254             progressEl.setDisplayed(opt.progress === true);
5255             if (opt.progress) {
5256                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5257             }
5258             this.updateProgress(0);
5259             activeTextEl.dom.value = opt.value || "";
5260             if(opt.prompt){
5261                 dlg.setDefaultButton(activeTextEl);
5262             }else{
5263                 var bs = opt.buttons;
5264                 var db = null;
5265                 if(bs && bs.ok){
5266                     db = buttons["ok"];
5267                 }else if(bs && bs.yes){
5268                     db = buttons["yes"];
5269                 }
5270                 dlg.setDefaultButton(db);
5271             }
5272             bwidth = updateButtons(opt.buttons);
5273             this.updateText(opt.msg);
5274             if(opt.cls){
5275                 d.el.addClass(opt.cls);
5276             }
5277             d.proxyDrag = opt.proxyDrag === true;
5278             d.modal = opt.modal !== false;
5279             d.mask = opt.modal !== false ? mask : false;
5280             if(!d.isVisible()){
5281                 // force it to the end of the z-index stack so it gets a cursor in FF
5282                 document.body.appendChild(dlg.el.dom);
5283                 d.animateTarget = null;
5284                 d.show(options.animEl);
5285             }
5286             return this;
5287         },
5288
5289         /**
5290          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5291          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5292          * and closing the message box when the process is complete.
5293          * @param {String} title The title bar text
5294          * @param {String} msg The message box body text
5295          * @return {Roo.MessageBox} This message box
5296          */
5297         progress : function(title, msg){
5298             this.show({
5299                 title : title,
5300                 msg : msg,
5301                 buttons: false,
5302                 progress:true,
5303                 closable:false,
5304                 minWidth: this.minProgressWidth,
5305                 modal : true
5306             });
5307             return this;
5308         },
5309
5310         /**
5311          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5312          * If a callback function is passed it will be called after the user clicks the button, and the
5313          * id of the button that was clicked will be passed as the only parameter to the callback
5314          * (could also be the top-right close button).
5315          * @param {String} title The title bar text
5316          * @param {String} msg The message box body text
5317          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5318          * @param {Object} scope (optional) The scope of the callback function
5319          * @return {Roo.MessageBox} This message box
5320          */
5321         alert : function(title, msg, fn, scope)
5322         {
5323             this.show({
5324                 title : title,
5325                 msg : msg,
5326                 buttons: this.OK,
5327                 fn: fn,
5328                 closable : false,
5329                 scope : scope,
5330                 modal : true
5331             });
5332             return this;
5333         },
5334
5335         /**
5336          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5337          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5338          * You are responsible for closing the message box when the process is complete.
5339          * @param {String} msg The message box body text
5340          * @param {String} title (optional) The title bar text
5341          * @return {Roo.MessageBox} This message box
5342          */
5343         wait : function(msg, title){
5344             this.show({
5345                 title : title,
5346                 msg : msg,
5347                 buttons: false,
5348                 closable:false,
5349                 progress:true,
5350                 modal:true,
5351                 width:300,
5352                 wait:true
5353             });
5354             waitTimer = Roo.TaskMgr.start({
5355                 run: function(i){
5356                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5357                 },
5358                 interval: 1000
5359             });
5360             return this;
5361         },
5362
5363         /**
5364          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5365          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5366          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5367          * @param {String} title The title bar text
5368          * @param {String} msg The message box body text
5369          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5370          * @param {Object} scope (optional) The scope of the callback function
5371          * @return {Roo.MessageBox} This message box
5372          */
5373         confirm : function(title, msg, fn, scope){
5374             this.show({
5375                 title : title,
5376                 msg : msg,
5377                 buttons: this.YESNO,
5378                 fn: fn,
5379                 scope : scope,
5380                 modal : true
5381             });
5382             return this;
5383         },
5384
5385         /**
5386          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5387          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5388          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5389          * (could also be the top-right close button) and the text that was entered will be passed as the two
5390          * parameters to the callback.
5391          * @param {String} title The title bar text
5392          * @param {String} msg The message box body text
5393          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5394          * @param {Object} scope (optional) The scope of the callback function
5395          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5396          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5397          * @return {Roo.MessageBox} This message box
5398          */
5399         prompt : function(title, msg, fn, scope, multiline){
5400             this.show({
5401                 title : title,
5402                 msg : msg,
5403                 buttons: this.OKCANCEL,
5404                 fn: fn,
5405                 minWidth:250,
5406                 scope : scope,
5407                 prompt:true,
5408                 multiline: multiline,
5409                 modal : true
5410             });
5411             return this;
5412         },
5413
5414         /**
5415          * Button config that displays a single OK button
5416          * @type Object
5417          */
5418         OK : {ok:true},
5419         /**
5420          * Button config that displays Yes and No buttons
5421          * @type Object
5422          */
5423         YESNO : {yes:true, no:true},
5424         /**
5425          * Button config that displays OK and Cancel buttons
5426          * @type Object
5427          */
5428         OKCANCEL : {ok:true, cancel:true},
5429         /**
5430          * Button config that displays Yes, No and Cancel buttons
5431          * @type Object
5432          */
5433         YESNOCANCEL : {yes:true, no:true, cancel:true},
5434
5435         /**
5436          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5437          * @type Number
5438          */
5439         defaultTextHeight : 75,
5440         /**
5441          * The maximum width in pixels of the message box (defaults to 600)
5442          * @type Number
5443          */
5444         maxWidth : 600,
5445         /**
5446          * The minimum width in pixels of the message box (defaults to 100)
5447          * @type Number
5448          */
5449         minWidth : 100,
5450         /**
5451          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5452          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5453          * @type Number
5454          */
5455         minProgressWidth : 250,
5456         /**
5457          * An object containing the default button text strings that can be overriden for localized language support.
5458          * Supported properties are: ok, cancel, yes and no.
5459          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5460          * @type Object
5461          */
5462         buttonText : {
5463             ok : "OK",
5464             cancel : "Cancel",
5465             yes : "Yes",
5466             no : "No"
5467         }
5468     };
5469 }();
5470
5471 /**
5472  * Shorthand for {@link Roo.MessageBox}
5473  */
5474 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5475 Roo.Msg = Roo.Msg || Roo.MessageBox;
5476 /*
5477  * - LGPL
5478  *
5479  * navbar
5480  * 
5481  */
5482
5483 /**
5484  * @class Roo.bootstrap.Navbar
5485  * @extends Roo.bootstrap.Component
5486  * Bootstrap Navbar class
5487
5488  * @constructor
5489  * Create a new Navbar
5490  * @param {Object} config The config object
5491  */
5492
5493
5494 Roo.bootstrap.Navbar = function(config){
5495     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5496     this.addEvents({
5497         // raw events
5498         /**
5499          * @event beforetoggle
5500          * Fire before toggle the menu
5501          * @param {Roo.EventObject} e
5502          */
5503         "beforetoggle" : true
5504     });
5505 };
5506
5507 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5508     
5509     
5510    
5511     // private
5512     navItems : false,
5513     loadMask : false,
5514     
5515     
5516     getAutoCreate : function(){
5517         
5518         
5519         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5520         
5521     },
5522     
5523     initEvents :function ()
5524     {
5525         //Roo.log(this.el.select('.navbar-toggle',true));
5526         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5527         
5528         var mark = {
5529             tag: "div",
5530             cls:"x-dlg-mask"
5531         };
5532         
5533         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5534         
5535         var size = this.el.getSize();
5536         this.maskEl.setSize(size.width, size.height);
5537         this.maskEl.enableDisplayMode("block");
5538         this.maskEl.hide();
5539         
5540         if(this.loadMask){
5541             this.maskEl.show();
5542         }
5543     },
5544     
5545     
5546     getChildContainer : function()
5547     {
5548         if (this.el && this.el.select('.collapse').getCount()) {
5549             return this.el.select('.collapse',true).first();
5550         }
5551         
5552         return this.el;
5553     },
5554     
5555     mask : function()
5556     {
5557         this.maskEl.show();
5558     },
5559     
5560     unmask : function()
5561     {
5562         this.maskEl.hide();
5563     },
5564     onToggle : function()
5565     {
5566         
5567         if(this.fireEvent('beforetoggle', this) === false){
5568             return;
5569         }
5570         var ce = this.el.select('.navbar-collapse',true).first();
5571       
5572         if (!ce.hasClass('show')) {
5573            this.expand();
5574         } else {
5575             this.collapse();
5576         }
5577         
5578         
5579     
5580     },
5581     /**
5582      * Expand the navbar pulldown 
5583      */
5584     expand : function ()
5585     {
5586        
5587         var ce = this.el.select('.navbar-collapse',true).first();
5588         if (ce.hasClass('collapsing')) {
5589             return;
5590         }
5591         ce.dom.style.height = '';
5592                // show it...
5593         ce.addClass('in'); // old...
5594         ce.removeClass('collapse');
5595         ce.addClass('show');
5596         var h = ce.getHeight();
5597         Roo.log(h);
5598         ce.removeClass('show');
5599         // at this point we should be able to see it..
5600         ce.addClass('collapsing');
5601         
5602         ce.setHeight(0); // resize it ...
5603         ce.on('transitionend', function() {
5604             //Roo.log('done transition');
5605             ce.removeClass('collapsing');
5606             ce.addClass('show');
5607             ce.removeClass('collapse');
5608
5609             ce.dom.style.height = '';
5610         }, this, { single: true} );
5611         ce.setHeight(h);
5612         ce.dom.scrollTop = 0;
5613     },
5614     /**
5615      * Collapse the navbar pulldown 
5616      */
5617     collapse : function()
5618     {
5619          var ce = this.el.select('.navbar-collapse',true).first();
5620        
5621         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5622             // it's collapsed or collapsing..
5623             return;
5624         }
5625         ce.removeClass('in'); // old...
5626         ce.setHeight(ce.getHeight());
5627         ce.removeClass('show');
5628         ce.addClass('collapsing');
5629         
5630         ce.on('transitionend', function() {
5631             ce.dom.style.height = '';
5632             ce.removeClass('collapsing');
5633             ce.addClass('collapse');
5634         }, this, { single: true} );
5635         ce.setHeight(0);
5636     }
5637     
5638     
5639     
5640 });
5641
5642
5643
5644  
5645
5646  /*
5647  * - LGPL
5648  *
5649  * navbar
5650  * 
5651  */
5652
5653 /**
5654  * @class Roo.bootstrap.NavSimplebar
5655  * @extends Roo.bootstrap.Navbar
5656  * Bootstrap Sidebar class
5657  *
5658  * @cfg {Boolean} inverse is inverted color
5659  * 
5660  * @cfg {String} type (nav | pills | tabs)
5661  * @cfg {Boolean} arrangement stacked | justified
5662  * @cfg {String} align (left | right) alignment
5663  * 
5664  * @cfg {Boolean} main (true|false) main nav bar? default false
5665  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5666  * 
5667  * @cfg {String} tag (header|footer|nav|div) default is nav 
5668
5669  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5670  * 
5671  * 
5672  * @constructor
5673  * Create a new Sidebar
5674  * @param {Object} config The config object
5675  */
5676
5677
5678 Roo.bootstrap.NavSimplebar = function(config){
5679     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5680 };
5681
5682 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5683     
5684     inverse: false,
5685     
5686     type: false,
5687     arrangement: '',
5688     align : false,
5689     
5690     weight : 'light',
5691     
5692     main : false,
5693     
5694     
5695     tag : false,
5696     
5697     
5698     getAutoCreate : function(){
5699         
5700         
5701         var cfg = {
5702             tag : this.tag || 'div',
5703             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5704         };
5705         if (['light','white'].indexOf(this.weight) > -1) {
5706             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5707         }
5708         cfg.cls += ' bg-' + this.weight;
5709         
5710         if (this.inverse) {
5711             cfg.cls += ' navbar-inverse';
5712             
5713         }
5714         
5715         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5716         
5717         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5718             return cfg;
5719         }
5720         
5721         
5722     
5723         
5724         cfg.cn = [
5725             {
5726                 cls: 'nav nav-' + this.xtype,
5727                 tag : 'ul'
5728             }
5729         ];
5730         
5731          
5732         this.type = this.type || 'nav';
5733         if (['tabs','pills'].indexOf(this.type) != -1) {
5734             cfg.cn[0].cls += ' nav-' + this.type
5735         
5736         
5737         } else {
5738             if (this.type!=='nav') {
5739                 Roo.log('nav type must be nav/tabs/pills')
5740             }
5741             cfg.cn[0].cls += ' navbar-nav'
5742         }
5743         
5744         
5745         
5746         
5747         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5748             cfg.cn[0].cls += ' nav-' + this.arrangement;
5749         }
5750         
5751         
5752         if (this.align === 'right') {
5753             cfg.cn[0].cls += ' navbar-right';
5754         }
5755         
5756         
5757         
5758         
5759         return cfg;
5760     
5761         
5762     }
5763     
5764     
5765     
5766 });
5767
5768
5769
5770  
5771
5772  
5773        /*
5774  * - LGPL
5775  *
5776  * navbar
5777  * navbar-fixed-top
5778  * navbar-expand-md  fixed-top 
5779  */
5780
5781 /**
5782  * @class Roo.bootstrap.NavHeaderbar
5783  * @extends Roo.bootstrap.NavSimplebar
5784  * Bootstrap Sidebar class
5785  *
5786  * @cfg {String} brand what is brand
5787  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5788  * @cfg {String} brand_href href of the brand
5789  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5790  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5791  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5792  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5793  * 
5794  * @constructor
5795  * Create a new Sidebar
5796  * @param {Object} config The config object
5797  */
5798
5799
5800 Roo.bootstrap.NavHeaderbar = function(config){
5801     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5802       
5803 };
5804
5805 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5806     
5807     position: '',
5808     brand: '',
5809     brand_href: false,
5810     srButton : true,
5811     autohide : false,
5812     desktopCenter : false,
5813    
5814     
5815     getAutoCreate : function(){
5816         
5817         var   cfg = {
5818             tag: this.nav || 'nav',
5819             cls: 'navbar navbar-expand-md',
5820             role: 'navigation',
5821             cn: []
5822         };
5823         
5824         var cn = cfg.cn;
5825         if (this.desktopCenter) {
5826             cn.push({cls : 'container', cn : []});
5827             cn = cn[0].cn;
5828         }
5829         
5830         if(this.srButton){
5831             var btn = {
5832                 tag: 'button',
5833                 type: 'button',
5834                 cls: 'navbar-toggle navbar-toggler',
5835                 'data-toggle': 'collapse',
5836                 cn: [
5837                     {
5838                         tag: 'span',
5839                         cls: 'sr-only',
5840                         html: 'Toggle navigation'
5841                     },
5842                     {
5843                         tag: 'span',
5844                         cls: 'icon-bar navbar-toggler-icon'
5845                     },
5846                     {
5847                         tag: 'span',
5848                         cls: 'icon-bar'
5849                     },
5850                     {
5851                         tag: 'span',
5852                         cls: 'icon-bar'
5853                     }
5854                 ]
5855             };
5856             
5857             cn.push( Roo.bootstrap.version == 4 ? btn : {
5858                 tag: 'div',
5859                 cls: 'navbar-header',
5860                 cn: [
5861                     btn
5862                 ]
5863             });
5864         }
5865         
5866         cn.push({
5867             tag: 'div',
5868             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5869             cn : []
5870         });
5871         
5872         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5873         
5874         if (['light','white'].indexOf(this.weight) > -1) {
5875             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5876         }
5877         cfg.cls += ' bg-' + this.weight;
5878         
5879         
5880         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5881             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5882             
5883             // tag can override this..
5884             
5885             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5886         }
5887         
5888         if (this.brand !== '') {
5889             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5890             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5891                 tag: 'a',
5892                 href: this.brand_href ? this.brand_href : '#',
5893                 cls: 'navbar-brand',
5894                 cn: [
5895                 this.brand
5896                 ]
5897             });
5898         }
5899         
5900         if(this.main){
5901             cfg.cls += ' main-nav';
5902         }
5903         
5904         
5905         return cfg;
5906
5907         
5908     },
5909     getHeaderChildContainer : function()
5910     {
5911         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5912             return this.el.select('.navbar-header',true).first();
5913         }
5914         
5915         return this.getChildContainer();
5916     },
5917     
5918     getChildContainer : function()
5919     {
5920          
5921         return this.el.select('.roo-navbar-collapse',true).first();
5922          
5923         
5924     },
5925     
5926     initEvents : function()
5927     {
5928         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5929         
5930         if (this.autohide) {
5931             
5932             var prevScroll = 0;
5933             var ft = this.el;
5934             
5935             Roo.get(document).on('scroll',function(e) {
5936                 var ns = Roo.get(document).getScroll().top;
5937                 var os = prevScroll;
5938                 prevScroll = ns;
5939                 
5940                 if(ns > os){
5941                     ft.removeClass('slideDown');
5942                     ft.addClass('slideUp');
5943                     return;
5944                 }
5945                 ft.removeClass('slideUp');
5946                 ft.addClass('slideDown');
5947                  
5948               
5949           },this);
5950         }
5951     }    
5952     
5953 });
5954
5955
5956
5957  
5958
5959  /*
5960  * - LGPL
5961  *
5962  * navbar
5963  * 
5964  */
5965
5966 /**
5967  * @class Roo.bootstrap.NavSidebar
5968  * @extends Roo.bootstrap.Navbar
5969  * Bootstrap Sidebar class
5970  * 
5971  * @constructor
5972  * Create a new Sidebar
5973  * @param {Object} config The config object
5974  */
5975
5976
5977 Roo.bootstrap.NavSidebar = function(config){
5978     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5979 };
5980
5981 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5982     
5983     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5984     
5985     getAutoCreate : function(){
5986         
5987         
5988         return  {
5989             tag: 'div',
5990             cls: 'sidebar sidebar-nav'
5991         };
5992     
5993         
5994     }
5995     
5996     
5997     
5998 });
5999
6000
6001
6002  
6003
6004  /*
6005  * - LGPL
6006  *
6007  * nav group
6008  * 
6009  */
6010
6011 /**
6012  * @class Roo.bootstrap.NavGroup
6013  * @extends Roo.bootstrap.Component
6014  * Bootstrap NavGroup class
6015  * @cfg {String} align (left|right)
6016  * @cfg {Boolean} inverse
6017  * @cfg {String} type (nav|pills|tab) default nav
6018  * @cfg {String} navId - reference Id for navbar.
6019  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6020  * 
6021  * @constructor
6022  * Create a new nav group
6023  * @param {Object} config The config object
6024  */
6025
6026 Roo.bootstrap.NavGroup = function(config){
6027     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6028     this.navItems = [];
6029    
6030     Roo.bootstrap.NavGroup.register(this);
6031      this.addEvents({
6032         /**
6033              * @event changed
6034              * Fires when the active item changes
6035              * @param {Roo.bootstrap.NavGroup} this
6036              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6037              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6038          */
6039         'changed': true
6040      });
6041     
6042 };
6043
6044 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6045     
6046     align: '',
6047     inverse: false,
6048     form: false,
6049     type: 'nav',
6050     navId : '',
6051     // private
6052     pilltype : true,
6053     
6054     navItems : false, 
6055     
6056     getAutoCreate : function()
6057     {
6058         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6059         
6060         cfg = {
6061             tag : 'ul',
6062             cls: 'nav' 
6063         };
6064         if (Roo.bootstrap.version == 4) {
6065             if (['tabs','pills'].indexOf(this.type) != -1) {
6066                 cfg.cls += ' nav-' + this.type; 
6067             } else {
6068                 // trying to remove so header bar can right align top?
6069                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6070                     // do not use on header bar... 
6071                     cfg.cls += ' navbar-nav';
6072                 }
6073             }
6074             
6075         } else {
6076             if (['tabs','pills'].indexOf(this.type) != -1) {
6077                 cfg.cls += ' nav-' + this.type
6078             } else {
6079                 if (this.type !== 'nav') {
6080                     Roo.log('nav type must be nav/tabs/pills')
6081                 }
6082                 cfg.cls += ' navbar-nav'
6083             }
6084         }
6085         
6086         if (this.parent() && this.parent().sidebar) {
6087             cfg = {
6088                 tag: 'ul',
6089                 cls: 'dashboard-menu sidebar-menu'
6090             };
6091             
6092             return cfg;
6093         }
6094         
6095         if (this.form === true) {
6096             cfg = {
6097                 tag: 'form',
6098                 cls: 'navbar-form form-inline'
6099             };
6100             //nav navbar-right ml-md-auto
6101             if (this.align === 'right') {
6102                 cfg.cls += ' navbar-right ml-md-auto';
6103             } else {
6104                 cfg.cls += ' navbar-left';
6105             }
6106         }
6107         
6108         if (this.align === 'right') {
6109             cfg.cls += ' navbar-right ml-md-auto';
6110         } else {
6111             cfg.cls += ' mr-auto';
6112         }
6113         
6114         if (this.inverse) {
6115             cfg.cls += ' navbar-inverse';
6116             
6117         }
6118         
6119         
6120         return cfg;
6121     },
6122     /**
6123     * sets the active Navigation item
6124     * @param {Roo.bootstrap.NavItem} the new current navitem
6125     */
6126     setActiveItem : function(item)
6127     {
6128         var prev = false;
6129         Roo.each(this.navItems, function(v){
6130             if (v == item) {
6131                 return ;
6132             }
6133             if (v.isActive()) {
6134                 v.setActive(false, true);
6135                 prev = v;
6136                 
6137             }
6138             
6139         });
6140
6141         item.setActive(true, true);
6142         this.fireEvent('changed', this, item, prev);
6143         
6144         
6145     },
6146     /**
6147     * gets the active Navigation item
6148     * @return {Roo.bootstrap.NavItem} the current navitem
6149     */
6150     getActive : function()
6151     {
6152         
6153         var prev = false;
6154         Roo.each(this.navItems, function(v){
6155             
6156             if (v.isActive()) {
6157                 prev = v;
6158                 
6159             }
6160             
6161         });
6162         return prev;
6163     },
6164     
6165     indexOfNav : function()
6166     {
6167         
6168         var prev = false;
6169         Roo.each(this.navItems, function(v,i){
6170             
6171             if (v.isActive()) {
6172                 prev = i;
6173                 
6174             }
6175             
6176         });
6177         return prev;
6178     },
6179     /**
6180     * adds a Navigation item
6181     * @param {Roo.bootstrap.NavItem} the navitem to add
6182     */
6183     addItem : function(cfg)
6184     {
6185         if (this.form && Roo.bootstrap.version == 4) {
6186             cfg.tag = 'div';
6187         }
6188         var cn = new Roo.bootstrap.NavItem(cfg);
6189         this.register(cn);
6190         cn.parentId = this.id;
6191         cn.onRender(this.el, null);
6192         return cn;
6193     },
6194     /**
6195     * register a Navigation item
6196     * @param {Roo.bootstrap.NavItem} the navitem to add
6197     */
6198     register : function(item)
6199     {
6200         this.navItems.push( item);
6201         item.navId = this.navId;
6202     
6203     },
6204     
6205     /**
6206     * clear all the Navigation item
6207     */
6208    
6209     clearAll : function()
6210     {
6211         this.navItems = [];
6212         this.el.dom.innerHTML = '';
6213     },
6214     
6215     getNavItem: function(tabId)
6216     {
6217         var ret = false;
6218         Roo.each(this.navItems, function(e) {
6219             if (e.tabId == tabId) {
6220                ret =  e;
6221                return false;
6222             }
6223             return true;
6224             
6225         });
6226         return ret;
6227     },
6228     
6229     setActiveNext : function()
6230     {
6231         var i = this.indexOfNav(this.getActive());
6232         if (i > this.navItems.length) {
6233             return;
6234         }
6235         this.setActiveItem(this.navItems[i+1]);
6236     },
6237     setActivePrev : function()
6238     {
6239         var i = this.indexOfNav(this.getActive());
6240         if (i  < 1) {
6241             return;
6242         }
6243         this.setActiveItem(this.navItems[i-1]);
6244     },
6245     clearWasActive : function(except) {
6246         Roo.each(this.navItems, function(e) {
6247             if (e.tabId != except.tabId && e.was_active) {
6248                e.was_active = false;
6249                return false;
6250             }
6251             return true;
6252             
6253         });
6254     },
6255     getWasActive : function ()
6256     {
6257         var r = false;
6258         Roo.each(this.navItems, function(e) {
6259             if (e.was_active) {
6260                r = e;
6261                return false;
6262             }
6263             return true;
6264             
6265         });
6266         return r;
6267     }
6268     
6269     
6270 });
6271
6272  
6273 Roo.apply(Roo.bootstrap.NavGroup, {
6274     
6275     groups: {},
6276      /**
6277     * register a Navigation Group
6278     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6279     */
6280     register : function(navgrp)
6281     {
6282         this.groups[navgrp.navId] = navgrp;
6283         
6284     },
6285     /**
6286     * fetch a Navigation Group based on the navigation ID
6287     * @param {string} the navgroup to add
6288     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6289     */
6290     get: function(navId) {
6291         if (typeof(this.groups[navId]) == 'undefined') {
6292             return false;
6293             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6294         }
6295         return this.groups[navId] ;
6296     }
6297     
6298     
6299     
6300 });
6301
6302  /*
6303  * - LGPL
6304  *
6305  * row
6306  * 
6307  */
6308
6309 /**
6310  * @class Roo.bootstrap.NavItem
6311  * @extends Roo.bootstrap.Component
6312  * Bootstrap Navbar.NavItem class
6313  * @cfg {String} href  link to
6314  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6315  * @cfg {Boolean} button_outline show and outlined button
6316  * @cfg {String} html content of button
6317  * @cfg {String} badge text inside badge
6318  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6319  * @cfg {String} glyphicon DEPRICATED - use fa
6320  * @cfg {String} icon DEPRICATED - use fa
6321  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6322  * @cfg {Boolean} active Is item active
6323  * @cfg {Boolean} disabled Is item disabled
6324  * @cfg {String} linkcls  Link Class
6325  * @cfg {Boolean} preventDefault (true | false) default false
6326  * @cfg {String} tabId the tab that this item activates.
6327  * @cfg {String} tagtype (a|span) render as a href or span?
6328  * @cfg {Boolean} animateRef (true|false) link to element default false  
6329   
6330  * @constructor
6331  * Create a new Navbar Item
6332  * @param {Object} config The config object
6333  */
6334 Roo.bootstrap.NavItem = function(config){
6335     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6336     this.addEvents({
6337         // raw events
6338         /**
6339          * @event click
6340          * The raw click event for the entire grid.
6341          * @param {Roo.EventObject} e
6342          */
6343         "click" : true,
6344          /**
6345             * @event changed
6346             * Fires when the active item active state changes
6347             * @param {Roo.bootstrap.NavItem} this
6348             * @param {boolean} state the new state
6349              
6350          */
6351         'changed': true,
6352         /**
6353             * @event scrollto
6354             * Fires when scroll to element
6355             * @param {Roo.bootstrap.NavItem} this
6356             * @param {Object} options
6357             * @param {Roo.EventObject} e
6358              
6359          */
6360         'scrollto': true
6361     });
6362    
6363 };
6364
6365 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6366     
6367     href: false,
6368     html: '',
6369     badge: '',
6370     icon: false,
6371     fa : false,
6372     glyphicon: false,
6373     active: false,
6374     preventDefault : false,
6375     tabId : false,
6376     tagtype : 'a',
6377     tag: 'li',
6378     disabled : false,
6379     animateRef : false,
6380     was_active : false,
6381     button_weight : '',
6382     button_outline : false,
6383     linkcls : '',
6384     navLink: false,
6385     
6386     getAutoCreate : function(){
6387          
6388         var cfg = {
6389             tag: this.tag,
6390             cls: 'nav-item'
6391         };
6392         
6393         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6394         
6395         if (this.active) {
6396             cfg.cls +=  ' active' ;
6397         }
6398         if (this.disabled) {
6399             cfg.cls += ' disabled';
6400         }
6401         
6402         // BS4 only?
6403         if (this.button_weight.length) {
6404             cfg.tag = this.href ? 'a' : 'button';
6405             cfg.html = this.html || '';
6406             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6407             if (this.href) {
6408                 cfg.href = this.href;
6409             }
6410             if (this.fa) {
6411                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6412             } else {
6413                 cfg.cls += " nav-html";
6414             }
6415             
6416             // menu .. should add dropdown-menu class - so no need for carat..
6417             
6418             if (this.badge !== '') {
6419                  
6420                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6421             }
6422             return cfg;
6423         }
6424         
6425         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6426             cfg.cn = [
6427                 {
6428                     tag: this.tagtype,
6429                     href : this.href || "#",
6430                     html: this.html || '',
6431                     cls : ''
6432                 }
6433             ];
6434             if (this.tagtype == 'a') {
6435                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6436         
6437             }
6438             if (this.icon) {
6439                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6440             } else  if (this.fa) {
6441                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6442             } else if(this.glyphicon) {
6443                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6444             } else {
6445                 cfg.cn[0].cls += " nav-html";
6446             }
6447             
6448             if (this.menu) {
6449                 cfg.cn[0].html += " <span class='caret'></span>";
6450              
6451             }
6452             
6453             if (this.badge !== '') {
6454                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6455             }
6456         }
6457         
6458         
6459         
6460         return cfg;
6461     },
6462     onRender : function(ct, position)
6463     {
6464        // Roo.log("Call onRender: " + this.xtype);
6465         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6466             this.tag = 'div';
6467         }
6468         
6469         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6470         this.navLink = this.el.select('.nav-link',true).first();
6471         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6472         return ret;
6473     },
6474       
6475     
6476     initEvents: function() 
6477     {
6478         if (typeof (this.menu) != 'undefined') {
6479             this.menu.parentType = this.xtype;
6480             this.menu.triggerEl = this.el;
6481             this.menu = this.addxtype(Roo.apply({}, this.menu));
6482         }
6483         
6484         this.el.on('click', this.onClick, this);
6485         
6486         //if(this.tagtype == 'span'){
6487         //    this.el.select('span',true).on('click', this.onClick, this);
6488         //}
6489        
6490         // at this point parent should be available..
6491         this.parent().register(this);
6492     },
6493     
6494     onClick : function(e)
6495     {
6496         if (e.getTarget('.dropdown-menu-item')) {
6497             // did you click on a menu itemm.... - then don't trigger onclick..
6498             return;
6499         }
6500         
6501         if(
6502                 this.preventDefault || 
6503                 this.href == '#' 
6504         ){
6505             Roo.log("NavItem - prevent Default?");
6506             e.preventDefault();
6507         }
6508         
6509         if (this.disabled) {
6510             return;
6511         }
6512         
6513         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6514         if (tg && tg.transition) {
6515             Roo.log("waiting for the transitionend");
6516             return;
6517         }
6518         
6519         
6520         
6521         //Roo.log("fire event clicked");
6522         if(this.fireEvent('click', this, e) === false){
6523             return;
6524         };
6525         
6526         if(this.tagtype == 'span'){
6527             return;
6528         }
6529         
6530         //Roo.log(this.href);
6531         var ael = this.el.select('a',true).first();
6532         //Roo.log(ael);
6533         
6534         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6535             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6536             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6537                 return; // ignore... - it's a 'hash' to another page.
6538             }
6539             Roo.log("NavItem - prevent Default?");
6540             e.preventDefault();
6541             this.scrollToElement(e);
6542         }
6543         
6544         
6545         var p =  this.parent();
6546    
6547         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6548             if (typeof(p.setActiveItem) !== 'undefined') {
6549                 p.setActiveItem(this);
6550             }
6551         }
6552         
6553         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6554         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6555             // remove the collapsed menu expand...
6556             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6557         }
6558     },
6559     
6560     isActive: function () {
6561         return this.active
6562     },
6563     setActive : function(state, fire, is_was_active)
6564     {
6565         if (this.active && !state && this.navId) {
6566             this.was_active = true;
6567             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6568             if (nv) {
6569                 nv.clearWasActive(this);
6570             }
6571             
6572         }
6573         this.active = state;
6574         
6575         if (!state ) {
6576             this.el.removeClass('active');
6577             this.navLink ? this.navLink.removeClass('active') : false;
6578         } else if (!this.el.hasClass('active')) {
6579             
6580             this.el.addClass('active');
6581             if (Roo.bootstrap.version == 4 && this.navLink ) {
6582                 this.navLink.addClass('active');
6583             }
6584             
6585         }
6586         if (fire) {
6587             this.fireEvent('changed', this, state);
6588         }
6589         
6590         // show a panel if it's registered and related..
6591         
6592         if (!this.navId || !this.tabId || !state || is_was_active) {
6593             return;
6594         }
6595         
6596         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6597         if (!tg) {
6598             return;
6599         }
6600         var pan = tg.getPanelByName(this.tabId);
6601         if (!pan) {
6602             return;
6603         }
6604         // if we can not flip to new panel - go back to old nav highlight..
6605         if (false == tg.showPanel(pan)) {
6606             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6607             if (nv) {
6608                 var onav = nv.getWasActive();
6609                 if (onav) {
6610                     onav.setActive(true, false, true);
6611                 }
6612             }
6613             
6614         }
6615         
6616         
6617         
6618     },
6619      // this should not be here...
6620     setDisabled : function(state)
6621     {
6622         this.disabled = state;
6623         if (!state ) {
6624             this.el.removeClass('disabled');
6625         } else if (!this.el.hasClass('disabled')) {
6626             this.el.addClass('disabled');
6627         }
6628         
6629     },
6630     
6631     /**
6632      * Fetch the element to display the tooltip on.
6633      * @return {Roo.Element} defaults to this.el
6634      */
6635     tooltipEl : function()
6636     {
6637         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6638     },
6639     
6640     scrollToElement : function(e)
6641     {
6642         var c = document.body;
6643         
6644         /*
6645          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6646          */
6647         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6648             c = document.documentElement;
6649         }
6650         
6651         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6652         
6653         if(!target){
6654             return;
6655         }
6656
6657         var o = target.calcOffsetsTo(c);
6658         
6659         var options = {
6660             target : target,
6661             value : o[1]
6662         };
6663         
6664         this.fireEvent('scrollto', this, options, e);
6665         
6666         Roo.get(c).scrollTo('top', options.value, true);
6667         
6668         return;
6669     },
6670     /**
6671      * Set the HTML (text content) of the item
6672      * @param {string} html  content for the nav item
6673      */
6674     setHtml : function(html)
6675     {
6676         this.html = html;
6677         this.htmlEl.dom.innerHTML = html;
6678         
6679     } 
6680 });
6681  
6682
6683  /*
6684  * - LGPL
6685  *
6686  * sidebar item
6687  *
6688  *  li
6689  *    <span> icon </span>
6690  *    <span> text </span>
6691  *    <span>badge </span>
6692  */
6693
6694 /**
6695  * @class Roo.bootstrap.NavSidebarItem
6696  * @extends Roo.bootstrap.NavItem
6697  * Bootstrap Navbar.NavSidebarItem class
6698  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6699  * {Boolean} open is the menu open
6700  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6701  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6702  * {String} buttonSize (sm|md|lg)the extra classes for the button
6703  * {Boolean} showArrow show arrow next to the text (default true)
6704  * @constructor
6705  * Create a new Navbar Button
6706  * @param {Object} config The config object
6707  */
6708 Roo.bootstrap.NavSidebarItem = function(config){
6709     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6710     this.addEvents({
6711         // raw events
6712         /**
6713          * @event click
6714          * The raw click event for the entire grid.
6715          * @param {Roo.EventObject} e
6716          */
6717         "click" : true,
6718          /**
6719             * @event changed
6720             * Fires when the active item active state changes
6721             * @param {Roo.bootstrap.NavSidebarItem} this
6722             * @param {boolean} state the new state
6723              
6724          */
6725         'changed': true
6726     });
6727    
6728 };
6729
6730 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6731     
6732     badgeWeight : 'default',
6733     
6734     open: false,
6735     
6736     buttonView : false,
6737     
6738     buttonWeight : 'default',
6739     
6740     buttonSize : 'md',
6741     
6742     showArrow : true,
6743     
6744     getAutoCreate : function(){
6745         
6746         
6747         var a = {
6748                 tag: 'a',
6749                 href : this.href || '#',
6750                 cls: '',
6751                 html : '',
6752                 cn : []
6753         };
6754         
6755         if(this.buttonView){
6756             a = {
6757                 tag: 'button',
6758                 href : this.href || '#',
6759                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6760                 html : this.html,
6761                 cn : []
6762             };
6763         }
6764         
6765         var cfg = {
6766             tag: 'li',
6767             cls: '',
6768             cn: [ a ]
6769         };
6770         
6771         if (this.active) {
6772             cfg.cls += ' active';
6773         }
6774         
6775         if (this.disabled) {
6776             cfg.cls += ' disabled';
6777         }
6778         if (this.open) {
6779             cfg.cls += ' open x-open';
6780         }
6781         // left icon..
6782         if (this.glyphicon || this.icon) {
6783             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6784             a.cn.push({ tag : 'i', cls : c }) ;
6785         }
6786         
6787         if(!this.buttonView){
6788             var span = {
6789                 tag: 'span',
6790                 html : this.html || ''
6791             };
6792
6793             a.cn.push(span);
6794             
6795         }
6796         
6797         if (this.badge !== '') {
6798             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6799         }
6800         
6801         if (this.menu) {
6802             
6803             if(this.showArrow){
6804                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6805             }
6806             
6807             a.cls += ' dropdown-toggle treeview' ;
6808         }
6809         
6810         return cfg;
6811     },
6812     
6813     initEvents : function()
6814     { 
6815         if (typeof (this.menu) != 'undefined') {
6816             this.menu.parentType = this.xtype;
6817             this.menu.triggerEl = this.el;
6818             this.menu = this.addxtype(Roo.apply({}, this.menu));
6819         }
6820         
6821         this.el.on('click', this.onClick, this);
6822         
6823         if(this.badge !== ''){
6824             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6825         }
6826         
6827     },
6828     
6829     onClick : function(e)
6830     {
6831         if(this.disabled){
6832             e.preventDefault();
6833             return;
6834         }
6835         
6836         if(this.preventDefault){
6837             e.preventDefault();
6838         }
6839         
6840         this.fireEvent('click', this, e);
6841     },
6842     
6843     disable : function()
6844     {
6845         this.setDisabled(true);
6846     },
6847     
6848     enable : function()
6849     {
6850         this.setDisabled(false);
6851     },
6852     
6853     setDisabled : function(state)
6854     {
6855         if(this.disabled == state){
6856             return;
6857         }
6858         
6859         this.disabled = state;
6860         
6861         if (state) {
6862             this.el.addClass('disabled');
6863             return;
6864         }
6865         
6866         this.el.removeClass('disabled');
6867         
6868         return;
6869     },
6870     
6871     setActive : function(state)
6872     {
6873         if(this.active == state){
6874             return;
6875         }
6876         
6877         this.active = state;
6878         
6879         if (state) {
6880             this.el.addClass('active');
6881             return;
6882         }
6883         
6884         this.el.removeClass('active');
6885         
6886         return;
6887     },
6888     
6889     isActive: function () 
6890     {
6891         return this.active;
6892     },
6893     
6894     setBadge : function(str)
6895     {
6896         if(!this.badgeEl){
6897             return;
6898         }
6899         
6900         this.badgeEl.dom.innerHTML = str;
6901     }
6902     
6903    
6904      
6905  
6906 });
6907  
6908
6909  /*
6910  * - LGPL
6911  *
6912  *  Breadcrumb Nav
6913  * 
6914  */
6915 Roo.namespace('Roo.bootstrap.breadcrumb');
6916
6917
6918 /**
6919  * @class Roo.bootstrap.breadcrumb.Nav
6920  * @extends Roo.bootstrap.Component
6921  * Bootstrap Breadcrumb Nav Class
6922  *  
6923  * @children Roo.bootstrap.breadcrumb.Item
6924  * 
6925  * @constructor
6926  * Create a new breadcrumb.Nav
6927  * @param {Object} config The config object
6928  */
6929
6930
6931 Roo.bootstrap.breadcrumb.Nav = function(config){
6932     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6933     
6934     
6935 };
6936
6937 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6938     
6939     getAutoCreate : function()
6940     {
6941
6942         var cfg = {
6943             tag: 'nav',
6944             cn : [
6945                 {
6946                     tag : 'ol',
6947                     cls : 'breadcrumb'
6948                 }
6949             ]
6950             
6951         };
6952           
6953         return cfg;
6954     },
6955     
6956     initEvents: function()
6957     {
6958         this.olEl = this.el.select('ol',true).first();    
6959     },
6960     getChildContainer : function()
6961     {
6962         return this.olEl;  
6963     }
6964     
6965 });
6966
6967  /*
6968  * - LGPL
6969  *
6970  *  Breadcrumb Item
6971  * 
6972  */
6973
6974
6975 /**
6976  * @class Roo.bootstrap.breadcrumb.Nav
6977  * @extends Roo.bootstrap.Component
6978  * Bootstrap Breadcrumb Nav Class
6979  *  
6980  * @children Roo.bootstrap.breadcrumb.Component
6981  * @cfg {String} html the content of the link.
6982  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6983  * @cfg {Boolean} active is it active
6984
6985  * 
6986  * @constructor
6987  * Create a new breadcrumb.Nav
6988  * @param {Object} config The config object
6989  */
6990
6991 Roo.bootstrap.breadcrumb.Item = function(config){
6992     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6993     this.addEvents({
6994         // img events
6995         /**
6996          * @event click
6997          * The img click event for the img.
6998          * @param {Roo.EventObject} e
6999          */
7000         "click" : true
7001     });
7002     
7003 };
7004
7005 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7006     
7007     href: false,
7008     html : '',
7009     
7010     getAutoCreate : function()
7011     {
7012
7013         var cfg = {
7014             tag: 'li',
7015             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7016         };
7017         if (this.href !== false) {
7018             cfg.cn = [{
7019                 tag : 'a',
7020                 href : this.href,
7021                 html : this.html
7022             }];
7023         } else {
7024             cfg.html = this.html;
7025         }
7026         
7027         return cfg;
7028     },
7029     
7030     initEvents: function()
7031     {
7032         if (this.href) {
7033             this.el.select('a', true).first().on('click',this.onClick, this)
7034         }
7035         
7036     },
7037     onClick : function(e)
7038     {
7039         e.preventDefault();
7040         this.fireEvent('click',this,  e);
7041     }
7042     
7043 });
7044
7045  /*
7046  * - LGPL
7047  *
7048  * row
7049  * 
7050  */
7051
7052 /**
7053  * @class Roo.bootstrap.Row
7054  * @extends Roo.bootstrap.Component
7055  * Bootstrap Row class (contains columns...)
7056  * 
7057  * @constructor
7058  * Create a new Row
7059  * @param {Object} config The config object
7060  */
7061
7062 Roo.bootstrap.Row = function(config){
7063     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7064 };
7065
7066 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7067     
7068     getAutoCreate : function(){
7069        return {
7070             cls: 'row clearfix'
7071        };
7072     }
7073     
7074     
7075 });
7076
7077  
7078
7079  /*
7080  * - LGPL
7081  *
7082  * pagination
7083  * 
7084  */
7085
7086 /**
7087  * @class Roo.bootstrap.Pagination
7088  * @extends Roo.bootstrap.Component
7089  * Bootstrap Pagination class
7090  * @cfg {String} size xs | sm | md | lg
7091  * @cfg {Boolean} inverse false | true
7092  * 
7093  * @constructor
7094  * Create a new Pagination
7095  * @param {Object} config The config object
7096  */
7097
7098 Roo.bootstrap.Pagination = function(config){
7099     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7100 };
7101
7102 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7103     
7104     cls: false,
7105     size: false,
7106     inverse: false,
7107     
7108     getAutoCreate : function(){
7109         var cfg = {
7110             tag: 'ul',
7111                 cls: 'pagination'
7112         };
7113         if (this.inverse) {
7114             cfg.cls += ' inverse';
7115         }
7116         if (this.html) {
7117             cfg.html=this.html;
7118         }
7119         if (this.cls) {
7120             cfg.cls += " " + this.cls;
7121         }
7122         return cfg;
7123     }
7124    
7125 });
7126
7127  
7128
7129  /*
7130  * - LGPL
7131  *
7132  * Pagination item
7133  * 
7134  */
7135
7136
7137 /**
7138  * @class Roo.bootstrap.PaginationItem
7139  * @extends Roo.bootstrap.Component
7140  * Bootstrap PaginationItem class
7141  * @cfg {String} html text
7142  * @cfg {String} href the link
7143  * @cfg {Boolean} preventDefault (true | false) default true
7144  * @cfg {Boolean} active (true | false) default false
7145  * @cfg {Boolean} disabled default false
7146  * 
7147  * 
7148  * @constructor
7149  * Create a new PaginationItem
7150  * @param {Object} config The config object
7151  */
7152
7153
7154 Roo.bootstrap.PaginationItem = function(config){
7155     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7156     this.addEvents({
7157         // raw events
7158         /**
7159          * @event click
7160          * The raw click event for the entire grid.
7161          * @param {Roo.EventObject} e
7162          */
7163         "click" : true
7164     });
7165 };
7166
7167 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7168     
7169     href : false,
7170     html : false,
7171     preventDefault: true,
7172     active : false,
7173     cls : false,
7174     disabled: false,
7175     
7176     getAutoCreate : function(){
7177         var cfg= {
7178             tag: 'li',
7179             cn: [
7180                 {
7181                     tag : 'a',
7182                     href : this.href ? this.href : '#',
7183                     html : this.html ? this.html : ''
7184                 }
7185             ]
7186         };
7187         
7188         if(this.cls){
7189             cfg.cls = this.cls;
7190         }
7191         
7192         if(this.disabled){
7193             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7194         }
7195         
7196         if(this.active){
7197             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7198         }
7199         
7200         return cfg;
7201     },
7202     
7203     initEvents: function() {
7204         
7205         this.el.on('click', this.onClick, this);
7206         
7207     },
7208     onClick : function(e)
7209     {
7210         Roo.log('PaginationItem on click ');
7211         if(this.preventDefault){
7212             e.preventDefault();
7213         }
7214         
7215         if(this.disabled){
7216             return;
7217         }
7218         
7219         this.fireEvent('click', this, e);
7220     }
7221    
7222 });
7223
7224  
7225
7226  /*
7227  * - LGPL
7228  *
7229  * slider
7230  * 
7231  */
7232
7233
7234 /**
7235  * @class Roo.bootstrap.Slider
7236  * @extends Roo.bootstrap.Component
7237  * Bootstrap Slider class
7238  *    
7239  * @constructor
7240  * Create a new Slider
7241  * @param {Object} config The config object
7242  */
7243
7244 Roo.bootstrap.Slider = function(config){
7245     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7246 };
7247
7248 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7249     
7250     getAutoCreate : function(){
7251         
7252         var cfg = {
7253             tag: 'div',
7254             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7255             cn: [
7256                 {
7257                     tag: 'a',
7258                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7259                 }
7260             ]
7261         };
7262         
7263         return cfg;
7264     }
7265    
7266 });
7267
7268  /*
7269  * Based on:
7270  * Ext JS Library 1.1.1
7271  * Copyright(c) 2006-2007, Ext JS, LLC.
7272  *
7273  * Originally Released Under LGPL - original licence link has changed is not relivant.
7274  *
7275  * Fork - LGPL
7276  * <script type="text/javascript">
7277  */
7278  
7279
7280 /**
7281  * @class Roo.grid.ColumnModel
7282  * @extends Roo.util.Observable
7283  * This is the default implementation of a ColumnModel used by the Grid. It defines
7284  * the columns in the grid.
7285  * <br>Usage:<br>
7286  <pre><code>
7287  var colModel = new Roo.grid.ColumnModel([
7288         {header: "Ticker", width: 60, sortable: true, locked: true},
7289         {header: "Company Name", width: 150, sortable: true},
7290         {header: "Market Cap.", width: 100, sortable: true},
7291         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7292         {header: "Employees", width: 100, sortable: true, resizable: false}
7293  ]);
7294  </code></pre>
7295  * <p>
7296  
7297  * The config options listed for this class are options which may appear in each
7298  * individual column definition.
7299  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7300  * @constructor
7301  * @param {Object} config An Array of column config objects. See this class's
7302  * config objects for details.
7303 */
7304 Roo.grid.ColumnModel = function(config){
7305         /**
7306      * The config passed into the constructor
7307      */
7308     this.config = config;
7309     this.lookup = {};
7310
7311     // if no id, create one
7312     // if the column does not have a dataIndex mapping,
7313     // map it to the order it is in the config
7314     for(var i = 0, len = config.length; i < len; i++){
7315         var c = config[i];
7316         if(typeof c.dataIndex == "undefined"){
7317             c.dataIndex = i;
7318         }
7319         if(typeof c.renderer == "string"){
7320             c.renderer = Roo.util.Format[c.renderer];
7321         }
7322         if(typeof c.id == "undefined"){
7323             c.id = Roo.id();
7324         }
7325         if(c.editor && c.editor.xtype){
7326             c.editor  = Roo.factory(c.editor, Roo.grid);
7327         }
7328         if(c.editor && c.editor.isFormField){
7329             c.editor = new Roo.grid.GridEditor(c.editor);
7330         }
7331         this.lookup[c.id] = c;
7332     }
7333
7334     /**
7335      * The width of columns which have no width specified (defaults to 100)
7336      * @type Number
7337      */
7338     this.defaultWidth = 100;
7339
7340     /**
7341      * Default sortable of columns which have no sortable specified (defaults to false)
7342      * @type Boolean
7343      */
7344     this.defaultSortable = false;
7345
7346     this.addEvents({
7347         /**
7348              * @event widthchange
7349              * Fires when the width of a column changes.
7350              * @param {ColumnModel} this
7351              * @param {Number} columnIndex The column index
7352              * @param {Number} newWidth The new width
7353              */
7354             "widthchange": true,
7355         /**
7356              * @event headerchange
7357              * Fires when the text of a header changes.
7358              * @param {ColumnModel} this
7359              * @param {Number} columnIndex The column index
7360              * @param {Number} newText The new header text
7361              */
7362             "headerchange": true,
7363         /**
7364              * @event hiddenchange
7365              * Fires when a column is hidden or "unhidden".
7366              * @param {ColumnModel} this
7367              * @param {Number} columnIndex The column index
7368              * @param {Boolean} hidden true if hidden, false otherwise
7369              */
7370             "hiddenchange": true,
7371             /**
7372          * @event columnmoved
7373          * Fires when a column is moved.
7374          * @param {ColumnModel} this
7375          * @param {Number} oldIndex
7376          * @param {Number} newIndex
7377          */
7378         "columnmoved" : true,
7379         /**
7380          * @event columlockchange
7381          * Fires when a column's locked state is changed
7382          * @param {ColumnModel} this
7383          * @param {Number} colIndex
7384          * @param {Boolean} locked true if locked
7385          */
7386         "columnlockchange" : true
7387     });
7388     Roo.grid.ColumnModel.superclass.constructor.call(this);
7389 };
7390 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7391     /**
7392      * @cfg {String} header The header text to display in the Grid view.
7393      */
7394     /**
7395      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7396      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7397      * specified, the column's index is used as an index into the Record's data Array.
7398      */
7399     /**
7400      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7401      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7402      */
7403     /**
7404      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7405      * Defaults to the value of the {@link #defaultSortable} property.
7406      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7407      */
7408     /**
7409      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7410      */
7411     /**
7412      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7413      */
7414     /**
7415      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7416      */
7417     /**
7418      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7419      */
7420     /**
7421      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7422      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7423      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7424      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7425      */
7426        /**
7427      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7428      */
7429     /**
7430      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7431      */
7432     /**
7433      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7434      */
7435     /**
7436      * @cfg {String} cursor (Optional)
7437      */
7438     /**
7439      * @cfg {String} tooltip (Optional)
7440      */
7441     /**
7442      * @cfg {Number} xs (Optional)
7443      */
7444     /**
7445      * @cfg {Number} sm (Optional)
7446      */
7447     /**
7448      * @cfg {Number} md (Optional)
7449      */
7450     /**
7451      * @cfg {Number} lg (Optional)
7452      */
7453     /**
7454      * Returns the id of the column at the specified index.
7455      * @param {Number} index The column index
7456      * @return {String} the id
7457      */
7458     getColumnId : function(index){
7459         return this.config[index].id;
7460     },
7461
7462     /**
7463      * Returns the column for a specified id.
7464      * @param {String} id The column id
7465      * @return {Object} the column
7466      */
7467     getColumnById : function(id){
7468         return this.lookup[id];
7469     },
7470
7471     
7472     /**
7473      * Returns the column for a specified dataIndex.
7474      * @param {String} dataIndex The column dataIndex
7475      * @return {Object|Boolean} the column or false if not found
7476      */
7477     getColumnByDataIndex: function(dataIndex){
7478         var index = this.findColumnIndex(dataIndex);
7479         return index > -1 ? this.config[index] : false;
7480     },
7481     
7482     /**
7483      * Returns the index for a specified column id.
7484      * @param {String} id The column id
7485      * @return {Number} the index, or -1 if not found
7486      */
7487     getIndexById : function(id){
7488         for(var i = 0, len = this.config.length; i < len; i++){
7489             if(this.config[i].id == id){
7490                 return i;
7491             }
7492         }
7493         return -1;
7494     },
7495     
7496     /**
7497      * Returns the index for a specified column dataIndex.
7498      * @param {String} dataIndex The column dataIndex
7499      * @return {Number} the index, or -1 if not found
7500      */
7501     
7502     findColumnIndex : function(dataIndex){
7503         for(var i = 0, len = this.config.length; i < len; i++){
7504             if(this.config[i].dataIndex == dataIndex){
7505                 return i;
7506             }
7507         }
7508         return -1;
7509     },
7510     
7511     
7512     moveColumn : function(oldIndex, newIndex){
7513         var c = this.config[oldIndex];
7514         this.config.splice(oldIndex, 1);
7515         this.config.splice(newIndex, 0, c);
7516         this.dataMap = null;
7517         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7518     },
7519
7520     isLocked : function(colIndex){
7521         return this.config[colIndex].locked === true;
7522     },
7523
7524     setLocked : function(colIndex, value, suppressEvent){
7525         if(this.isLocked(colIndex) == value){
7526             return;
7527         }
7528         this.config[colIndex].locked = value;
7529         if(!suppressEvent){
7530             this.fireEvent("columnlockchange", this, colIndex, value);
7531         }
7532     },
7533
7534     getTotalLockedWidth : function(){
7535         var totalWidth = 0;
7536         for(var i = 0; i < this.config.length; i++){
7537             if(this.isLocked(i) && !this.isHidden(i)){
7538                 this.totalWidth += this.getColumnWidth(i);
7539             }
7540         }
7541         return totalWidth;
7542     },
7543
7544     getLockedCount : function(){
7545         for(var i = 0, len = this.config.length; i < len; i++){
7546             if(!this.isLocked(i)){
7547                 return i;
7548             }
7549         }
7550         
7551         return this.config.length;
7552     },
7553
7554     /**
7555      * Returns the number of columns.
7556      * @return {Number}
7557      */
7558     getColumnCount : function(visibleOnly){
7559         if(visibleOnly === true){
7560             var c = 0;
7561             for(var i = 0, len = this.config.length; i < len; i++){
7562                 if(!this.isHidden(i)){
7563                     c++;
7564                 }
7565             }
7566             return c;
7567         }
7568         return this.config.length;
7569     },
7570
7571     /**
7572      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7573      * @param {Function} fn
7574      * @param {Object} scope (optional)
7575      * @return {Array} result
7576      */
7577     getColumnsBy : function(fn, scope){
7578         var r = [];
7579         for(var i = 0, len = this.config.length; i < len; i++){
7580             var c = this.config[i];
7581             if(fn.call(scope||this, c, i) === true){
7582                 r[r.length] = c;
7583             }
7584         }
7585         return r;
7586     },
7587
7588     /**
7589      * Returns true if the specified column is sortable.
7590      * @param {Number} col The column index
7591      * @return {Boolean}
7592      */
7593     isSortable : function(col){
7594         if(typeof this.config[col].sortable == "undefined"){
7595             return this.defaultSortable;
7596         }
7597         return this.config[col].sortable;
7598     },
7599
7600     /**
7601      * Returns the rendering (formatting) function defined for the column.
7602      * @param {Number} col The column index.
7603      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7604      */
7605     getRenderer : function(col){
7606         if(!this.config[col].renderer){
7607             return Roo.grid.ColumnModel.defaultRenderer;
7608         }
7609         return this.config[col].renderer;
7610     },
7611
7612     /**
7613      * Sets the rendering (formatting) function for a column.
7614      * @param {Number} col The column index
7615      * @param {Function} fn The function to use to process the cell's raw data
7616      * to return HTML markup for the grid view. The render function is called with
7617      * the following parameters:<ul>
7618      * <li>Data value.</li>
7619      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7620      * <li>css A CSS style string to apply to the table cell.</li>
7621      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7622      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7623      * <li>Row index</li>
7624      * <li>Column index</li>
7625      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7626      */
7627     setRenderer : function(col, fn){
7628         this.config[col].renderer = fn;
7629     },
7630
7631     /**
7632      * Returns the width for the specified column.
7633      * @param {Number} col The column index
7634      * @return {Number}
7635      */
7636     getColumnWidth : function(col){
7637         return this.config[col].width * 1 || this.defaultWidth;
7638     },
7639
7640     /**
7641      * Sets the width for a column.
7642      * @param {Number} col The column index
7643      * @param {Number} width The new width
7644      */
7645     setColumnWidth : function(col, width, suppressEvent){
7646         this.config[col].width = width;
7647         this.totalWidth = null;
7648         if(!suppressEvent){
7649              this.fireEvent("widthchange", this, col, width);
7650         }
7651     },
7652
7653     /**
7654      * Returns the total width of all columns.
7655      * @param {Boolean} includeHidden True to include hidden column widths
7656      * @return {Number}
7657      */
7658     getTotalWidth : function(includeHidden){
7659         if(!this.totalWidth){
7660             this.totalWidth = 0;
7661             for(var i = 0, len = this.config.length; i < len; i++){
7662                 if(includeHidden || !this.isHidden(i)){
7663                     this.totalWidth += this.getColumnWidth(i);
7664                 }
7665             }
7666         }
7667         return this.totalWidth;
7668     },
7669
7670     /**
7671      * Returns the header for the specified column.
7672      * @param {Number} col The column index
7673      * @return {String}
7674      */
7675     getColumnHeader : function(col){
7676         return this.config[col].header;
7677     },
7678
7679     /**
7680      * Sets the header for a column.
7681      * @param {Number} col The column index
7682      * @param {String} header The new header
7683      */
7684     setColumnHeader : function(col, header){
7685         this.config[col].header = header;
7686         this.fireEvent("headerchange", this, col, header);
7687     },
7688
7689     /**
7690      * Returns the tooltip for the specified column.
7691      * @param {Number} col The column index
7692      * @return {String}
7693      */
7694     getColumnTooltip : function(col){
7695             return this.config[col].tooltip;
7696     },
7697     /**
7698      * Sets the tooltip for a column.
7699      * @param {Number} col The column index
7700      * @param {String} tooltip The new tooltip
7701      */
7702     setColumnTooltip : function(col, tooltip){
7703             this.config[col].tooltip = tooltip;
7704     },
7705
7706     /**
7707      * Returns the dataIndex for the specified column.
7708      * @param {Number} col The column index
7709      * @return {Number}
7710      */
7711     getDataIndex : function(col){
7712         return this.config[col].dataIndex;
7713     },
7714
7715     /**
7716      * Sets the dataIndex for a column.
7717      * @param {Number} col The column index
7718      * @param {Number} dataIndex The new dataIndex
7719      */
7720     setDataIndex : function(col, dataIndex){
7721         this.config[col].dataIndex = dataIndex;
7722     },
7723
7724     
7725     
7726     /**
7727      * Returns true if the cell is editable.
7728      * @param {Number} colIndex The column index
7729      * @param {Number} rowIndex The row index - this is nto actually used..?
7730      * @return {Boolean}
7731      */
7732     isCellEditable : function(colIndex, rowIndex){
7733         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7734     },
7735
7736     /**
7737      * Returns the editor defined for the cell/column.
7738      * return false or null to disable editing.
7739      * @param {Number} colIndex The column index
7740      * @param {Number} rowIndex The row index
7741      * @return {Object}
7742      */
7743     getCellEditor : function(colIndex, rowIndex){
7744         return this.config[colIndex].editor;
7745     },
7746
7747     /**
7748      * Sets if a column is editable.
7749      * @param {Number} col The column index
7750      * @param {Boolean} editable True if the column is editable
7751      */
7752     setEditable : function(col, editable){
7753         this.config[col].editable = editable;
7754     },
7755
7756
7757     /**
7758      * Returns true if the column is hidden.
7759      * @param {Number} colIndex The column index
7760      * @return {Boolean}
7761      */
7762     isHidden : function(colIndex){
7763         return this.config[colIndex].hidden;
7764     },
7765
7766
7767     /**
7768      * Returns true if the column width cannot be changed
7769      */
7770     isFixed : function(colIndex){
7771         return this.config[colIndex].fixed;
7772     },
7773
7774     /**
7775      * Returns true if the column can be resized
7776      * @return {Boolean}
7777      */
7778     isResizable : function(colIndex){
7779         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7780     },
7781     /**
7782      * Sets if a column is hidden.
7783      * @param {Number} colIndex The column index
7784      * @param {Boolean} hidden True if the column is hidden
7785      */
7786     setHidden : function(colIndex, hidden){
7787         this.config[colIndex].hidden = hidden;
7788         this.totalWidth = null;
7789         this.fireEvent("hiddenchange", this, colIndex, hidden);
7790     },
7791
7792     /**
7793      * Sets the editor for a column.
7794      * @param {Number} col The column index
7795      * @param {Object} editor The editor object
7796      */
7797     setEditor : function(col, editor){
7798         this.config[col].editor = editor;
7799     }
7800 });
7801
7802 Roo.grid.ColumnModel.defaultRenderer = function(value)
7803 {
7804     if(typeof value == "object") {
7805         return value;
7806     }
7807         if(typeof value == "string" && value.length < 1){
7808             return "&#160;";
7809         }
7810     
7811         return String.format("{0}", value);
7812 };
7813
7814 // Alias for backwards compatibility
7815 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7816 /*
7817  * Based on:
7818  * Ext JS Library 1.1.1
7819  * Copyright(c) 2006-2007, Ext JS, LLC.
7820  *
7821  * Originally Released Under LGPL - original licence link has changed is not relivant.
7822  *
7823  * Fork - LGPL
7824  * <script type="text/javascript">
7825  */
7826  
7827 /**
7828  * @class Roo.LoadMask
7829  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7830  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7831  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7832  * element's UpdateManager load indicator and will be destroyed after the initial load.
7833  * @constructor
7834  * Create a new LoadMask
7835  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7836  * @param {Object} config The config object
7837  */
7838 Roo.LoadMask = function(el, config){
7839     this.el = Roo.get(el);
7840     Roo.apply(this, config);
7841     if(this.store){
7842         this.store.on('beforeload', this.onBeforeLoad, this);
7843         this.store.on('load', this.onLoad, this);
7844         this.store.on('loadexception', this.onLoadException, this);
7845         this.removeMask = false;
7846     }else{
7847         var um = this.el.getUpdateManager();
7848         um.showLoadIndicator = false; // disable the default indicator
7849         um.on('beforeupdate', this.onBeforeLoad, this);
7850         um.on('update', this.onLoad, this);
7851         um.on('failure', this.onLoad, this);
7852         this.removeMask = true;
7853     }
7854 };
7855
7856 Roo.LoadMask.prototype = {
7857     /**
7858      * @cfg {Boolean} removeMask
7859      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7860      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7861      */
7862     /**
7863      * @cfg {String} msg
7864      * The text to display in a centered loading message box (defaults to 'Loading...')
7865      */
7866     msg : 'Loading...',
7867     /**
7868      * @cfg {String} msgCls
7869      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7870      */
7871     msgCls : 'x-mask-loading',
7872
7873     /**
7874      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7875      * @type Boolean
7876      */
7877     disabled: false,
7878
7879     /**
7880      * Disables the mask to prevent it from being displayed
7881      */
7882     disable : function(){
7883        this.disabled = true;
7884     },
7885
7886     /**
7887      * Enables the mask so that it can be displayed
7888      */
7889     enable : function(){
7890         this.disabled = false;
7891     },
7892     
7893     onLoadException : function()
7894     {
7895         Roo.log(arguments);
7896         
7897         if (typeof(arguments[3]) != 'undefined') {
7898             Roo.MessageBox.alert("Error loading",arguments[3]);
7899         } 
7900         /*
7901         try {
7902             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7903                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7904             }   
7905         } catch(e) {
7906             
7907         }
7908         */
7909     
7910         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7911     },
7912     // private
7913     onLoad : function()
7914     {
7915         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7916     },
7917
7918     // private
7919     onBeforeLoad : function(){
7920         if(!this.disabled){
7921             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7922         }
7923     },
7924
7925     // private
7926     destroy : function(){
7927         if(this.store){
7928             this.store.un('beforeload', this.onBeforeLoad, this);
7929             this.store.un('load', this.onLoad, this);
7930             this.store.un('loadexception', this.onLoadException, this);
7931         }else{
7932             var um = this.el.getUpdateManager();
7933             um.un('beforeupdate', this.onBeforeLoad, this);
7934             um.un('update', this.onLoad, this);
7935             um.un('failure', this.onLoad, this);
7936         }
7937     }
7938 };/*
7939  * - LGPL
7940  *
7941  * table
7942  * 
7943  */
7944
7945 /**
7946  * @class Roo.bootstrap.Table
7947  * @extends Roo.bootstrap.Component
7948  * Bootstrap Table class
7949  * @cfg {String} cls table class
7950  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7951  * @cfg {String} bgcolor Specifies the background color for a table
7952  * @cfg {Number} border Specifies whether the table cells should have borders or not
7953  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7954  * @cfg {Number} cellspacing Specifies the space between cells
7955  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7956  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7957  * @cfg {String} sortable Specifies that the table should be sortable
7958  * @cfg {String} summary Specifies a summary of the content of a table
7959  * @cfg {Number} width Specifies the width of a table
7960  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7961  * 
7962  * @cfg {boolean} striped Should the rows be alternative striped
7963  * @cfg {boolean} bordered Add borders to the table
7964  * @cfg {boolean} hover Add hover highlighting
7965  * @cfg {boolean} condensed Format condensed
7966  * @cfg {boolean} responsive Format condensed
7967  * @cfg {Boolean} loadMask (true|false) default false
7968  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7969  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7970  * @cfg {Boolean} rowSelection (true|false) default false
7971  * @cfg {Boolean} cellSelection (true|false) default false
7972  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7973  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7974  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7975  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7976  
7977  * 
7978  * @constructor
7979  * Create a new Table
7980  * @param {Object} config The config object
7981  */
7982
7983 Roo.bootstrap.Table = function(config){
7984     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7985     
7986   
7987     
7988     // BC...
7989     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7990     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7991     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7992     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7993     
7994     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7995     if (this.sm) {
7996         this.sm.grid = this;
7997         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7998         this.sm = this.selModel;
7999         this.sm.xmodule = this.xmodule || false;
8000     }
8001     
8002     if (this.cm && typeof(this.cm.config) == 'undefined') {
8003         this.colModel = new Roo.grid.ColumnModel(this.cm);
8004         this.cm = this.colModel;
8005         this.cm.xmodule = this.xmodule || false;
8006     }
8007     if (this.store) {
8008         this.store= Roo.factory(this.store, Roo.data);
8009         this.ds = this.store;
8010         this.ds.xmodule = this.xmodule || false;
8011          
8012     }
8013     if (this.footer && this.store) {
8014         this.footer.dataSource = this.ds;
8015         this.footer = Roo.factory(this.footer);
8016     }
8017     
8018     /** @private */
8019     this.addEvents({
8020         /**
8021          * @event cellclick
8022          * Fires when a cell is clicked
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         "cellclick" : true,
8030         /**
8031          * @event celldblclick
8032          * Fires when a cell is double clicked
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         "celldblclick" : true,
8040         /**
8041          * @event rowclick
8042          * Fires when a row is clicked
8043          * @param {Roo.bootstrap.Table} this
8044          * @param {Roo.Element} el
8045          * @param {Number} rowIndex
8046          * @param {Roo.EventObject} e
8047          */
8048         "rowclick" : true,
8049         /**
8050          * @event rowdblclick
8051          * Fires when a row is double clicked
8052          * @param {Roo.bootstrap.Table} this
8053          * @param {Roo.Element} el
8054          * @param {Number} rowIndex
8055          * @param {Roo.EventObject} e
8056          */
8057         "rowdblclick" : true,
8058         /**
8059          * @event mouseover
8060          * Fires when a mouseover occur
8061          * @param {Roo.bootstrap.Table} this
8062          * @param {Roo.Element} el
8063          * @param {Number} rowIndex
8064          * @param {Number} columnIndex
8065          * @param {Roo.EventObject} e
8066          */
8067         "mouseover" : true,
8068         /**
8069          * @event mouseout
8070          * Fires when a mouseout occur
8071          * @param {Roo.bootstrap.Table} this
8072          * @param {Roo.Element} el
8073          * @param {Number} rowIndex
8074          * @param {Number} columnIndex
8075          * @param {Roo.EventObject} e
8076          */
8077         "mouseout" : true,
8078         /**
8079          * @event rowclass
8080          * Fires when a row is rendered, so you can change add a style to it.
8081          * @param {Roo.bootstrap.Table} this
8082          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8083          */
8084         'rowclass' : true,
8085           /**
8086          * @event rowsrendered
8087          * Fires when all the  rows have been rendered
8088          * @param {Roo.bootstrap.Table} this
8089          */
8090         'rowsrendered' : true,
8091         /**
8092          * @event contextmenu
8093          * The raw contextmenu event for the entire grid.
8094          * @param {Roo.EventObject} e
8095          */
8096         "contextmenu" : true,
8097         /**
8098          * @event rowcontextmenu
8099          * Fires when a row is right clicked
8100          * @param {Roo.bootstrap.Table} this
8101          * @param {Number} rowIndex
8102          * @param {Roo.EventObject} e
8103          */
8104         "rowcontextmenu" : true,
8105         /**
8106          * @event cellcontextmenu
8107          * Fires when a cell is right clicked
8108          * @param {Roo.bootstrap.Table} this
8109          * @param {Number} rowIndex
8110          * @param {Number} cellIndex
8111          * @param {Roo.EventObject} e
8112          */
8113          "cellcontextmenu" : true,
8114          /**
8115          * @event headercontextmenu
8116          * Fires when a header is right clicked
8117          * @param {Roo.bootstrap.Table} this
8118          * @param {Number} columnIndex
8119          * @param {Roo.EventObject} e
8120          */
8121         "headercontextmenu" : true
8122     });
8123 };
8124
8125 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8126     
8127     cls: false,
8128     align: false,
8129     bgcolor: false,
8130     border: false,
8131     cellpadding: false,
8132     cellspacing: false,
8133     frame: false,
8134     rules: false,
8135     sortable: false,
8136     summary: false,
8137     width: false,
8138     striped : false,
8139     scrollBody : false,
8140     bordered: false,
8141     hover:  false,
8142     condensed : false,
8143     responsive : false,
8144     sm : false,
8145     cm : false,
8146     store : false,
8147     loadMask : false,
8148     footerShow : true,
8149     headerShow : true,
8150   
8151     rowSelection : false,
8152     cellSelection : false,
8153     layout : false,
8154     
8155     // Roo.Element - the tbody
8156     mainBody: false,
8157     // Roo.Element - thead element
8158     mainHead: false,
8159     
8160     container: false, // used by gridpanel...
8161     
8162     lazyLoad : false,
8163     
8164     CSS : Roo.util.CSS,
8165     
8166     auto_hide_footer : false,
8167     
8168     getAutoCreate : function()
8169     {
8170         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8171         
8172         cfg = {
8173             tag: 'table',
8174             cls : 'table',
8175             cn : []
8176         };
8177         if (this.scrollBody) {
8178             cfg.cls += ' table-body-fixed';
8179         }    
8180         if (this.striped) {
8181             cfg.cls += ' table-striped';
8182         }
8183         
8184         if (this.hover) {
8185             cfg.cls += ' table-hover';
8186         }
8187         if (this.bordered) {
8188             cfg.cls += ' table-bordered';
8189         }
8190         if (this.condensed) {
8191             cfg.cls += ' table-condensed';
8192         }
8193         if (this.responsive) {
8194             cfg.cls += ' table-responsive';
8195         }
8196         
8197         if (this.cls) {
8198             cfg.cls+=  ' ' +this.cls;
8199         }
8200         
8201         // this lot should be simplifed...
8202         var _t = this;
8203         var cp = [
8204             'align',
8205             'bgcolor',
8206             'border',
8207             'cellpadding',
8208             'cellspacing',
8209             'frame',
8210             'rules',
8211             'sortable',
8212             'summary',
8213             'width'
8214         ].forEach(function(k) {
8215             if (_t[k]) {
8216                 cfg[k] = _t[k];
8217             }
8218         });
8219         
8220         
8221         if (this.layout) {
8222             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8223         }
8224         
8225         if(this.store || this.cm){
8226             if(this.headerShow){
8227                 cfg.cn.push(this.renderHeader());
8228             }
8229             
8230             cfg.cn.push(this.renderBody());
8231             
8232             if(this.footerShow){
8233                 cfg.cn.push(this.renderFooter());
8234             }
8235             // where does this come from?
8236             //cfg.cls+=  ' TableGrid';
8237         }
8238         
8239         return { cn : [ cfg ] };
8240     },
8241     
8242     initEvents : function()
8243     {   
8244         if(!this.store || !this.cm){
8245             return;
8246         }
8247         if (this.selModel) {
8248             this.selModel.initEvents();
8249         }
8250         
8251         
8252         //Roo.log('initEvents with ds!!!!');
8253         
8254         this.mainBody = this.el.select('tbody', true).first();
8255         this.mainHead = this.el.select('thead', true).first();
8256         this.mainFoot = this.el.select('tfoot', true).first();
8257         
8258         
8259         
8260         var _this = this;
8261         
8262         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8263             e.on('click', _this.sort, _this);
8264         });
8265         
8266         this.mainBody.on("click", this.onClick, this);
8267         this.mainBody.on("dblclick", this.onDblClick, this);
8268         
8269         // why is this done????? = it breaks dialogs??
8270         //this.parent().el.setStyle('position', 'relative');
8271         
8272         
8273         if (this.footer) {
8274             this.footer.parentId = this.id;
8275             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8276             
8277             if(this.lazyLoad){
8278                 this.el.select('tfoot tr td').first().addClass('hide');
8279             }
8280         } 
8281         
8282         if(this.loadMask) {
8283             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8284         }
8285         
8286         this.store.on('load', this.onLoad, this);
8287         this.store.on('beforeload', this.onBeforeLoad, this);
8288         this.store.on('update', this.onUpdate, this);
8289         this.store.on('add', this.onAdd, this);
8290         this.store.on("clear", this.clear, this);
8291         
8292         this.el.on("contextmenu", this.onContextMenu, this);
8293         
8294         this.mainBody.on('scroll', this.onBodyScroll, this);
8295         
8296         this.cm.on("headerchange", this.onHeaderChange, this);
8297         
8298         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8299         
8300     },
8301     
8302     onContextMenu : function(e, t)
8303     {
8304         this.processEvent("contextmenu", e);
8305     },
8306     
8307     processEvent : function(name, e)
8308     {
8309         if (name != 'touchstart' ) {
8310             this.fireEvent(name, e);    
8311         }
8312         
8313         var t = e.getTarget();
8314         
8315         var cell = Roo.get(t);
8316         
8317         if(!cell){
8318             return;
8319         }
8320         
8321         if(cell.findParent('tfoot', false, true)){
8322             return;
8323         }
8324         
8325         if(cell.findParent('thead', false, true)){
8326             
8327             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8328                 cell = Roo.get(t).findParent('th', false, true);
8329                 if (!cell) {
8330                     Roo.log("failed to find th in thead?");
8331                     Roo.log(e.getTarget());
8332                     return;
8333                 }
8334             }
8335             
8336             var cellIndex = cell.dom.cellIndex;
8337             
8338             var ename = name == 'touchstart' ? 'click' : name;
8339             this.fireEvent("header" + ename, this, cellIndex, e);
8340             
8341             return;
8342         }
8343         
8344         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8345             cell = Roo.get(t).findParent('td', false, true);
8346             if (!cell) {
8347                 Roo.log("failed to find th in tbody?");
8348                 Roo.log(e.getTarget());
8349                 return;
8350             }
8351         }
8352         
8353         var row = cell.findParent('tr', false, true);
8354         var cellIndex = cell.dom.cellIndex;
8355         var rowIndex = row.dom.rowIndex - 1;
8356         
8357         if(row !== false){
8358             
8359             this.fireEvent("row" + name, this, rowIndex, e);
8360             
8361             if(cell !== false){
8362             
8363                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8364             }
8365         }
8366         
8367     },
8368     
8369     onMouseover : function(e, el)
8370     {
8371         var cell = Roo.get(el);
8372         
8373         if(!cell){
8374             return;
8375         }
8376         
8377         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8378             cell = cell.findParent('td', false, true);
8379         }
8380         
8381         var row = cell.findParent('tr', false, true);
8382         var cellIndex = cell.dom.cellIndex;
8383         var rowIndex = row.dom.rowIndex - 1; // start from 0
8384         
8385         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8386         
8387     },
8388     
8389     onMouseout : function(e, el)
8390     {
8391         var cell = Roo.get(el);
8392         
8393         if(!cell){
8394             return;
8395         }
8396         
8397         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8398             cell = cell.findParent('td', false, true);
8399         }
8400         
8401         var row = cell.findParent('tr', false, true);
8402         var cellIndex = cell.dom.cellIndex;
8403         var rowIndex = row.dom.rowIndex - 1; // start from 0
8404         
8405         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8406         
8407     },
8408     
8409     onClick : function(e, el)
8410     {
8411         var cell = Roo.get(el);
8412         
8413         if(!cell || (!this.cellSelection && !this.rowSelection)){
8414             return;
8415         }
8416         
8417         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8418             cell = cell.findParent('td', false, true);
8419         }
8420         
8421         if(!cell || typeof(cell) == 'undefined'){
8422             return;
8423         }
8424         
8425         var row = cell.findParent('tr', false, true);
8426         
8427         if(!row || typeof(row) == 'undefined'){
8428             return;
8429         }
8430         
8431         var cellIndex = cell.dom.cellIndex;
8432         var rowIndex = this.getRowIndex(row);
8433         
8434         // why??? - should these not be based on SelectionModel?
8435         if(this.cellSelection){
8436             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8437         }
8438         
8439         if(this.rowSelection){
8440             this.fireEvent('rowclick', this, row, rowIndex, e);
8441         }
8442         
8443         
8444     },
8445         
8446     onDblClick : function(e,el)
8447     {
8448         var cell = Roo.get(el);
8449         
8450         if(!cell || (!this.cellSelection && !this.rowSelection)){
8451             return;
8452         }
8453         
8454         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8455             cell = cell.findParent('td', false, true);
8456         }
8457         
8458         if(!cell || typeof(cell) == 'undefined'){
8459             return;
8460         }
8461         
8462         var row = cell.findParent('tr', false, true);
8463         
8464         if(!row || typeof(row) == 'undefined'){
8465             return;
8466         }
8467         
8468         var cellIndex = cell.dom.cellIndex;
8469         var rowIndex = this.getRowIndex(row);
8470         
8471         if(this.cellSelection){
8472             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8473         }
8474         
8475         if(this.rowSelection){
8476             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8477         }
8478     },
8479     
8480     sort : function(e,el)
8481     {
8482         var col = Roo.get(el);
8483         
8484         if(!col.hasClass('sortable')){
8485             return;
8486         }
8487         
8488         var sort = col.attr('sort');
8489         var dir = 'ASC';
8490         
8491         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8492             dir = 'DESC';
8493         }
8494         
8495         this.store.sortInfo = {field : sort, direction : dir};
8496         
8497         if (this.footer) {
8498             Roo.log("calling footer first");
8499             this.footer.onClick('first');
8500         } else {
8501         
8502             this.store.load({ params : { start : 0 } });
8503         }
8504     },
8505     
8506     renderHeader : function()
8507     {
8508         var header = {
8509             tag: 'thead',
8510             cn : []
8511         };
8512         
8513         var cm = this.cm;
8514         this.totalWidth = 0;
8515         
8516         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8517             
8518             var config = cm.config[i];
8519             
8520             var c = {
8521                 tag: 'th',
8522                 cls : 'x-hcol-' + i,
8523                 style : '',
8524                 html: cm.getColumnHeader(i)
8525             };
8526             
8527             var hh = '';
8528             
8529             if(typeof(config.sortable) != 'undefined' && config.sortable){
8530                 c.cls = 'sortable';
8531                 c.html = '<i class="glyphicon"></i>' + c.html;
8532             }
8533             
8534             // could use BS4 hidden-..-down 
8535             
8536             if(typeof(config.lgHeader) != 'undefined'){
8537                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8538             }
8539             
8540             if(typeof(config.mdHeader) != 'undefined'){
8541                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8542             }
8543             
8544             if(typeof(config.smHeader) != 'undefined'){
8545                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8546             }
8547             
8548             if(typeof(config.xsHeader) != 'undefined'){
8549                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8550             }
8551             
8552             if(hh.length){
8553                 c.html = hh;
8554             }
8555             
8556             if(typeof(config.tooltip) != 'undefined'){
8557                 c.tooltip = config.tooltip;
8558             }
8559             
8560             if(typeof(config.colspan) != 'undefined'){
8561                 c.colspan = config.colspan;
8562             }
8563             
8564             if(typeof(config.hidden) != 'undefined' && config.hidden){
8565                 c.style += ' display:none;';
8566             }
8567             
8568             if(typeof(config.dataIndex) != 'undefined'){
8569                 c.sort = config.dataIndex;
8570             }
8571             
8572            
8573             
8574             if(typeof(config.align) != 'undefined' && config.align.length){
8575                 c.style += ' text-align:' + config.align + ';';
8576             }
8577             
8578             if(typeof(config.width) != 'undefined'){
8579                 c.style += ' width:' + config.width + 'px;';
8580                 this.totalWidth += config.width;
8581             } else {
8582                 this.totalWidth += 100; // assume minimum of 100 per column?
8583             }
8584             
8585             if(typeof(config.cls) != 'undefined'){
8586                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8587             }
8588             
8589             ['xs','sm','md','lg'].map(function(size){
8590                 
8591                 if(typeof(config[size]) == 'undefined'){
8592                     return;
8593                 }
8594                  
8595                 if (!config[size]) { // 0 = hidden
8596                     // BS 4 '0' is treated as hide that column and below.
8597                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8598                     return;
8599                 }
8600                 
8601                 c.cls += ' col-' + size + '-' + config[size] + (
8602                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8603                 );
8604                 
8605                 
8606             });
8607             
8608             header.cn.push(c)
8609         }
8610         
8611         return header;
8612     },
8613     
8614     renderBody : function()
8615     {
8616         var body = {
8617             tag: 'tbody',
8618             cn : [
8619                 {
8620                     tag: 'tr',
8621                     cn : [
8622                         {
8623                             tag : 'td',
8624                             colspan :  this.cm.getColumnCount()
8625                         }
8626                     ]
8627                 }
8628             ]
8629         };
8630         
8631         return body;
8632     },
8633     
8634     renderFooter : function()
8635     {
8636         var footer = {
8637             tag: 'tfoot',
8638             cn : [
8639                 {
8640                     tag: 'tr',
8641                     cn : [
8642                         {
8643                             tag : 'td',
8644                             colspan :  this.cm.getColumnCount()
8645                         }
8646                     ]
8647                 }
8648             ]
8649         };
8650         
8651         return footer;
8652     },
8653     
8654     
8655     
8656     onLoad : function()
8657     {
8658 //        Roo.log('ds onload');
8659         this.clear();
8660         
8661         var _this = this;
8662         var cm = this.cm;
8663         var ds = this.store;
8664         
8665         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8666             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8667             if (_this.store.sortInfo) {
8668                     
8669                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8670                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8671                 }
8672                 
8673                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8674                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8675                 }
8676             }
8677         });
8678         
8679         var tbody =  this.mainBody;
8680               
8681         if(ds.getCount() > 0){
8682             ds.data.each(function(d,rowIndex){
8683                 var row =  this.renderRow(cm, ds, rowIndex);
8684                 
8685                 tbody.createChild(row);
8686                 
8687                 var _this = this;
8688                 
8689                 if(row.cellObjects.length){
8690                     Roo.each(row.cellObjects, function(r){
8691                         _this.renderCellObject(r);
8692                     })
8693                 }
8694                 
8695             }, this);
8696         }
8697         
8698         var tfoot = this.el.select('tfoot', true).first();
8699         
8700         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8701             
8702             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8703             
8704             var total = this.ds.getTotalCount();
8705             
8706             if(this.footer.pageSize < total){
8707                 this.mainFoot.show();
8708             }
8709         }
8710         
8711         Roo.each(this.el.select('tbody td', true).elements, function(e){
8712             e.on('mouseover', _this.onMouseover, _this);
8713         });
8714         
8715         Roo.each(this.el.select('tbody td', true).elements, function(e){
8716             e.on('mouseout', _this.onMouseout, _this);
8717         });
8718         this.fireEvent('rowsrendered', this);
8719         
8720         this.autoSize();
8721     },
8722     
8723     
8724     onUpdate : function(ds,record)
8725     {
8726         this.refreshRow(record);
8727         this.autoSize();
8728     },
8729     
8730     onRemove : function(ds, record, index, isUpdate){
8731         if(isUpdate !== true){
8732             this.fireEvent("beforerowremoved", this, index, record);
8733         }
8734         var bt = this.mainBody.dom;
8735         
8736         var rows = this.el.select('tbody > tr', true).elements;
8737         
8738         if(typeof(rows[index]) != 'undefined'){
8739             bt.removeChild(rows[index].dom);
8740         }
8741         
8742 //        if(bt.rows[index]){
8743 //            bt.removeChild(bt.rows[index]);
8744 //        }
8745         
8746         if(isUpdate !== true){
8747             //this.stripeRows(index);
8748             //this.syncRowHeights(index, index);
8749             //this.layout();
8750             this.fireEvent("rowremoved", this, index, record);
8751         }
8752     },
8753     
8754     onAdd : function(ds, records, rowIndex)
8755     {
8756         //Roo.log('on Add called');
8757         // - note this does not handle multiple adding very well..
8758         var bt = this.mainBody.dom;
8759         for (var i =0 ; i < records.length;i++) {
8760             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8761             //Roo.log(records[i]);
8762             //Roo.log(this.store.getAt(rowIndex+i));
8763             this.insertRow(this.store, rowIndex + i, false);
8764             return;
8765         }
8766         
8767     },
8768     
8769     
8770     refreshRow : function(record){
8771         var ds = this.store, index;
8772         if(typeof record == 'number'){
8773             index = record;
8774             record = ds.getAt(index);
8775         }else{
8776             index = ds.indexOf(record);
8777             if (index < 0) {
8778                 return; // should not happen - but seems to 
8779             }
8780         }
8781         this.insertRow(ds, index, true);
8782         this.autoSize();
8783         this.onRemove(ds, record, index+1, true);
8784         this.autoSize();
8785         //this.syncRowHeights(index, index);
8786         //this.layout();
8787         this.fireEvent("rowupdated", this, index, record);
8788     },
8789     
8790     insertRow : function(dm, rowIndex, isUpdate){
8791         
8792         if(!isUpdate){
8793             this.fireEvent("beforerowsinserted", this, rowIndex);
8794         }
8795             //var s = this.getScrollState();
8796         var row = this.renderRow(this.cm, this.store, rowIndex);
8797         // insert before rowIndex..
8798         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8799         
8800         var _this = this;
8801                 
8802         if(row.cellObjects.length){
8803             Roo.each(row.cellObjects, function(r){
8804                 _this.renderCellObject(r);
8805             })
8806         }
8807             
8808         if(!isUpdate){
8809             this.fireEvent("rowsinserted", this, rowIndex);
8810             //this.syncRowHeights(firstRow, lastRow);
8811             //this.stripeRows(firstRow);
8812             //this.layout();
8813         }
8814         
8815     },
8816     
8817     
8818     getRowDom : function(rowIndex)
8819     {
8820         var rows = this.el.select('tbody > tr', true).elements;
8821         
8822         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8823         
8824     },
8825     // returns the object tree for a tr..
8826   
8827     
8828     renderRow : function(cm, ds, rowIndex) 
8829     {
8830         var d = ds.getAt(rowIndex);
8831         
8832         var row = {
8833             tag : 'tr',
8834             cls : 'x-row-' + rowIndex,
8835             cn : []
8836         };
8837             
8838         var cellObjects = [];
8839         
8840         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8841             var config = cm.config[i];
8842             
8843             var renderer = cm.getRenderer(i);
8844             var value = '';
8845             var id = false;
8846             
8847             if(typeof(renderer) !== 'undefined'){
8848                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8849             }
8850             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8851             // and are rendered into the cells after the row is rendered - using the id for the element.
8852             
8853             if(typeof(value) === 'object'){
8854                 id = Roo.id();
8855                 cellObjects.push({
8856                     container : id,
8857                     cfg : value 
8858                 })
8859             }
8860             
8861             var rowcfg = {
8862                 record: d,
8863                 rowIndex : rowIndex,
8864                 colIndex : i,
8865                 rowClass : ''
8866             };
8867
8868             this.fireEvent('rowclass', this, rowcfg);
8869             
8870             var td = {
8871                 tag: 'td',
8872                 cls : rowcfg.rowClass + ' x-col-' + i,
8873                 style: '',
8874                 html: (typeof(value) === 'object') ? '' : value
8875             };
8876             
8877             if (id) {
8878                 td.id = id;
8879             }
8880             
8881             if(typeof(config.colspan) != 'undefined'){
8882                 td.colspan = config.colspan;
8883             }
8884             
8885             if(typeof(config.hidden) != 'undefined' && config.hidden){
8886                 td.style += ' display:none;';
8887             }
8888             
8889             if(typeof(config.align) != 'undefined' && config.align.length){
8890                 td.style += ' text-align:' + config.align + ';';
8891             }
8892             if(typeof(config.valign) != 'undefined' && config.valign.length){
8893                 td.style += ' vertical-align:' + config.valign + ';';
8894             }
8895             
8896             if(typeof(config.width) != 'undefined'){
8897                 td.style += ' width:' +  config.width + 'px;';
8898             }
8899             
8900             if(typeof(config.cursor) != 'undefined'){
8901                 td.style += ' cursor:' +  config.cursor + ';';
8902             }
8903             
8904             if(typeof(config.cls) != 'undefined'){
8905                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8906             }
8907             
8908             ['xs','sm','md','lg'].map(function(size){
8909                 
8910                 if(typeof(config[size]) == 'undefined'){
8911                     return;
8912                 }
8913                 
8914                 
8915                   
8916                 if (!config[size]) { // 0 = hidden
8917                     // BS 4 '0' is treated as hide that column and below.
8918                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8919                     return;
8920                 }
8921                 
8922                 td.cls += ' col-' + size + '-' + config[size] + (
8923                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8924                 );
8925                  
8926
8927             });
8928             
8929             row.cn.push(td);
8930            
8931         }
8932         
8933         row.cellObjects = cellObjects;
8934         
8935         return row;
8936           
8937     },
8938     
8939     
8940     
8941     onBeforeLoad : function()
8942     {
8943         
8944     },
8945      /**
8946      * Remove all rows
8947      */
8948     clear : function()
8949     {
8950         this.el.select('tbody', true).first().dom.innerHTML = '';
8951     },
8952     /**
8953      * Show or hide a row.
8954      * @param {Number} rowIndex to show or hide
8955      * @param {Boolean} state hide
8956      */
8957     setRowVisibility : function(rowIndex, state)
8958     {
8959         var bt = this.mainBody.dom;
8960         
8961         var rows = this.el.select('tbody > tr', true).elements;
8962         
8963         if(typeof(rows[rowIndex]) == 'undefined'){
8964             return;
8965         }
8966         rows[rowIndex].dom.style.display = state ? '' : 'none';
8967     },
8968     
8969     
8970     getSelectionModel : function(){
8971         if(!this.selModel){
8972             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8973         }
8974         return this.selModel;
8975     },
8976     /*
8977      * Render the Roo.bootstrap object from renderder
8978      */
8979     renderCellObject : function(r)
8980     {
8981         var _this = this;
8982         
8983         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8984         
8985         var t = r.cfg.render(r.container);
8986         
8987         if(r.cfg.cn){
8988             Roo.each(r.cfg.cn, function(c){
8989                 var child = {
8990                     container: t.getChildContainer(),
8991                     cfg: c
8992                 };
8993                 _this.renderCellObject(child);
8994             })
8995         }
8996     },
8997     
8998     getRowIndex : function(row)
8999     {
9000         var rowIndex = -1;
9001         
9002         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9003             if(el != row){
9004                 return;
9005             }
9006             
9007             rowIndex = index;
9008         });
9009         
9010         return rowIndex;
9011     },
9012      /**
9013      * Returns the grid's underlying element = used by panel.Grid
9014      * @return {Element} The element
9015      */
9016     getGridEl : function(){
9017         return this.el;
9018     },
9019      /**
9020      * Forces a resize - used by panel.Grid
9021      * @return {Element} The element
9022      */
9023     autoSize : function()
9024     {
9025         //var ctr = Roo.get(this.container.dom.parentElement);
9026         var ctr = Roo.get(this.el.dom);
9027         
9028         var thd = this.getGridEl().select('thead',true).first();
9029         var tbd = this.getGridEl().select('tbody', true).first();
9030         var tfd = this.getGridEl().select('tfoot', true).first();
9031         
9032         var cw = ctr.getWidth();
9033         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9034         
9035         if (tbd) {
9036             
9037             tbd.setWidth(ctr.getWidth());
9038             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9039             // this needs fixing for various usage - currently only hydra job advers I think..
9040             //tdb.setHeight(
9041             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9042             //); 
9043             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9044             cw -= barsize;
9045         }
9046         cw = Math.max(cw, this.totalWidth);
9047         this.getGridEl().select('tbody tr',true).setWidth(cw);
9048         
9049         // resize 'expandable coloumn?
9050         
9051         return; // we doe not have a view in this design..
9052         
9053     },
9054     onBodyScroll: function()
9055     {
9056         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9057         if(this.mainHead){
9058             this.mainHead.setStyle({
9059                 'position' : 'relative',
9060                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9061             });
9062         }
9063         
9064         if(this.lazyLoad){
9065             
9066             var scrollHeight = this.mainBody.dom.scrollHeight;
9067             
9068             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9069             
9070             var height = this.mainBody.getHeight();
9071             
9072             if(scrollHeight - height == scrollTop) {
9073                 
9074                 var total = this.ds.getTotalCount();
9075                 
9076                 if(this.footer.cursor + this.footer.pageSize < total){
9077                     
9078                     this.footer.ds.load({
9079                         params : {
9080                             start : this.footer.cursor + this.footer.pageSize,
9081                             limit : this.footer.pageSize
9082                         },
9083                         add : true
9084                     });
9085                 }
9086             }
9087             
9088         }
9089     },
9090     
9091     onHeaderChange : function()
9092     {
9093         var header = this.renderHeader();
9094         var table = this.el.select('table', true).first();
9095         
9096         this.mainHead.remove();
9097         this.mainHead = table.createChild(header, this.mainBody, false);
9098     },
9099     
9100     onHiddenChange : function(colModel, colIndex, hidden)
9101     {
9102         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9103         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9104         
9105         this.CSS.updateRule(thSelector, "display", "");
9106         this.CSS.updateRule(tdSelector, "display", "");
9107         
9108         if(hidden){
9109             this.CSS.updateRule(thSelector, "display", "none");
9110             this.CSS.updateRule(tdSelector, "display", "none");
9111         }
9112         
9113         this.onHeaderChange();
9114         this.onLoad();
9115     },
9116     
9117     setColumnWidth: function(col_index, width)
9118     {
9119         // width = "md-2 xs-2..."
9120         if(!this.colModel.config[col_index]) {
9121             return;
9122         }
9123         
9124         var w = width.split(" ");
9125         
9126         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9127         
9128         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9129         
9130         
9131         for(var j = 0; j < w.length; j++) {
9132             
9133             if(!w[j]) {
9134                 continue;
9135             }
9136             
9137             var size_cls = w[j].split("-");
9138             
9139             if(!Number.isInteger(size_cls[1] * 1)) {
9140                 continue;
9141             }
9142             
9143             if(!this.colModel.config[col_index][size_cls[0]]) {
9144                 continue;
9145             }
9146             
9147             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9148                 continue;
9149             }
9150             
9151             h_row[0].classList.replace(
9152                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9153                 "col-"+size_cls[0]+"-"+size_cls[1]
9154             );
9155             
9156             for(var i = 0; i < rows.length; i++) {
9157                 
9158                 var size_cls = w[j].split("-");
9159                 
9160                 if(!Number.isInteger(size_cls[1] * 1)) {
9161                     continue;
9162                 }
9163                 
9164                 if(!this.colModel.config[col_index][size_cls[0]]) {
9165                     continue;
9166                 }
9167                 
9168                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9169                     continue;
9170                 }
9171                 
9172                 rows[i].classList.replace(
9173                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9174                     "col-"+size_cls[0]+"-"+size_cls[1]
9175                 );
9176             }
9177             
9178             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9179         }
9180     }
9181 });
9182
9183  
9184
9185  /*
9186  * - LGPL
9187  *
9188  * table cell
9189  * 
9190  */
9191
9192 /**
9193  * @class Roo.bootstrap.TableCell
9194  * @extends Roo.bootstrap.Component
9195  * Bootstrap TableCell class
9196  * @cfg {String} html cell contain text
9197  * @cfg {String} cls cell class
9198  * @cfg {String} tag cell tag (td|th) default td
9199  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9200  * @cfg {String} align Aligns the content in a cell
9201  * @cfg {String} axis Categorizes cells
9202  * @cfg {String} bgcolor Specifies the background color of a cell
9203  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9204  * @cfg {Number} colspan Specifies the number of columns a cell should span
9205  * @cfg {String} headers Specifies one or more header cells a cell is related to
9206  * @cfg {Number} height Sets the height of a cell
9207  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9208  * @cfg {Number} rowspan Sets the number of rows a cell should span
9209  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9210  * @cfg {String} valign Vertical aligns the content in a cell
9211  * @cfg {Number} width Specifies the width of a cell
9212  * 
9213  * @constructor
9214  * Create a new TableCell
9215  * @param {Object} config The config object
9216  */
9217
9218 Roo.bootstrap.TableCell = function(config){
9219     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9220 };
9221
9222 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9223     
9224     html: false,
9225     cls: false,
9226     tag: false,
9227     abbr: false,
9228     align: false,
9229     axis: false,
9230     bgcolor: false,
9231     charoff: false,
9232     colspan: false,
9233     headers: false,
9234     height: false,
9235     nowrap: false,
9236     rowspan: false,
9237     scope: false,
9238     valign: false,
9239     width: false,
9240     
9241     
9242     getAutoCreate : function(){
9243         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9244         
9245         cfg = {
9246             tag: 'td'
9247         };
9248         
9249         if(this.tag){
9250             cfg.tag = this.tag;
9251         }
9252         
9253         if (this.html) {
9254             cfg.html=this.html
9255         }
9256         if (this.cls) {
9257             cfg.cls=this.cls
9258         }
9259         if (this.abbr) {
9260             cfg.abbr=this.abbr
9261         }
9262         if (this.align) {
9263             cfg.align=this.align
9264         }
9265         if (this.axis) {
9266             cfg.axis=this.axis
9267         }
9268         if (this.bgcolor) {
9269             cfg.bgcolor=this.bgcolor
9270         }
9271         if (this.charoff) {
9272             cfg.charoff=this.charoff
9273         }
9274         if (this.colspan) {
9275             cfg.colspan=this.colspan
9276         }
9277         if (this.headers) {
9278             cfg.headers=this.headers
9279         }
9280         if (this.height) {
9281             cfg.height=this.height
9282         }
9283         if (this.nowrap) {
9284             cfg.nowrap=this.nowrap
9285         }
9286         if (this.rowspan) {
9287             cfg.rowspan=this.rowspan
9288         }
9289         if (this.scope) {
9290             cfg.scope=this.scope
9291         }
9292         if (this.valign) {
9293             cfg.valign=this.valign
9294         }
9295         if (this.width) {
9296             cfg.width=this.width
9297         }
9298         
9299         
9300         return cfg;
9301     }
9302    
9303 });
9304
9305  
9306
9307  /*
9308  * - LGPL
9309  *
9310  * table row
9311  * 
9312  */
9313
9314 /**
9315  * @class Roo.bootstrap.TableRow
9316  * @extends Roo.bootstrap.Component
9317  * Bootstrap TableRow class
9318  * @cfg {String} cls row class
9319  * @cfg {String} align Aligns the content in a table row
9320  * @cfg {String} bgcolor Specifies a background color for a table row
9321  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9322  * @cfg {String} valign Vertical aligns the content in a table row
9323  * 
9324  * @constructor
9325  * Create a new TableRow
9326  * @param {Object} config The config object
9327  */
9328
9329 Roo.bootstrap.TableRow = function(config){
9330     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9331 };
9332
9333 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9334     
9335     cls: false,
9336     align: false,
9337     bgcolor: false,
9338     charoff: false,
9339     valign: false,
9340     
9341     getAutoCreate : function(){
9342         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9343         
9344         cfg = {
9345             tag: 'tr'
9346         };
9347             
9348         if(this.cls){
9349             cfg.cls = this.cls;
9350         }
9351         if(this.align){
9352             cfg.align = this.align;
9353         }
9354         if(this.bgcolor){
9355             cfg.bgcolor = this.bgcolor;
9356         }
9357         if(this.charoff){
9358             cfg.charoff = this.charoff;
9359         }
9360         if(this.valign){
9361             cfg.valign = this.valign;
9362         }
9363         
9364         return cfg;
9365     }
9366    
9367 });
9368
9369  
9370
9371  /*
9372  * - LGPL
9373  *
9374  * table body
9375  * 
9376  */
9377
9378 /**
9379  * @class Roo.bootstrap.TableBody
9380  * @extends Roo.bootstrap.Component
9381  * Bootstrap TableBody class
9382  * @cfg {String} cls element class
9383  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9384  * @cfg {String} align Aligns the content inside the element
9385  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9386  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9387  * 
9388  * @constructor
9389  * Create a new TableBody
9390  * @param {Object} config The config object
9391  */
9392
9393 Roo.bootstrap.TableBody = function(config){
9394     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9395 };
9396
9397 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9398     
9399     cls: false,
9400     tag: false,
9401     align: false,
9402     charoff: false,
9403     valign: false,
9404     
9405     getAutoCreate : function(){
9406         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9407         
9408         cfg = {
9409             tag: 'tbody'
9410         };
9411             
9412         if (this.cls) {
9413             cfg.cls=this.cls
9414         }
9415         if(this.tag){
9416             cfg.tag = this.tag;
9417         }
9418         
9419         if(this.align){
9420             cfg.align = this.align;
9421         }
9422         if(this.charoff){
9423             cfg.charoff = this.charoff;
9424         }
9425         if(this.valign){
9426             cfg.valign = this.valign;
9427         }
9428         
9429         return cfg;
9430     }
9431     
9432     
9433 //    initEvents : function()
9434 //    {
9435 //        
9436 //        if(!this.store){
9437 //            return;
9438 //        }
9439 //        
9440 //        this.store = Roo.factory(this.store, Roo.data);
9441 //        this.store.on('load', this.onLoad, this);
9442 //        
9443 //        this.store.load();
9444 //        
9445 //    },
9446 //    
9447 //    onLoad: function () 
9448 //    {   
9449 //        this.fireEvent('load', this);
9450 //    }
9451 //    
9452 //   
9453 });
9454
9455  
9456
9457  /*
9458  * Based on:
9459  * Ext JS Library 1.1.1
9460  * Copyright(c) 2006-2007, Ext JS, LLC.
9461  *
9462  * Originally Released Under LGPL - original licence link has changed is not relivant.
9463  *
9464  * Fork - LGPL
9465  * <script type="text/javascript">
9466  */
9467
9468 // as we use this in bootstrap.
9469 Roo.namespace('Roo.form');
9470  /**
9471  * @class Roo.form.Action
9472  * Internal Class used to handle form actions
9473  * @constructor
9474  * @param {Roo.form.BasicForm} el The form element or its id
9475  * @param {Object} config Configuration options
9476  */
9477
9478  
9479  
9480 // define the action interface
9481 Roo.form.Action = function(form, options){
9482     this.form = form;
9483     this.options = options || {};
9484 };
9485 /**
9486  * Client Validation Failed
9487  * @const 
9488  */
9489 Roo.form.Action.CLIENT_INVALID = 'client';
9490 /**
9491  * Server Validation Failed
9492  * @const 
9493  */
9494 Roo.form.Action.SERVER_INVALID = 'server';
9495  /**
9496  * Connect to Server Failed
9497  * @const 
9498  */
9499 Roo.form.Action.CONNECT_FAILURE = 'connect';
9500 /**
9501  * Reading Data from Server Failed
9502  * @const 
9503  */
9504 Roo.form.Action.LOAD_FAILURE = 'load';
9505
9506 Roo.form.Action.prototype = {
9507     type : 'default',
9508     failureType : undefined,
9509     response : undefined,
9510     result : undefined,
9511
9512     // interface method
9513     run : function(options){
9514
9515     },
9516
9517     // interface method
9518     success : function(response){
9519
9520     },
9521
9522     // interface method
9523     handleResponse : function(response){
9524
9525     },
9526
9527     // default connection failure
9528     failure : function(response){
9529         
9530         this.response = response;
9531         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9532         this.form.afterAction(this, false);
9533     },
9534
9535     processResponse : function(response){
9536         this.response = response;
9537         if(!response.responseText){
9538             return true;
9539         }
9540         this.result = this.handleResponse(response);
9541         return this.result;
9542     },
9543
9544     // utility functions used internally
9545     getUrl : function(appendParams){
9546         var url = this.options.url || this.form.url || this.form.el.dom.action;
9547         if(appendParams){
9548             var p = this.getParams();
9549             if(p){
9550                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9551             }
9552         }
9553         return url;
9554     },
9555
9556     getMethod : function(){
9557         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9558     },
9559
9560     getParams : function(){
9561         var bp = this.form.baseParams;
9562         var p = this.options.params;
9563         if(p){
9564             if(typeof p == "object"){
9565                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9566             }else if(typeof p == 'string' && bp){
9567                 p += '&' + Roo.urlEncode(bp);
9568             }
9569         }else if(bp){
9570             p = Roo.urlEncode(bp);
9571         }
9572         return p;
9573     },
9574
9575     createCallback : function(){
9576         return {
9577             success: this.success,
9578             failure: this.failure,
9579             scope: this,
9580             timeout: (this.form.timeout*1000),
9581             upload: this.form.fileUpload ? this.success : undefined
9582         };
9583     }
9584 };
9585
9586 Roo.form.Action.Submit = function(form, options){
9587     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9588 };
9589
9590 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9591     type : 'submit',
9592
9593     haveProgress : false,
9594     uploadComplete : false,
9595     
9596     // uploadProgress indicator.
9597     uploadProgress : function()
9598     {
9599         if (!this.form.progressUrl) {
9600             return;
9601         }
9602         
9603         if (!this.haveProgress) {
9604             Roo.MessageBox.progress("Uploading", "Uploading");
9605         }
9606         if (this.uploadComplete) {
9607            Roo.MessageBox.hide();
9608            return;
9609         }
9610         
9611         this.haveProgress = true;
9612    
9613         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9614         
9615         var c = new Roo.data.Connection();
9616         c.request({
9617             url : this.form.progressUrl,
9618             params: {
9619                 id : uid
9620             },
9621             method: 'GET',
9622             success : function(req){
9623                //console.log(data);
9624                 var rdata = false;
9625                 var edata;
9626                 try  {
9627                    rdata = Roo.decode(req.responseText)
9628                 } catch (e) {
9629                     Roo.log("Invalid data from server..");
9630                     Roo.log(edata);
9631                     return;
9632                 }
9633                 if (!rdata || !rdata.success) {
9634                     Roo.log(rdata);
9635                     Roo.MessageBox.alert(Roo.encode(rdata));
9636                     return;
9637                 }
9638                 var data = rdata.data;
9639                 
9640                 if (this.uploadComplete) {
9641                    Roo.MessageBox.hide();
9642                    return;
9643                 }
9644                    
9645                 if (data){
9646                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9647                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9648                     );
9649                 }
9650                 this.uploadProgress.defer(2000,this);
9651             },
9652        
9653             failure: function(data) {
9654                 Roo.log('progress url failed ');
9655                 Roo.log(data);
9656             },
9657             scope : this
9658         });
9659            
9660     },
9661     
9662     
9663     run : function()
9664     {
9665         // run get Values on the form, so it syncs any secondary forms.
9666         this.form.getValues();
9667         
9668         var o = this.options;
9669         var method = this.getMethod();
9670         var isPost = method == 'POST';
9671         if(o.clientValidation === false || this.form.isValid()){
9672             
9673             if (this.form.progressUrl) {
9674                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9675                     (new Date() * 1) + '' + Math.random());
9676                     
9677             } 
9678             
9679             
9680             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9681                 form:this.form.el.dom,
9682                 url:this.getUrl(!isPost),
9683                 method: method,
9684                 params:isPost ? this.getParams() : null,
9685                 isUpload: this.form.fileUpload,
9686                 formData : this.form.formData
9687             }));
9688             
9689             this.uploadProgress();
9690
9691         }else if (o.clientValidation !== false){ // client validation failed
9692             this.failureType = Roo.form.Action.CLIENT_INVALID;
9693             this.form.afterAction(this, false);
9694         }
9695     },
9696
9697     success : function(response)
9698     {
9699         this.uploadComplete= true;
9700         if (this.haveProgress) {
9701             Roo.MessageBox.hide();
9702         }
9703         
9704         
9705         var result = this.processResponse(response);
9706         if(result === true || result.success){
9707             this.form.afterAction(this, true);
9708             return;
9709         }
9710         if(result.errors){
9711             this.form.markInvalid(result.errors);
9712             this.failureType = Roo.form.Action.SERVER_INVALID;
9713         }
9714         this.form.afterAction(this, false);
9715     },
9716     failure : function(response)
9717     {
9718         this.uploadComplete= true;
9719         if (this.haveProgress) {
9720             Roo.MessageBox.hide();
9721         }
9722         
9723         this.response = response;
9724         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9725         this.form.afterAction(this, false);
9726     },
9727     
9728     handleResponse : function(response){
9729         if(this.form.errorReader){
9730             var rs = this.form.errorReader.read(response);
9731             var errors = [];
9732             if(rs.records){
9733                 for(var i = 0, len = rs.records.length; i < len; i++) {
9734                     var r = rs.records[i];
9735                     errors[i] = r.data;
9736                 }
9737             }
9738             if(errors.length < 1){
9739                 errors = null;
9740             }
9741             return {
9742                 success : rs.success,
9743                 errors : errors
9744             };
9745         }
9746         var ret = false;
9747         try {
9748             ret = Roo.decode(response.responseText);
9749         } catch (e) {
9750             ret = {
9751                 success: false,
9752                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9753                 errors : []
9754             };
9755         }
9756         return ret;
9757         
9758     }
9759 });
9760
9761
9762 Roo.form.Action.Load = function(form, options){
9763     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9764     this.reader = this.form.reader;
9765 };
9766
9767 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9768     type : 'load',
9769
9770     run : function(){
9771         
9772         Roo.Ajax.request(Roo.apply(
9773                 this.createCallback(), {
9774                     method:this.getMethod(),
9775                     url:this.getUrl(false),
9776                     params:this.getParams()
9777         }));
9778     },
9779
9780     success : function(response){
9781         
9782         var result = this.processResponse(response);
9783         if(result === true || !result.success || !result.data){
9784             this.failureType = Roo.form.Action.LOAD_FAILURE;
9785             this.form.afterAction(this, false);
9786             return;
9787         }
9788         this.form.clearInvalid();
9789         this.form.setValues(result.data);
9790         this.form.afterAction(this, true);
9791     },
9792
9793     handleResponse : function(response){
9794         if(this.form.reader){
9795             var rs = this.form.reader.read(response);
9796             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9797             return {
9798                 success : rs.success,
9799                 data : data
9800             };
9801         }
9802         return Roo.decode(response.responseText);
9803     }
9804 });
9805
9806 Roo.form.Action.ACTION_TYPES = {
9807     'load' : Roo.form.Action.Load,
9808     'submit' : Roo.form.Action.Submit
9809 };/*
9810  * - LGPL
9811  *
9812  * form
9813  *
9814  */
9815
9816 /**
9817  * @class Roo.bootstrap.Form
9818  * @extends Roo.bootstrap.Component
9819  * Bootstrap Form class
9820  * @cfg {String} method  GET | POST (default POST)
9821  * @cfg {String} labelAlign top | left (default top)
9822  * @cfg {String} align left  | right - for navbars
9823  * @cfg {Boolean} loadMask load mask when submit (default true)
9824
9825  *
9826  * @constructor
9827  * Create a new Form
9828  * @param {Object} config The config object
9829  */
9830
9831
9832 Roo.bootstrap.Form = function(config){
9833     
9834     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9835     
9836     Roo.bootstrap.Form.popover.apply();
9837     
9838     this.addEvents({
9839         /**
9840          * @event clientvalidation
9841          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9842          * @param {Form} this
9843          * @param {Boolean} valid true if the form has passed client-side validation
9844          */
9845         clientvalidation: true,
9846         /**
9847          * @event beforeaction
9848          * Fires before any action is performed. Return false to cancel the action.
9849          * @param {Form} this
9850          * @param {Action} action The action to be performed
9851          */
9852         beforeaction: true,
9853         /**
9854          * @event actionfailed
9855          * Fires when an action fails.
9856          * @param {Form} this
9857          * @param {Action} action The action that failed
9858          */
9859         actionfailed : true,
9860         /**
9861          * @event actioncomplete
9862          * Fires when an action is completed.
9863          * @param {Form} this
9864          * @param {Action} action The action that completed
9865          */
9866         actioncomplete : true
9867     });
9868 };
9869
9870 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9871
9872      /**
9873      * @cfg {String} method
9874      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9875      */
9876     method : 'POST',
9877     /**
9878      * @cfg {String} url
9879      * The URL to use for form actions if one isn't supplied in the action options.
9880      */
9881     /**
9882      * @cfg {Boolean} fileUpload
9883      * Set to true if this form is a file upload.
9884      */
9885
9886     /**
9887      * @cfg {Object} baseParams
9888      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9889      */
9890
9891     /**
9892      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9893      */
9894     timeout: 30,
9895     /**
9896      * @cfg {Sting} align (left|right) for navbar forms
9897      */
9898     align : 'left',
9899
9900     // private
9901     activeAction : null,
9902
9903     /**
9904      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9905      * element by passing it or its id or mask the form itself by passing in true.
9906      * @type Mixed
9907      */
9908     waitMsgTarget : false,
9909
9910     loadMask : true,
9911     
9912     /**
9913      * @cfg {Boolean} errorMask (true|false) default false
9914      */
9915     errorMask : false,
9916     
9917     /**
9918      * @cfg {Number} maskOffset Default 100
9919      */
9920     maskOffset : 100,
9921     
9922     /**
9923      * @cfg {Boolean} maskBody
9924      */
9925     maskBody : false,
9926
9927     getAutoCreate : function(){
9928
9929         var cfg = {
9930             tag: 'form',
9931             method : this.method || 'POST',
9932             id : this.id || Roo.id(),
9933             cls : ''
9934         };
9935         if (this.parent().xtype.match(/^Nav/)) {
9936             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9937
9938         }
9939
9940         if (this.labelAlign == 'left' ) {
9941             cfg.cls += ' form-horizontal';
9942         }
9943
9944
9945         return cfg;
9946     },
9947     initEvents : function()
9948     {
9949         this.el.on('submit', this.onSubmit, this);
9950         // this was added as random key presses on the form where triggering form submit.
9951         this.el.on('keypress', function(e) {
9952             if (e.getCharCode() != 13) {
9953                 return true;
9954             }
9955             // we might need to allow it for textareas.. and some other items.
9956             // check e.getTarget().
9957
9958             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9959                 return true;
9960             }
9961
9962             Roo.log("keypress blocked");
9963
9964             e.preventDefault();
9965             return false;
9966         });
9967         
9968     },
9969     // private
9970     onSubmit : function(e){
9971         e.stopEvent();
9972     },
9973
9974      /**
9975      * Returns true if client-side validation on the form is successful.
9976      * @return Boolean
9977      */
9978     isValid : function(){
9979         var items = this.getItems();
9980         var valid = true;
9981         var target = false;
9982         
9983         items.each(function(f){
9984             
9985             if(f.validate()){
9986                 return;
9987             }
9988             
9989             Roo.log('invalid field: ' + f.name);
9990             
9991             valid = false;
9992
9993             if(!target && f.el.isVisible(true)){
9994                 target = f;
9995             }
9996            
9997         });
9998         
9999         if(this.errorMask && !valid){
10000             Roo.bootstrap.Form.popover.mask(this, target);
10001         }
10002         
10003         return valid;
10004     },
10005     
10006     /**
10007      * Returns true if any fields in this form have changed since their original load.
10008      * @return Boolean
10009      */
10010     isDirty : function(){
10011         var dirty = false;
10012         var items = this.getItems();
10013         items.each(function(f){
10014            if(f.isDirty()){
10015                dirty = true;
10016                return false;
10017            }
10018            return true;
10019         });
10020         return dirty;
10021     },
10022      /**
10023      * Performs a predefined action (submit or load) or custom actions you define on this form.
10024      * @param {String} actionName The name of the action type
10025      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10026      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10027      * accept other config options):
10028      * <pre>
10029 Property          Type             Description
10030 ----------------  ---------------  ----------------------------------------------------------------------------------
10031 url               String           The url for the action (defaults to the form's url)
10032 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10033 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10034 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10035                                    validate the form on the client (defaults to false)
10036      * </pre>
10037      * @return {BasicForm} this
10038      */
10039     doAction : function(action, options){
10040         if(typeof action == 'string'){
10041             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10042         }
10043         if(this.fireEvent('beforeaction', this, action) !== false){
10044             this.beforeAction(action);
10045             action.run.defer(100, action);
10046         }
10047         return this;
10048     },
10049
10050     // private
10051     beforeAction : function(action){
10052         var o = action.options;
10053         
10054         if(this.loadMask){
10055             
10056             if(this.maskBody){
10057                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10058             } else {
10059                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10060             }
10061         }
10062         // not really supported yet.. ??
10063
10064         //if(this.waitMsgTarget === true){
10065         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10066         //}else if(this.waitMsgTarget){
10067         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10068         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10069         //}else {
10070         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10071        // }
10072
10073     },
10074
10075     // private
10076     afterAction : function(action, success){
10077         this.activeAction = null;
10078         var o = action.options;
10079
10080         if(this.loadMask){
10081             
10082             if(this.maskBody){
10083                 Roo.get(document.body).unmask();
10084             } else {
10085                 this.el.unmask();
10086             }
10087         }
10088         
10089         //if(this.waitMsgTarget === true){
10090 //            this.el.unmask();
10091         //}else if(this.waitMsgTarget){
10092         //    this.waitMsgTarget.unmask();
10093         //}else{
10094         //    Roo.MessageBox.updateProgress(1);
10095         //    Roo.MessageBox.hide();
10096        // }
10097         //
10098         if(success){
10099             if(o.reset){
10100                 this.reset();
10101             }
10102             Roo.callback(o.success, o.scope, [this, action]);
10103             this.fireEvent('actioncomplete', this, action);
10104
10105         }else{
10106
10107             // failure condition..
10108             // we have a scenario where updates need confirming.
10109             // eg. if a locking scenario exists..
10110             // we look for { errors : { needs_confirm : true }} in the response.
10111             if (
10112                 (typeof(action.result) != 'undefined')  &&
10113                 (typeof(action.result.errors) != 'undefined')  &&
10114                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10115            ){
10116                 var _t = this;
10117                 Roo.log("not supported yet");
10118                  /*
10119
10120                 Roo.MessageBox.confirm(
10121                     "Change requires confirmation",
10122                     action.result.errorMsg,
10123                     function(r) {
10124                         if (r != 'yes') {
10125                             return;
10126                         }
10127                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10128                     }
10129
10130                 );
10131                 */
10132
10133
10134                 return;
10135             }
10136
10137             Roo.callback(o.failure, o.scope, [this, action]);
10138             // show an error message if no failed handler is set..
10139             if (!this.hasListener('actionfailed')) {
10140                 Roo.log("need to add dialog support");
10141                 /*
10142                 Roo.MessageBox.alert("Error",
10143                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10144                         action.result.errorMsg :
10145                         "Saving Failed, please check your entries or try again"
10146                 );
10147                 */
10148             }
10149
10150             this.fireEvent('actionfailed', this, action);
10151         }
10152
10153     },
10154     /**
10155      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10156      * @param {String} id The value to search for
10157      * @return Field
10158      */
10159     findField : function(id){
10160         var items = this.getItems();
10161         var field = items.get(id);
10162         if(!field){
10163              items.each(function(f){
10164                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10165                     field = f;
10166                     return false;
10167                 }
10168                 return true;
10169             });
10170         }
10171         return field || null;
10172     },
10173      /**
10174      * Mark fields in this form invalid in bulk.
10175      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10176      * @return {BasicForm} this
10177      */
10178     markInvalid : function(errors){
10179         if(errors instanceof Array){
10180             for(var i = 0, len = errors.length; i < len; i++){
10181                 var fieldError = errors[i];
10182                 var f = this.findField(fieldError.id);
10183                 if(f){
10184                     f.markInvalid(fieldError.msg);
10185                 }
10186             }
10187         }else{
10188             var field, id;
10189             for(id in errors){
10190                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10191                     field.markInvalid(errors[id]);
10192                 }
10193             }
10194         }
10195         //Roo.each(this.childForms || [], function (f) {
10196         //    f.markInvalid(errors);
10197         //});
10198
10199         return this;
10200     },
10201
10202     /**
10203      * Set values for fields in this form in bulk.
10204      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10205      * @return {BasicForm} this
10206      */
10207     setValues : function(values){
10208         if(values instanceof Array){ // array of objects
10209             for(var i = 0, len = values.length; i < len; i++){
10210                 var v = values[i];
10211                 var f = this.findField(v.id);
10212                 if(f){
10213                     f.setValue(v.value);
10214                     if(this.trackResetOnLoad){
10215                         f.originalValue = f.getValue();
10216                     }
10217                 }
10218             }
10219         }else{ // object hash
10220             var field, id;
10221             for(id in values){
10222                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10223
10224                     if (field.setFromData &&
10225                         field.valueField &&
10226                         field.displayField &&
10227                         // combos' with local stores can
10228                         // be queried via setValue()
10229                         // to set their value..
10230                         (field.store && !field.store.isLocal)
10231                         ) {
10232                         // it's a combo
10233                         var sd = { };
10234                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10235                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10236                         field.setFromData(sd);
10237
10238                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10239                         
10240                         field.setFromData(values);
10241                         
10242                     } else {
10243                         field.setValue(values[id]);
10244                     }
10245
10246
10247                     if(this.trackResetOnLoad){
10248                         field.originalValue = field.getValue();
10249                     }
10250                 }
10251             }
10252         }
10253
10254         //Roo.each(this.childForms || [], function (f) {
10255         //    f.setValues(values);
10256         //});
10257
10258         return this;
10259     },
10260
10261     /**
10262      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10263      * they are returned as an array.
10264      * @param {Boolean} asString
10265      * @return {Object}
10266      */
10267     getValues : function(asString){
10268         //if (this.childForms) {
10269             // copy values from the child forms
10270         //    Roo.each(this.childForms, function (f) {
10271         //        this.setValues(f.getValues());
10272         //    }, this);
10273         //}
10274
10275
10276
10277         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10278         if(asString === true){
10279             return fs;
10280         }
10281         return Roo.urlDecode(fs);
10282     },
10283
10284     /**
10285      * Returns the fields in this form as an object with key/value pairs.
10286      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10287      * @return {Object}
10288      */
10289     getFieldValues : function(with_hidden)
10290     {
10291         var items = this.getItems();
10292         var ret = {};
10293         items.each(function(f){
10294             
10295             if (!f.getName()) {
10296                 return;
10297             }
10298             
10299             var v = f.getValue();
10300             
10301             if (f.inputType =='radio') {
10302                 if (typeof(ret[f.getName()]) == 'undefined') {
10303                     ret[f.getName()] = ''; // empty..
10304                 }
10305
10306                 if (!f.el.dom.checked) {
10307                     return;
10308
10309                 }
10310                 v = f.el.dom.value;
10311
10312             }
10313             
10314             if(f.xtype == 'MoneyField'){
10315                 ret[f.currencyName] = f.getCurrency();
10316             }
10317
10318             // not sure if this supported any more..
10319             if ((typeof(v) == 'object') && f.getRawValue) {
10320                 v = f.getRawValue() ; // dates..
10321             }
10322             // combo boxes where name != hiddenName...
10323             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10324                 ret[f.name] = f.getRawValue();
10325             }
10326             ret[f.getName()] = v;
10327         });
10328
10329         return ret;
10330     },
10331
10332     /**
10333      * Clears all invalid messages in this form.
10334      * @return {BasicForm} this
10335      */
10336     clearInvalid : function(){
10337         var items = this.getItems();
10338
10339         items.each(function(f){
10340            f.clearInvalid();
10341         });
10342
10343         return this;
10344     },
10345
10346     /**
10347      * Resets this form.
10348      * @return {BasicForm} this
10349      */
10350     reset : function(){
10351         var items = this.getItems();
10352         items.each(function(f){
10353             f.reset();
10354         });
10355
10356         Roo.each(this.childForms || [], function (f) {
10357             f.reset();
10358         });
10359
10360
10361         return this;
10362     },
10363     
10364     getItems : function()
10365     {
10366         var r=new Roo.util.MixedCollection(false, function(o){
10367             return o.id || (o.id = Roo.id());
10368         });
10369         var iter = function(el) {
10370             if (el.inputEl) {
10371                 r.add(el);
10372             }
10373             if (!el.items) {
10374                 return;
10375             }
10376             Roo.each(el.items,function(e) {
10377                 iter(e);
10378             });
10379         };
10380
10381         iter(this);
10382         return r;
10383     },
10384     
10385     hideFields : function(items)
10386     {
10387         Roo.each(items, function(i){
10388             
10389             var f = this.findField(i);
10390             
10391             if(!f){
10392                 return;
10393             }
10394             
10395             f.hide();
10396             
10397         }, this);
10398     },
10399     
10400     showFields : function(items)
10401     {
10402         Roo.each(items, function(i){
10403             
10404             var f = this.findField(i);
10405             
10406             if(!f){
10407                 return;
10408             }
10409             
10410             f.show();
10411             
10412         }, this);
10413     }
10414
10415 });
10416
10417 Roo.apply(Roo.bootstrap.Form, {
10418     
10419     popover : {
10420         
10421         padding : 5,
10422         
10423         isApplied : false,
10424         
10425         isMasked : false,
10426         
10427         form : false,
10428         
10429         target : false,
10430         
10431         toolTip : false,
10432         
10433         intervalID : false,
10434         
10435         maskEl : false,
10436         
10437         apply : function()
10438         {
10439             if(this.isApplied){
10440                 return;
10441             }
10442             
10443             this.maskEl = {
10444                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10445                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10446                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10447                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10448             };
10449             
10450             this.maskEl.top.enableDisplayMode("block");
10451             this.maskEl.left.enableDisplayMode("block");
10452             this.maskEl.bottom.enableDisplayMode("block");
10453             this.maskEl.right.enableDisplayMode("block");
10454             
10455             this.toolTip = new Roo.bootstrap.Tooltip({
10456                 cls : 'roo-form-error-popover',
10457                 alignment : {
10458                     'left' : ['r-l', [-2,0], 'right'],
10459                     'right' : ['l-r', [2,0], 'left'],
10460                     'bottom' : ['tl-bl', [0,2], 'top'],
10461                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10462                 }
10463             });
10464             
10465             this.toolTip.render(Roo.get(document.body));
10466
10467             this.toolTip.el.enableDisplayMode("block");
10468             
10469             Roo.get(document.body).on('click', function(){
10470                 this.unmask();
10471             }, this);
10472             
10473             Roo.get(document.body).on('touchstart', function(){
10474                 this.unmask();
10475             }, this);
10476             
10477             this.isApplied = true
10478         },
10479         
10480         mask : function(form, target)
10481         {
10482             this.form = form;
10483             
10484             this.target = target;
10485             
10486             if(!this.form.errorMask || !target.el){
10487                 return;
10488             }
10489             
10490             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10491             
10492             Roo.log(scrollable);
10493             
10494             var ot = this.target.el.calcOffsetsTo(scrollable);
10495             
10496             var scrollTo = ot[1] - this.form.maskOffset;
10497             
10498             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10499             
10500             scrollable.scrollTo('top', scrollTo);
10501             
10502             var box = this.target.el.getBox();
10503             Roo.log(box);
10504             var zIndex = Roo.bootstrap.Modal.zIndex++;
10505
10506             
10507             this.maskEl.top.setStyle('position', 'absolute');
10508             this.maskEl.top.setStyle('z-index', zIndex);
10509             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10510             this.maskEl.top.setLeft(0);
10511             this.maskEl.top.setTop(0);
10512             this.maskEl.top.show();
10513             
10514             this.maskEl.left.setStyle('position', 'absolute');
10515             this.maskEl.left.setStyle('z-index', zIndex);
10516             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10517             this.maskEl.left.setLeft(0);
10518             this.maskEl.left.setTop(box.y - this.padding);
10519             this.maskEl.left.show();
10520
10521             this.maskEl.bottom.setStyle('position', 'absolute');
10522             this.maskEl.bottom.setStyle('z-index', zIndex);
10523             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10524             this.maskEl.bottom.setLeft(0);
10525             this.maskEl.bottom.setTop(box.bottom + this.padding);
10526             this.maskEl.bottom.show();
10527
10528             this.maskEl.right.setStyle('position', 'absolute');
10529             this.maskEl.right.setStyle('z-index', zIndex);
10530             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10531             this.maskEl.right.setLeft(box.right + this.padding);
10532             this.maskEl.right.setTop(box.y - this.padding);
10533             this.maskEl.right.show();
10534
10535             this.toolTip.bindEl = this.target.el;
10536
10537             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10538
10539             var tip = this.target.blankText;
10540
10541             if(this.target.getValue() !== '' ) {
10542                 
10543                 if (this.target.invalidText.length) {
10544                     tip = this.target.invalidText;
10545                 } else if (this.target.regexText.length){
10546                     tip = this.target.regexText;
10547                 }
10548             }
10549
10550             this.toolTip.show(tip);
10551
10552             this.intervalID = window.setInterval(function() {
10553                 Roo.bootstrap.Form.popover.unmask();
10554             }, 10000);
10555
10556             window.onwheel = function(){ return false;};
10557             
10558             (function(){ this.isMasked = true; }).defer(500, this);
10559             
10560         },
10561         
10562         unmask : function()
10563         {
10564             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10565                 return;
10566             }
10567             
10568             this.maskEl.top.setStyle('position', 'absolute');
10569             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10570             this.maskEl.top.hide();
10571
10572             this.maskEl.left.setStyle('position', 'absolute');
10573             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10574             this.maskEl.left.hide();
10575
10576             this.maskEl.bottom.setStyle('position', 'absolute');
10577             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10578             this.maskEl.bottom.hide();
10579
10580             this.maskEl.right.setStyle('position', 'absolute');
10581             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10582             this.maskEl.right.hide();
10583             
10584             this.toolTip.hide();
10585             
10586             this.toolTip.el.hide();
10587             
10588             window.onwheel = function(){ return true;};
10589             
10590             if(this.intervalID){
10591                 window.clearInterval(this.intervalID);
10592                 this.intervalID = false;
10593             }
10594             
10595             this.isMasked = false;
10596             
10597         }
10598         
10599     }
10600     
10601 });
10602
10603 /*
10604  * Based on:
10605  * Ext JS Library 1.1.1
10606  * Copyright(c) 2006-2007, Ext JS, LLC.
10607  *
10608  * Originally Released Under LGPL - original licence link has changed is not relivant.
10609  *
10610  * Fork - LGPL
10611  * <script type="text/javascript">
10612  */
10613 /**
10614  * @class Roo.form.VTypes
10615  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10616  * @singleton
10617  */
10618 Roo.form.VTypes = function(){
10619     // closure these in so they are only created once.
10620     var alpha = /^[a-zA-Z_]+$/;
10621     var alphanum = /^[a-zA-Z0-9_]+$/;
10622     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10623     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10624
10625     // All these messages and functions are configurable
10626     return {
10627         /**
10628          * The function used to validate email addresses
10629          * @param {String} value The email address
10630          */
10631         'email' : function(v){
10632             return email.test(v);
10633         },
10634         /**
10635          * The error text to display when the email validation function returns false
10636          * @type String
10637          */
10638         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10639         /**
10640          * The keystroke filter mask to be applied on email input
10641          * @type RegExp
10642          */
10643         'emailMask' : /[a-z0-9_\.\-@]/i,
10644
10645         /**
10646          * The function used to validate URLs
10647          * @param {String} value The URL
10648          */
10649         'url' : function(v){
10650             return url.test(v);
10651         },
10652         /**
10653          * The error text to display when the url validation function returns false
10654          * @type String
10655          */
10656         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10657         
10658         /**
10659          * The function used to validate alpha values
10660          * @param {String} value The value
10661          */
10662         'alpha' : function(v){
10663             return alpha.test(v);
10664         },
10665         /**
10666          * The error text to display when the alpha validation function returns false
10667          * @type String
10668          */
10669         'alphaText' : 'This field should only contain letters and _',
10670         /**
10671          * The keystroke filter mask to be applied on alpha input
10672          * @type RegExp
10673          */
10674         'alphaMask' : /[a-z_]/i,
10675
10676         /**
10677          * The function used to validate alphanumeric values
10678          * @param {String} value The value
10679          */
10680         'alphanum' : function(v){
10681             return alphanum.test(v);
10682         },
10683         /**
10684          * The error text to display when the alphanumeric validation function returns false
10685          * @type String
10686          */
10687         'alphanumText' : 'This field should only contain letters, numbers and _',
10688         /**
10689          * The keystroke filter mask to be applied on alphanumeric input
10690          * @type RegExp
10691          */
10692         'alphanumMask' : /[a-z0-9_]/i
10693     };
10694 }();/*
10695  * - LGPL
10696  *
10697  * Input
10698  * 
10699  */
10700
10701 /**
10702  * @class Roo.bootstrap.Input
10703  * @extends Roo.bootstrap.Component
10704  * Bootstrap Input class
10705  * @cfg {Boolean} disabled is it disabled
10706  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10707  * @cfg {String} name name of the input
10708  * @cfg {string} fieldLabel - the label associated
10709  * @cfg {string} placeholder - placeholder to put in text.
10710  * @cfg {string}  before - input group add on before
10711  * @cfg {string} after - input group add on after
10712  * @cfg {string} size - (lg|sm) or leave empty..
10713  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10714  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10715  * @cfg {Number} md colspan out of 12 for computer-sized screens
10716  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10717  * @cfg {string} value default value of the input
10718  * @cfg {Number} labelWidth set the width of label 
10719  * @cfg {Number} labellg set the width of label (1-12)
10720  * @cfg {Number} labelmd set the width of label (1-12)
10721  * @cfg {Number} labelsm set the width of label (1-12)
10722  * @cfg {Number} labelxs set the width of label (1-12)
10723  * @cfg {String} labelAlign (top|left)
10724  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10725  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10726  * @cfg {String} indicatorpos (left|right) default left
10727  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10728  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10729  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10730
10731  * @cfg {String} align (left|center|right) Default left
10732  * @cfg {Boolean} forceFeedback (true|false) Default false
10733  * 
10734  * @constructor
10735  * Create a new Input
10736  * @param {Object} config The config object
10737  */
10738
10739 Roo.bootstrap.Input = function(config){
10740     
10741     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10742     
10743     this.addEvents({
10744         /**
10745          * @event focus
10746          * Fires when this field receives input focus.
10747          * @param {Roo.form.Field} this
10748          */
10749         focus : true,
10750         /**
10751          * @event blur
10752          * Fires when this field loses input focus.
10753          * @param {Roo.form.Field} this
10754          */
10755         blur : true,
10756         /**
10757          * @event specialkey
10758          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10759          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10760          * @param {Roo.form.Field} this
10761          * @param {Roo.EventObject} e The event object
10762          */
10763         specialkey : true,
10764         /**
10765          * @event change
10766          * Fires just before the field blurs if the field value has changed.
10767          * @param {Roo.form.Field} this
10768          * @param {Mixed} newValue The new value
10769          * @param {Mixed} oldValue The original value
10770          */
10771         change : true,
10772         /**
10773          * @event invalid
10774          * Fires after the field has been marked as invalid.
10775          * @param {Roo.form.Field} this
10776          * @param {String} msg The validation message
10777          */
10778         invalid : true,
10779         /**
10780          * @event valid
10781          * Fires after the field has been validated with no errors.
10782          * @param {Roo.form.Field} this
10783          */
10784         valid : true,
10785          /**
10786          * @event keyup
10787          * Fires after the key up
10788          * @param {Roo.form.Field} this
10789          * @param {Roo.EventObject}  e The event Object
10790          */
10791         keyup : true,
10792         /**
10793          * @event paste
10794          * Fires after the user pastes into input
10795          * @param {Roo.form.Field} this
10796          * @param {Roo.EventObject}  e The event Object
10797          */
10798         paste : true
10799     });
10800 };
10801
10802 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10803      /**
10804      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10805       automatic validation (defaults to "keyup").
10806      */
10807     validationEvent : "keyup",
10808      /**
10809      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10810      */
10811     validateOnBlur : true,
10812     /**
10813      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10814      */
10815     validationDelay : 250,
10816      /**
10817      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10818      */
10819     focusClass : "x-form-focus",  // not needed???
10820     
10821        
10822     /**
10823      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10824      */
10825     invalidClass : "has-warning",
10826     
10827     /**
10828      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10829      */
10830     validClass : "has-success",
10831     
10832     /**
10833      * @cfg {Boolean} hasFeedback (true|false) default true
10834      */
10835     hasFeedback : true,
10836     
10837     /**
10838      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10839      */
10840     invalidFeedbackClass : "glyphicon-warning-sign",
10841     
10842     /**
10843      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10844      */
10845     validFeedbackClass : "glyphicon-ok",
10846     
10847     /**
10848      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10849      */
10850     selectOnFocus : false,
10851     
10852      /**
10853      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10854      */
10855     maskRe : null,
10856        /**
10857      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10858      */
10859     vtype : null,
10860     
10861       /**
10862      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10863      */
10864     disableKeyFilter : false,
10865     
10866        /**
10867      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10868      */
10869     disabled : false,
10870      /**
10871      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10872      */
10873     allowBlank : true,
10874     /**
10875      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10876      */
10877     blankText : "Please complete this mandatory field",
10878     
10879      /**
10880      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10881      */
10882     minLength : 0,
10883     /**
10884      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10885      */
10886     maxLength : Number.MAX_VALUE,
10887     /**
10888      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10889      */
10890     minLengthText : "The minimum length for this field is {0}",
10891     /**
10892      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10893      */
10894     maxLengthText : "The maximum length for this field is {0}",
10895   
10896     
10897     /**
10898      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10899      * If available, this function will be called only after the basic validators all return true, and will be passed the
10900      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10901      */
10902     validator : null,
10903     /**
10904      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10905      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10906      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10907      */
10908     regex : null,
10909     /**
10910      * @cfg {String} regexText -- Depricated - use Invalid Text
10911      */
10912     regexText : "",
10913     
10914     /**
10915      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10916      */
10917     invalidText : "",
10918     
10919     
10920     
10921     autocomplete: false,
10922     
10923     
10924     fieldLabel : '',
10925     inputType : 'text',
10926     
10927     name : false,
10928     placeholder: false,
10929     before : false,
10930     after : false,
10931     size : false,
10932     hasFocus : false,
10933     preventMark: false,
10934     isFormField : true,
10935     value : '',
10936     labelWidth : 2,
10937     labelAlign : false,
10938     readOnly : false,
10939     align : false,
10940     formatedValue : false,
10941     forceFeedback : false,
10942     
10943     indicatorpos : 'left',
10944     
10945     labellg : 0,
10946     labelmd : 0,
10947     labelsm : 0,
10948     labelxs : 0,
10949     
10950     capture : '',
10951     accept : '',
10952     
10953     parentLabelAlign : function()
10954     {
10955         var parent = this;
10956         while (parent.parent()) {
10957             parent = parent.parent();
10958             if (typeof(parent.labelAlign) !='undefined') {
10959                 return parent.labelAlign;
10960             }
10961         }
10962         return 'left';
10963         
10964     },
10965     
10966     getAutoCreate : function()
10967     {
10968         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10969         
10970         var id = Roo.id();
10971         
10972         var cfg = {};
10973         
10974         if(this.inputType != 'hidden'){
10975             cfg.cls = 'form-group' //input-group
10976         }
10977         
10978         var input =  {
10979             tag: 'input',
10980             id : id,
10981             type : this.inputType,
10982             value : this.value,
10983             cls : 'form-control',
10984             placeholder : this.placeholder || '',
10985             autocomplete : this.autocomplete || 'new-password'
10986         };
10987         if (this.inputType == 'file') {
10988             input.style = 'overflow:hidden'; // why not in CSS?
10989         }
10990         
10991         if(this.capture.length){
10992             input.capture = this.capture;
10993         }
10994         
10995         if(this.accept.length){
10996             input.accept = this.accept + "/*";
10997         }
10998         
10999         if(this.align){
11000             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11001         }
11002         
11003         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11004             input.maxLength = this.maxLength;
11005         }
11006         
11007         if (this.disabled) {
11008             input.disabled=true;
11009         }
11010         
11011         if (this.readOnly) {
11012             input.readonly=true;
11013         }
11014         
11015         if (this.name) {
11016             input.name = this.name;
11017         }
11018         
11019         if (this.size) {
11020             input.cls += ' input-' + this.size;
11021         }
11022         
11023         var settings=this;
11024         ['xs','sm','md','lg'].map(function(size){
11025             if (settings[size]) {
11026                 cfg.cls += ' col-' + size + '-' + settings[size];
11027             }
11028         });
11029         
11030         var inputblock = input;
11031         
11032         var feedback = {
11033             tag: 'span',
11034             cls: 'glyphicon form-control-feedback'
11035         };
11036             
11037         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11038             
11039             inputblock = {
11040                 cls : 'has-feedback',
11041                 cn :  [
11042                     input,
11043                     feedback
11044                 ] 
11045             };  
11046         }
11047         
11048         if (this.before || this.after) {
11049             
11050             inputblock = {
11051                 cls : 'input-group',
11052                 cn :  [] 
11053             };
11054             
11055             if (this.before && typeof(this.before) == 'string') {
11056                 
11057                 inputblock.cn.push({
11058                     tag :'span',
11059                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11060                     html : this.before
11061                 });
11062             }
11063             if (this.before && typeof(this.before) == 'object') {
11064                 this.before = Roo.factory(this.before);
11065                 
11066                 inputblock.cn.push({
11067                     tag :'span',
11068                     cls : 'roo-input-before input-group-prepend   input-group-' +
11069                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11070                 });
11071             }
11072             
11073             inputblock.cn.push(input);
11074             
11075             if (this.after && typeof(this.after) == 'string') {
11076                 inputblock.cn.push({
11077                     tag :'span',
11078                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11079                     html : this.after
11080                 });
11081             }
11082             if (this.after && typeof(this.after) == 'object') {
11083                 this.after = Roo.factory(this.after);
11084                 
11085                 inputblock.cn.push({
11086                     tag :'span',
11087                     cls : 'roo-input-after input-group-append  input-group-' +
11088                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11089                 });
11090             }
11091             
11092             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11093                 inputblock.cls += ' has-feedback';
11094                 inputblock.cn.push(feedback);
11095             }
11096         };
11097         var indicator = {
11098             tag : 'i',
11099             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11100             tooltip : 'This field is required'
11101         };
11102         if (this.allowBlank ) {
11103             indicator.style = this.allowBlank ? ' display:none' : '';
11104         }
11105         if (align ==='left' && this.fieldLabel.length) {
11106             
11107             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11108             
11109             cfg.cn = [
11110                 indicator,
11111                 {
11112                     tag: 'label',
11113                     'for' :  id,
11114                     cls : 'control-label col-form-label',
11115                     html : this.fieldLabel
11116
11117                 },
11118                 {
11119                     cls : "", 
11120                     cn: [
11121                         inputblock
11122                     ]
11123                 }
11124             ];
11125             
11126             var labelCfg = cfg.cn[1];
11127             var contentCfg = cfg.cn[2];
11128             
11129             if(this.indicatorpos == 'right'){
11130                 cfg.cn = [
11131                     {
11132                         tag: 'label',
11133                         'for' :  id,
11134                         cls : 'control-label col-form-label',
11135                         cn : [
11136                             {
11137                                 tag : 'span',
11138                                 html : this.fieldLabel
11139                             },
11140                             indicator
11141                         ]
11142                     },
11143                     {
11144                         cls : "",
11145                         cn: [
11146                             inputblock
11147                         ]
11148                     }
11149
11150                 ];
11151                 
11152                 labelCfg = cfg.cn[0];
11153                 contentCfg = cfg.cn[1];
11154             
11155             }
11156             
11157             if(this.labelWidth > 12){
11158                 labelCfg.style = "width: " + this.labelWidth + 'px';
11159             }
11160             
11161             if(this.labelWidth < 13 && this.labelmd == 0){
11162                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11163             }
11164             
11165             if(this.labellg > 0){
11166                 labelCfg.cls += ' col-lg-' + this.labellg;
11167                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11168             }
11169             
11170             if(this.labelmd > 0){
11171                 labelCfg.cls += ' col-md-' + this.labelmd;
11172                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11173             }
11174             
11175             if(this.labelsm > 0){
11176                 labelCfg.cls += ' col-sm-' + this.labelsm;
11177                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11178             }
11179             
11180             if(this.labelxs > 0){
11181                 labelCfg.cls += ' col-xs-' + this.labelxs;
11182                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11183             }
11184             
11185             
11186         } else if ( this.fieldLabel.length) {
11187                 
11188             
11189             
11190             cfg.cn = [
11191                 {
11192                     tag : 'i',
11193                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11194                     tooltip : 'This field is required',
11195                     style : this.allowBlank ? ' display:none' : '' 
11196                 },
11197                 {
11198                     tag: 'label',
11199                    //cls : 'input-group-addon',
11200                     html : this.fieldLabel
11201
11202                 },
11203
11204                inputblock
11205
11206            ];
11207            
11208            if(this.indicatorpos == 'right'){
11209        
11210                 cfg.cn = [
11211                     {
11212                         tag: 'label',
11213                        //cls : 'input-group-addon',
11214                         html : this.fieldLabel
11215
11216                     },
11217                     {
11218                         tag : 'i',
11219                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11220                         tooltip : 'This field is required',
11221                         style : this.allowBlank ? ' display:none' : '' 
11222                     },
11223
11224                    inputblock
11225
11226                ];
11227
11228             }
11229
11230         } else {
11231             
11232             cfg.cn = [
11233
11234                     inputblock
11235
11236             ];
11237                 
11238                 
11239         };
11240         
11241         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11242            cfg.cls += ' navbar-form';
11243         }
11244         
11245         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11246             // on BS4 we do this only if not form 
11247             cfg.cls += ' navbar-form';
11248             cfg.tag = 'li';
11249         }
11250         
11251         return cfg;
11252         
11253     },
11254     /**
11255      * return the real input element.
11256      */
11257     inputEl: function ()
11258     {
11259         return this.el.select('input.form-control',true).first();
11260     },
11261     
11262     tooltipEl : function()
11263     {
11264         return this.inputEl();
11265     },
11266     
11267     indicatorEl : function()
11268     {
11269         if (Roo.bootstrap.version == 4) {
11270             return false; // not enabled in v4 yet.
11271         }
11272         
11273         var indicator = this.el.select('i.roo-required-indicator',true).first();
11274         
11275         if(!indicator){
11276             return false;
11277         }
11278         
11279         return indicator;
11280         
11281     },
11282     
11283     setDisabled : function(v)
11284     {
11285         var i  = this.inputEl().dom;
11286         if (!v) {
11287             i.removeAttribute('disabled');
11288             return;
11289             
11290         }
11291         i.setAttribute('disabled','true');
11292     },
11293     initEvents : function()
11294     {
11295           
11296         this.inputEl().on("keydown" , this.fireKey,  this);
11297         this.inputEl().on("focus", this.onFocus,  this);
11298         this.inputEl().on("blur", this.onBlur,  this);
11299         
11300         this.inputEl().relayEvent('keyup', this);
11301         this.inputEl().relayEvent('paste', this);
11302         
11303         this.indicator = this.indicatorEl();
11304         
11305         if(this.indicator){
11306             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11307         }
11308  
11309         // reference to original value for reset
11310         this.originalValue = this.getValue();
11311         //Roo.form.TextField.superclass.initEvents.call(this);
11312         if(this.validationEvent == 'keyup'){
11313             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11314             this.inputEl().on('keyup', this.filterValidation, this);
11315         }
11316         else if(this.validationEvent !== false){
11317             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11318         }
11319         
11320         if(this.selectOnFocus){
11321             this.on("focus", this.preFocus, this);
11322             
11323         }
11324         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11325             this.inputEl().on("keypress", this.filterKeys, this);
11326         } else {
11327             this.inputEl().relayEvent('keypress', this);
11328         }
11329        /* if(this.grow){
11330             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11331             this.el.on("click", this.autoSize,  this);
11332         }
11333         */
11334         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11335             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11336         }
11337         
11338         if (typeof(this.before) == 'object') {
11339             this.before.render(this.el.select('.roo-input-before',true).first());
11340         }
11341         if (typeof(this.after) == 'object') {
11342             this.after.render(this.el.select('.roo-input-after',true).first());
11343         }
11344         
11345         this.inputEl().on('change', this.onChange, this);
11346         
11347     },
11348     filterValidation : function(e){
11349         if(!e.isNavKeyPress()){
11350             this.validationTask.delay(this.validationDelay);
11351         }
11352     },
11353      /**
11354      * Validates the field value
11355      * @return {Boolean} True if the value is valid, else false
11356      */
11357     validate : function(){
11358         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11359         if(this.disabled || this.validateValue(this.getRawValue())){
11360             this.markValid();
11361             return true;
11362         }
11363         
11364         this.markInvalid();
11365         return false;
11366     },
11367     
11368     
11369     /**
11370      * Validates a value according to the field's validation rules and marks the field as invalid
11371      * if the validation fails
11372      * @param {Mixed} value The value to validate
11373      * @return {Boolean} True if the value is valid, else false
11374      */
11375     validateValue : function(value)
11376     {
11377         if(this.getVisibilityEl().hasClass('hidden')){
11378             return true;
11379         }
11380         
11381         if(value.length < 1)  { // if it's blank
11382             if(this.allowBlank){
11383                 return true;
11384             }
11385             return false;
11386         }
11387         
11388         if(value.length < this.minLength){
11389             return false;
11390         }
11391         if(value.length > this.maxLength){
11392             return false;
11393         }
11394         if(this.vtype){
11395             var vt = Roo.form.VTypes;
11396             if(!vt[this.vtype](value, this)){
11397                 return false;
11398             }
11399         }
11400         if(typeof this.validator == "function"){
11401             var msg = this.validator(value);
11402             if(msg !== true){
11403                 return false;
11404             }
11405             if (typeof(msg) == 'string') {
11406                 this.invalidText = msg;
11407             }
11408         }
11409         
11410         if(this.regex && !this.regex.test(value)){
11411             return false;
11412         }
11413         
11414         return true;
11415     },
11416     
11417      // private
11418     fireKey : function(e){
11419         //Roo.log('field ' + e.getKey());
11420         if(e.isNavKeyPress()){
11421             this.fireEvent("specialkey", this, e);
11422         }
11423     },
11424     focus : function (selectText){
11425         if(this.rendered){
11426             this.inputEl().focus();
11427             if(selectText === true){
11428                 this.inputEl().dom.select();
11429             }
11430         }
11431         return this;
11432     } ,
11433     
11434     onFocus : function(){
11435         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11436            // this.el.addClass(this.focusClass);
11437         }
11438         if(!this.hasFocus){
11439             this.hasFocus = true;
11440             this.startValue = this.getValue();
11441             this.fireEvent("focus", this);
11442         }
11443     },
11444     
11445     beforeBlur : Roo.emptyFn,
11446
11447     
11448     // private
11449     onBlur : function(){
11450         this.beforeBlur();
11451         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11452             //this.el.removeClass(this.focusClass);
11453         }
11454         this.hasFocus = false;
11455         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11456             this.validate();
11457         }
11458         var v = this.getValue();
11459         if(String(v) !== String(this.startValue)){
11460             this.fireEvent('change', this, v, this.startValue);
11461         }
11462         this.fireEvent("blur", this);
11463     },
11464     
11465     onChange : function(e)
11466     {
11467         var v = this.getValue();
11468         if(String(v) !== String(this.startValue)){
11469             this.fireEvent('change', this, v, this.startValue);
11470         }
11471         
11472     },
11473     
11474     /**
11475      * Resets the current field value to the originally loaded value and clears any validation messages
11476      */
11477     reset : function(){
11478         this.setValue(this.originalValue);
11479         this.validate();
11480     },
11481      /**
11482      * Returns the name of the field
11483      * @return {Mixed} name The name field
11484      */
11485     getName: function(){
11486         return this.name;
11487     },
11488      /**
11489      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11490      * @return {Mixed} value The field value
11491      */
11492     getValue : function(){
11493         
11494         var v = this.inputEl().getValue();
11495         
11496         return v;
11497     },
11498     /**
11499      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11500      * @return {Mixed} value The field value
11501      */
11502     getRawValue : function(){
11503         var v = this.inputEl().getValue();
11504         
11505         return v;
11506     },
11507     
11508     /**
11509      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11510      * @param {Mixed} value The value to set
11511      */
11512     setRawValue : function(v){
11513         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11514     },
11515     
11516     selectText : function(start, end){
11517         var v = this.getRawValue();
11518         if(v.length > 0){
11519             start = start === undefined ? 0 : start;
11520             end = end === undefined ? v.length : end;
11521             var d = this.inputEl().dom;
11522             if(d.setSelectionRange){
11523                 d.setSelectionRange(start, end);
11524             }else if(d.createTextRange){
11525                 var range = d.createTextRange();
11526                 range.moveStart("character", start);
11527                 range.moveEnd("character", v.length-end);
11528                 range.select();
11529             }
11530         }
11531     },
11532     
11533     /**
11534      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11535      * @param {Mixed} value The value to set
11536      */
11537     setValue : function(v){
11538         this.value = v;
11539         if(this.rendered){
11540             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11541             this.validate();
11542         }
11543     },
11544     
11545     /*
11546     processValue : function(value){
11547         if(this.stripCharsRe){
11548             var newValue = value.replace(this.stripCharsRe, '');
11549             if(newValue !== value){
11550                 this.setRawValue(newValue);
11551                 return newValue;
11552             }
11553         }
11554         return value;
11555     },
11556   */
11557     preFocus : function(){
11558         
11559         if(this.selectOnFocus){
11560             this.inputEl().dom.select();
11561         }
11562     },
11563     filterKeys : function(e){
11564         var k = e.getKey();
11565         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11566             return;
11567         }
11568         var c = e.getCharCode(), cc = String.fromCharCode(c);
11569         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11570             return;
11571         }
11572         if(!this.maskRe.test(cc)){
11573             e.stopEvent();
11574         }
11575     },
11576      /**
11577      * Clear any invalid styles/messages for this field
11578      */
11579     clearInvalid : function(){
11580         
11581         if(!this.el || this.preventMark){ // not rendered
11582             return;
11583         }
11584         
11585         
11586         this.el.removeClass([this.invalidClass, 'is-invalid']);
11587         
11588         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11589             
11590             var feedback = this.el.select('.form-control-feedback', true).first();
11591             
11592             if(feedback){
11593                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11594             }
11595             
11596         }
11597         
11598         if(this.indicator){
11599             this.indicator.removeClass('visible');
11600             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11601         }
11602         
11603         this.fireEvent('valid', this);
11604     },
11605     
11606      /**
11607      * Mark this field as valid
11608      */
11609     markValid : function()
11610     {
11611         if(!this.el  || this.preventMark){ // not rendered...
11612             return;
11613         }
11614         
11615         this.el.removeClass([this.invalidClass, this.validClass]);
11616         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11617
11618         var feedback = this.el.select('.form-control-feedback', true).first();
11619             
11620         if(feedback){
11621             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11622         }
11623         
11624         if(this.indicator){
11625             this.indicator.removeClass('visible');
11626             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11627         }
11628         
11629         if(this.disabled){
11630             return;
11631         }
11632         
11633            
11634         if(this.allowBlank && !this.getRawValue().length){
11635             return;
11636         }
11637         if (Roo.bootstrap.version == 3) {
11638             this.el.addClass(this.validClass);
11639         } else {
11640             this.inputEl().addClass('is-valid');
11641         }
11642
11643         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11644             
11645             var feedback = this.el.select('.form-control-feedback', true).first();
11646             
11647             if(feedback){
11648                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11649                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11650             }
11651             
11652         }
11653         
11654         this.fireEvent('valid', this);
11655     },
11656     
11657      /**
11658      * Mark this field as invalid
11659      * @param {String} msg The validation message
11660      */
11661     markInvalid : function(msg)
11662     {
11663         if(!this.el  || this.preventMark){ // not rendered
11664             return;
11665         }
11666         
11667         this.el.removeClass([this.invalidClass, this.validClass]);
11668         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11669         
11670         var feedback = this.el.select('.form-control-feedback', true).first();
11671             
11672         if(feedback){
11673             this.el.select('.form-control-feedback', true).first().removeClass(
11674                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11675         }
11676
11677         if(this.disabled){
11678             return;
11679         }
11680         
11681         if(this.allowBlank && !this.getRawValue().length){
11682             return;
11683         }
11684         
11685         if(this.indicator){
11686             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11687             this.indicator.addClass('visible');
11688         }
11689         if (Roo.bootstrap.version == 3) {
11690             this.el.addClass(this.invalidClass);
11691         } else {
11692             this.inputEl().addClass('is-invalid');
11693         }
11694         
11695         
11696         
11697         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11698             
11699             var feedback = this.el.select('.form-control-feedback', true).first();
11700             
11701             if(feedback){
11702                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11703                 
11704                 if(this.getValue().length || this.forceFeedback){
11705                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11706                 }
11707                 
11708             }
11709             
11710         }
11711         
11712         this.fireEvent('invalid', this, msg);
11713     },
11714     // private
11715     SafariOnKeyDown : function(event)
11716     {
11717         // this is a workaround for a password hang bug on chrome/ webkit.
11718         if (this.inputEl().dom.type != 'password') {
11719             return;
11720         }
11721         
11722         var isSelectAll = false;
11723         
11724         if(this.inputEl().dom.selectionEnd > 0){
11725             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11726         }
11727         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11728             event.preventDefault();
11729             this.setValue('');
11730             return;
11731         }
11732         
11733         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11734             
11735             event.preventDefault();
11736             // this is very hacky as keydown always get's upper case.
11737             //
11738             var cc = String.fromCharCode(event.getCharCode());
11739             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11740             
11741         }
11742     },
11743     adjustWidth : function(tag, w){
11744         tag = tag.toLowerCase();
11745         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11746             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11747                 if(tag == 'input'){
11748                     return w + 2;
11749                 }
11750                 if(tag == 'textarea'){
11751                     return w-2;
11752                 }
11753             }else if(Roo.isOpera){
11754                 if(tag == 'input'){
11755                     return w + 2;
11756                 }
11757                 if(tag == 'textarea'){
11758                     return w-2;
11759                 }
11760             }
11761         }
11762         return w;
11763     },
11764     
11765     setFieldLabel : function(v)
11766     {
11767         if(!this.rendered){
11768             return;
11769         }
11770         
11771         if(this.indicatorEl()){
11772             var ar = this.el.select('label > span',true);
11773             
11774             if (ar.elements.length) {
11775                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11776                 this.fieldLabel = v;
11777                 return;
11778             }
11779             
11780             var br = this.el.select('label',true);
11781             
11782             if(br.elements.length) {
11783                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11784                 this.fieldLabel = v;
11785                 return;
11786             }
11787             
11788             Roo.log('Cannot Found any of label > span || label in input');
11789             return;
11790         }
11791         
11792         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11793         this.fieldLabel = v;
11794         
11795         
11796     }
11797 });
11798
11799  
11800 /*
11801  * - LGPL
11802  *
11803  * Input
11804  * 
11805  */
11806
11807 /**
11808  * @class Roo.bootstrap.TextArea
11809  * @extends Roo.bootstrap.Input
11810  * Bootstrap TextArea class
11811  * @cfg {Number} cols Specifies the visible width of a text area
11812  * @cfg {Number} rows Specifies the visible number of lines in a text area
11813  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11814  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11815  * @cfg {string} html text
11816  * 
11817  * @constructor
11818  * Create a new TextArea
11819  * @param {Object} config The config object
11820  */
11821
11822 Roo.bootstrap.TextArea = function(config){
11823     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11824    
11825 };
11826
11827 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11828      
11829     cols : false,
11830     rows : 5,
11831     readOnly : false,
11832     warp : 'soft',
11833     resize : false,
11834     value: false,
11835     html: false,
11836     
11837     getAutoCreate : function(){
11838         
11839         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11840         
11841         var id = Roo.id();
11842         
11843         var cfg = {};
11844         
11845         if(this.inputType != 'hidden'){
11846             cfg.cls = 'form-group' //input-group
11847         }
11848         
11849         var input =  {
11850             tag: 'textarea',
11851             id : id,
11852             warp : this.warp,
11853             rows : this.rows,
11854             value : this.value || '',
11855             html: this.html || '',
11856             cls : 'form-control',
11857             placeholder : this.placeholder || '' 
11858             
11859         };
11860         
11861         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11862             input.maxLength = this.maxLength;
11863         }
11864         
11865         if(this.resize){
11866             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11867         }
11868         
11869         if(this.cols){
11870             input.cols = this.cols;
11871         }
11872         
11873         if (this.readOnly) {
11874             input.readonly = true;
11875         }
11876         
11877         if (this.name) {
11878             input.name = this.name;
11879         }
11880         
11881         if (this.size) {
11882             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11883         }
11884         
11885         var settings=this;
11886         ['xs','sm','md','lg'].map(function(size){
11887             if (settings[size]) {
11888                 cfg.cls += ' col-' + size + '-' + settings[size];
11889             }
11890         });
11891         
11892         var inputblock = input;
11893         
11894         if(this.hasFeedback && !this.allowBlank){
11895             
11896             var feedback = {
11897                 tag: 'span',
11898                 cls: 'glyphicon form-control-feedback'
11899             };
11900
11901             inputblock = {
11902                 cls : 'has-feedback',
11903                 cn :  [
11904                     input,
11905                     feedback
11906                 ] 
11907             };  
11908         }
11909         
11910         
11911         if (this.before || this.after) {
11912             
11913             inputblock = {
11914                 cls : 'input-group',
11915                 cn :  [] 
11916             };
11917             if (this.before) {
11918                 inputblock.cn.push({
11919                     tag :'span',
11920                     cls : 'input-group-addon',
11921                     html : this.before
11922                 });
11923             }
11924             
11925             inputblock.cn.push(input);
11926             
11927             if(this.hasFeedback && !this.allowBlank){
11928                 inputblock.cls += ' has-feedback';
11929                 inputblock.cn.push(feedback);
11930             }
11931             
11932             if (this.after) {
11933                 inputblock.cn.push({
11934                     tag :'span',
11935                     cls : 'input-group-addon',
11936                     html : this.after
11937                 });
11938             }
11939             
11940         }
11941         
11942         if (align ==='left' && this.fieldLabel.length) {
11943             cfg.cn = [
11944                 {
11945                     tag: 'label',
11946                     'for' :  id,
11947                     cls : 'control-label',
11948                     html : this.fieldLabel
11949                 },
11950                 {
11951                     cls : "",
11952                     cn: [
11953                         inputblock
11954                     ]
11955                 }
11956
11957             ];
11958             
11959             if(this.labelWidth > 12){
11960                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11961             }
11962
11963             if(this.labelWidth < 13 && this.labelmd == 0){
11964                 this.labelmd = this.labelWidth;
11965             }
11966
11967             if(this.labellg > 0){
11968                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11969                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11970             }
11971
11972             if(this.labelmd > 0){
11973                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11974                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11975             }
11976
11977             if(this.labelsm > 0){
11978                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11979                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11980             }
11981
11982             if(this.labelxs > 0){
11983                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11984                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11985             }
11986             
11987         } else if ( this.fieldLabel.length) {
11988             cfg.cn = [
11989
11990                {
11991                    tag: 'label',
11992                    //cls : 'input-group-addon',
11993                    html : this.fieldLabel
11994
11995                },
11996
11997                inputblock
11998
11999            ];
12000
12001         } else {
12002
12003             cfg.cn = [
12004
12005                 inputblock
12006
12007             ];
12008                 
12009         }
12010         
12011         if (this.disabled) {
12012             input.disabled=true;
12013         }
12014         
12015         return cfg;
12016         
12017     },
12018     /**
12019      * return the real textarea element.
12020      */
12021     inputEl: function ()
12022     {
12023         return this.el.select('textarea.form-control',true).first();
12024     },
12025     
12026     /**
12027      * Clear any invalid styles/messages for this field
12028      */
12029     clearInvalid : function()
12030     {
12031         
12032         if(!this.el || this.preventMark){ // not rendered
12033             return;
12034         }
12035         
12036         var label = this.el.select('label', true).first();
12037         var icon = this.el.select('i.fa-star', true).first();
12038         
12039         if(label && icon){
12040             icon.remove();
12041         }
12042         this.el.removeClass( this.validClass);
12043         this.inputEl().removeClass('is-invalid');
12044          
12045         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12046             
12047             var feedback = this.el.select('.form-control-feedback', true).first();
12048             
12049             if(feedback){
12050                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12051             }
12052             
12053         }
12054         
12055         this.fireEvent('valid', this);
12056     },
12057     
12058      /**
12059      * Mark this field as valid
12060      */
12061     markValid : function()
12062     {
12063         if(!this.el  || this.preventMark){ // not rendered
12064             return;
12065         }
12066         
12067         this.el.removeClass([this.invalidClass, this.validClass]);
12068         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12069         
12070         var feedback = this.el.select('.form-control-feedback', true).first();
12071             
12072         if(feedback){
12073             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12074         }
12075
12076         if(this.disabled || this.allowBlank){
12077             return;
12078         }
12079         
12080         var label = this.el.select('label', true).first();
12081         var icon = this.el.select('i.fa-star', true).first();
12082         
12083         if(label && icon){
12084             icon.remove();
12085         }
12086         if (Roo.bootstrap.version == 3) {
12087             this.el.addClass(this.validClass);
12088         } else {
12089             this.inputEl().addClass('is-valid');
12090         }
12091         
12092         
12093         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12094             
12095             var feedback = this.el.select('.form-control-feedback', true).first();
12096             
12097             if(feedback){
12098                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12099                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12100             }
12101             
12102         }
12103         
12104         this.fireEvent('valid', this);
12105     },
12106     
12107      /**
12108      * Mark this field as invalid
12109      * @param {String} msg The validation message
12110      */
12111     markInvalid : function(msg)
12112     {
12113         if(!this.el  || this.preventMark){ // not rendered
12114             return;
12115         }
12116         
12117         this.el.removeClass([this.invalidClass, this.validClass]);
12118         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12119         
12120         var feedback = this.el.select('.form-control-feedback', true).first();
12121             
12122         if(feedback){
12123             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12124         }
12125
12126         if(this.disabled || this.allowBlank){
12127             return;
12128         }
12129         
12130         var label = this.el.select('label', true).first();
12131         var icon = this.el.select('i.fa-star', true).first();
12132         
12133         if(!this.getValue().length && label && !icon){
12134             this.el.createChild({
12135                 tag : 'i',
12136                 cls : 'text-danger fa fa-lg fa-star',
12137                 tooltip : 'This field is required',
12138                 style : 'margin-right:5px;'
12139             }, label, true);
12140         }
12141         
12142         if (Roo.bootstrap.version == 3) {
12143             this.el.addClass(this.invalidClass);
12144         } else {
12145             this.inputEl().addClass('is-invalid');
12146         }
12147         
12148         // fixme ... this may be depricated need to test..
12149         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12150             
12151             var feedback = this.el.select('.form-control-feedback', true).first();
12152             
12153             if(feedback){
12154                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12155                 
12156                 if(this.getValue().length || this.forceFeedback){
12157                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12158                 }
12159                 
12160             }
12161             
12162         }
12163         
12164         this.fireEvent('invalid', this, msg);
12165     }
12166 });
12167
12168  
12169 /*
12170  * - LGPL
12171  *
12172  * trigger field - base class for combo..
12173  * 
12174  */
12175  
12176 /**
12177  * @class Roo.bootstrap.TriggerField
12178  * @extends Roo.bootstrap.Input
12179  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12180  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12181  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12182  * for which you can provide a custom implementation.  For example:
12183  * <pre><code>
12184 var trigger = new Roo.bootstrap.TriggerField();
12185 trigger.onTriggerClick = myTriggerFn;
12186 trigger.applyTo('my-field');
12187 </code></pre>
12188  *
12189  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12190  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12191  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12192  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12193  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12194
12195  * @constructor
12196  * Create a new TriggerField.
12197  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12198  * to the base TextField)
12199  */
12200 Roo.bootstrap.TriggerField = function(config){
12201     this.mimicing = false;
12202     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12203 };
12204
12205 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12206     /**
12207      * @cfg {String} triggerClass A CSS class to apply to the trigger
12208      */
12209      /**
12210      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12211      */
12212     hideTrigger:false,
12213
12214     /**
12215      * @cfg {Boolean} removable (true|false) special filter default false
12216      */
12217     removable : false,
12218     
12219     /** @cfg {Boolean} grow @hide */
12220     /** @cfg {Number} growMin @hide */
12221     /** @cfg {Number} growMax @hide */
12222
12223     /**
12224      * @hide 
12225      * @method
12226      */
12227     autoSize: Roo.emptyFn,
12228     // private
12229     monitorTab : true,
12230     // private
12231     deferHeight : true,
12232
12233     
12234     actionMode : 'wrap',
12235     
12236     caret : false,
12237     
12238     
12239     getAutoCreate : function(){
12240        
12241         var align = this.labelAlign || this.parentLabelAlign();
12242         
12243         var id = Roo.id();
12244         
12245         var cfg = {
12246             cls: 'form-group' //input-group
12247         };
12248         
12249         
12250         var input =  {
12251             tag: 'input',
12252             id : id,
12253             type : this.inputType,
12254             cls : 'form-control',
12255             autocomplete: 'new-password',
12256             placeholder : this.placeholder || '' 
12257             
12258         };
12259         if (this.name) {
12260             input.name = this.name;
12261         }
12262         if (this.size) {
12263             input.cls += ' input-' + this.size;
12264         }
12265         
12266         if (this.disabled) {
12267             input.disabled=true;
12268         }
12269         
12270         var inputblock = input;
12271         
12272         if(this.hasFeedback && !this.allowBlank){
12273             
12274             var feedback = {
12275                 tag: 'span',
12276                 cls: 'glyphicon form-control-feedback'
12277             };
12278             
12279             if(this.removable && !this.editable  ){
12280                 inputblock = {
12281                     cls : 'has-feedback',
12282                     cn :  [
12283                         inputblock,
12284                         {
12285                             tag: 'button',
12286                             html : 'x',
12287                             cls : 'roo-combo-removable-btn close'
12288                         },
12289                         feedback
12290                     ] 
12291                 };
12292             } else {
12293                 inputblock = {
12294                     cls : 'has-feedback',
12295                     cn :  [
12296                         inputblock,
12297                         feedback
12298                     ] 
12299                 };
12300             }
12301
12302         } else {
12303             if(this.removable && !this.editable ){
12304                 inputblock = {
12305                     cls : 'roo-removable',
12306                     cn :  [
12307                         inputblock,
12308                         {
12309                             tag: 'button',
12310                             html : 'x',
12311                             cls : 'roo-combo-removable-btn close'
12312                         }
12313                     ] 
12314                 };
12315             }
12316         }
12317         
12318         if (this.before || this.after) {
12319             
12320             inputblock = {
12321                 cls : 'input-group',
12322                 cn :  [] 
12323             };
12324             if (this.before) {
12325                 inputblock.cn.push({
12326                     tag :'span',
12327                     cls : 'input-group-addon input-group-prepend input-group-text',
12328                     html : this.before
12329                 });
12330             }
12331             
12332             inputblock.cn.push(input);
12333             
12334             if(this.hasFeedback && !this.allowBlank){
12335                 inputblock.cls += ' has-feedback';
12336                 inputblock.cn.push(feedback);
12337             }
12338             
12339             if (this.after) {
12340                 inputblock.cn.push({
12341                     tag :'span',
12342                     cls : 'input-group-addon input-group-append input-group-text',
12343                     html : this.after
12344                 });
12345             }
12346             
12347         };
12348         
12349       
12350         
12351         var ibwrap = inputblock;
12352         
12353         if(this.multiple){
12354             ibwrap = {
12355                 tag: 'ul',
12356                 cls: 'roo-select2-choices',
12357                 cn:[
12358                     {
12359                         tag: 'li',
12360                         cls: 'roo-select2-search-field',
12361                         cn: [
12362
12363                             inputblock
12364                         ]
12365                     }
12366                 ]
12367             };
12368                 
12369         }
12370         
12371         var combobox = {
12372             cls: 'roo-select2-container input-group',
12373             cn: [
12374                  {
12375                     tag: 'input',
12376                     type : 'hidden',
12377                     cls: 'form-hidden-field'
12378                 },
12379                 ibwrap
12380             ]
12381         };
12382         
12383         if(!this.multiple && this.showToggleBtn){
12384             
12385             var caret = {
12386                         tag: 'span',
12387                         cls: 'caret'
12388              };
12389             if (this.caret != false) {
12390                 caret = {
12391                      tag: 'i',
12392                      cls: 'fa fa-' + this.caret
12393                 };
12394                 
12395             }
12396             
12397             combobox.cn.push({
12398                 tag :'span',
12399                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12400                 cn : [
12401                     Roo.bootstrap.version == 3 ? caret : '',
12402                     {
12403                         tag: 'span',
12404                         cls: 'combobox-clear',
12405                         cn  : [
12406                             {
12407                                 tag : 'i',
12408                                 cls: 'icon-remove'
12409                             }
12410                         ]
12411                     }
12412                 ]
12413
12414             })
12415         }
12416         
12417         if(this.multiple){
12418             combobox.cls += ' roo-select2-container-multi';
12419         }
12420          var indicator = {
12421             tag : 'i',
12422             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12423             tooltip : 'This field is required'
12424         };
12425         if (Roo.bootstrap.version == 4) {
12426             indicator = {
12427                 tag : 'i',
12428                 style : 'display:none'
12429             };
12430         }
12431         
12432         
12433         if (align ==='left' && this.fieldLabel.length) {
12434             
12435             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12436
12437             cfg.cn = [
12438                 indicator,
12439                 {
12440                     tag: 'label',
12441                     'for' :  id,
12442                     cls : 'control-label',
12443                     html : this.fieldLabel
12444
12445                 },
12446                 {
12447                     cls : "", 
12448                     cn: [
12449                         combobox
12450                     ]
12451                 }
12452
12453             ];
12454             
12455             var labelCfg = cfg.cn[1];
12456             var contentCfg = cfg.cn[2];
12457             
12458             if(this.indicatorpos == 'right'){
12459                 cfg.cn = [
12460                     {
12461                         tag: 'label',
12462                         'for' :  id,
12463                         cls : 'control-label',
12464                         cn : [
12465                             {
12466                                 tag : 'span',
12467                                 html : this.fieldLabel
12468                             },
12469                             indicator
12470                         ]
12471                     },
12472                     {
12473                         cls : "", 
12474                         cn: [
12475                             combobox
12476                         ]
12477                     }
12478
12479                 ];
12480                 
12481                 labelCfg = cfg.cn[0];
12482                 contentCfg = cfg.cn[1];
12483             }
12484             
12485             if(this.labelWidth > 12){
12486                 labelCfg.style = "width: " + this.labelWidth + 'px';
12487             }
12488             
12489             if(this.labelWidth < 13 && this.labelmd == 0){
12490                 this.labelmd = this.labelWidth;
12491             }
12492             
12493             if(this.labellg > 0){
12494                 labelCfg.cls += ' col-lg-' + this.labellg;
12495                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12496             }
12497             
12498             if(this.labelmd > 0){
12499                 labelCfg.cls += ' col-md-' + this.labelmd;
12500                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12501             }
12502             
12503             if(this.labelsm > 0){
12504                 labelCfg.cls += ' col-sm-' + this.labelsm;
12505                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12506             }
12507             
12508             if(this.labelxs > 0){
12509                 labelCfg.cls += ' col-xs-' + this.labelxs;
12510                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12511             }
12512             
12513         } else if ( this.fieldLabel.length) {
12514 //                Roo.log(" label");
12515             cfg.cn = [
12516                 indicator,
12517                {
12518                    tag: 'label',
12519                    //cls : 'input-group-addon',
12520                    html : this.fieldLabel
12521
12522                },
12523
12524                combobox
12525
12526             ];
12527             
12528             if(this.indicatorpos == 'right'){
12529                 
12530                 cfg.cn = [
12531                     {
12532                        tag: 'label',
12533                        cn : [
12534                            {
12535                                tag : 'span',
12536                                html : this.fieldLabel
12537                            },
12538                            indicator
12539                        ]
12540
12541                     },
12542                     combobox
12543
12544                 ];
12545
12546             }
12547
12548         } else {
12549             
12550 //                Roo.log(" no label && no align");
12551                 cfg = combobox
12552                      
12553                 
12554         }
12555         
12556         var settings=this;
12557         ['xs','sm','md','lg'].map(function(size){
12558             if (settings[size]) {
12559                 cfg.cls += ' col-' + size + '-' + settings[size];
12560             }
12561         });
12562         
12563         return cfg;
12564         
12565     },
12566     
12567     
12568     
12569     // private
12570     onResize : function(w, h){
12571 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12572 //        if(typeof w == 'number'){
12573 //            var x = w - this.trigger.getWidth();
12574 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12575 //            this.trigger.setStyle('left', x+'px');
12576 //        }
12577     },
12578
12579     // private
12580     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12581
12582     // private
12583     getResizeEl : function(){
12584         return this.inputEl();
12585     },
12586
12587     // private
12588     getPositionEl : function(){
12589         return this.inputEl();
12590     },
12591
12592     // private
12593     alignErrorIcon : function(){
12594         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12595     },
12596
12597     // private
12598     initEvents : function(){
12599         
12600         this.createList();
12601         
12602         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12603         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12604         if(!this.multiple && this.showToggleBtn){
12605             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12606             if(this.hideTrigger){
12607                 this.trigger.setDisplayed(false);
12608             }
12609             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12610         }
12611         
12612         if(this.multiple){
12613             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12614         }
12615         
12616         if(this.removable && !this.editable && !this.tickable){
12617             var close = this.closeTriggerEl();
12618             
12619             if(close){
12620                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12621                 close.on('click', this.removeBtnClick, this, close);
12622             }
12623         }
12624         
12625         //this.trigger.addClassOnOver('x-form-trigger-over');
12626         //this.trigger.addClassOnClick('x-form-trigger-click');
12627         
12628         //if(!this.width){
12629         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12630         //}
12631     },
12632     
12633     closeTriggerEl : function()
12634     {
12635         var close = this.el.select('.roo-combo-removable-btn', true).first();
12636         return close ? close : false;
12637     },
12638     
12639     removeBtnClick : function(e, h, el)
12640     {
12641         e.preventDefault();
12642         
12643         if(this.fireEvent("remove", this) !== false){
12644             this.reset();
12645             this.fireEvent("afterremove", this)
12646         }
12647     },
12648     
12649     createList : function()
12650     {
12651         this.list = Roo.get(document.body).createChild({
12652             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12653             cls: 'typeahead typeahead-long dropdown-menu shadow',
12654             style: 'display:none'
12655         });
12656         
12657         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12658         
12659     },
12660
12661     // private
12662     initTrigger : function(){
12663        
12664     },
12665
12666     // private
12667     onDestroy : function(){
12668         if(this.trigger){
12669             this.trigger.removeAllListeners();
12670           //  this.trigger.remove();
12671         }
12672         //if(this.wrap){
12673         //    this.wrap.remove();
12674         //}
12675         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12676     },
12677
12678     // private
12679     onFocus : function(){
12680         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12681         /*
12682         if(!this.mimicing){
12683             this.wrap.addClass('x-trigger-wrap-focus');
12684             this.mimicing = true;
12685             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12686             if(this.monitorTab){
12687                 this.el.on("keydown", this.checkTab, this);
12688             }
12689         }
12690         */
12691     },
12692
12693     // private
12694     checkTab : function(e){
12695         if(e.getKey() == e.TAB){
12696             this.triggerBlur();
12697         }
12698     },
12699
12700     // private
12701     onBlur : function(){
12702         // do nothing
12703     },
12704
12705     // private
12706     mimicBlur : function(e, t){
12707         /*
12708         if(!this.wrap.contains(t) && this.validateBlur()){
12709             this.triggerBlur();
12710         }
12711         */
12712     },
12713
12714     // private
12715     triggerBlur : function(){
12716         this.mimicing = false;
12717         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12718         if(this.monitorTab){
12719             this.el.un("keydown", this.checkTab, this);
12720         }
12721         //this.wrap.removeClass('x-trigger-wrap-focus');
12722         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12723     },
12724
12725     // private
12726     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12727     validateBlur : function(e, t){
12728         return true;
12729     },
12730
12731     // private
12732     onDisable : function(){
12733         this.inputEl().dom.disabled = true;
12734         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12735         //if(this.wrap){
12736         //    this.wrap.addClass('x-item-disabled');
12737         //}
12738     },
12739
12740     // private
12741     onEnable : function(){
12742         this.inputEl().dom.disabled = false;
12743         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12744         //if(this.wrap){
12745         //    this.el.removeClass('x-item-disabled');
12746         //}
12747     },
12748
12749     // private
12750     onShow : function(){
12751         var ae = this.getActionEl();
12752         
12753         if(ae){
12754             ae.dom.style.display = '';
12755             ae.dom.style.visibility = 'visible';
12756         }
12757     },
12758
12759     // private
12760     
12761     onHide : function(){
12762         var ae = this.getActionEl();
12763         ae.dom.style.display = 'none';
12764     },
12765
12766     /**
12767      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12768      * by an implementing function.
12769      * @method
12770      * @param {EventObject} e
12771      */
12772     onTriggerClick : Roo.emptyFn
12773 });
12774  
12775 /*
12776 * Licence: LGPL
12777 */
12778
12779 /**
12780  * @class Roo.bootstrap.CardUploader
12781  * @extends Roo.bootstrap.Button
12782  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12783  * @cfg {Number} errorTimeout default 3000
12784  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12785  * @cfg {Array}  html The button text.
12786
12787  *
12788  * @constructor
12789  * Create a new CardUploader
12790  * @param {Object} config The config object
12791  */
12792
12793 Roo.bootstrap.CardUploader = function(config){
12794     
12795  
12796     
12797     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12798     
12799     
12800     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12801         return r.data.id
12802      });
12803     
12804      this.addEvents({
12805          // raw events
12806         /**
12807          * @event preview
12808          * When a image is clicked on - and needs to display a slideshow or similar..
12809          * @param {Roo.bootstrap.Card} this
12810          * @param {Object} The image information data 
12811          *
12812          */
12813         'preview' : true,
12814          /**
12815          * @event download
12816          * When a the download link is clicked
12817          * @param {Roo.bootstrap.Card} this
12818          * @param {Object} The image information data  contains 
12819          */
12820         'download' : true
12821         
12822     });
12823 };
12824  
12825 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12826     
12827      
12828     errorTimeout : 3000,
12829      
12830     images : false,
12831    
12832     fileCollection : false,
12833     allowBlank : true,
12834     
12835     getAutoCreate : function()
12836     {
12837         
12838         var cfg =  {
12839             cls :'form-group' ,
12840             cn : [
12841                
12842                 {
12843                     tag: 'label',
12844                    //cls : 'input-group-addon',
12845                     html : this.fieldLabel
12846
12847                 },
12848
12849                 {
12850                     tag: 'input',
12851                     type : 'hidden',
12852                     name : this.name,
12853                     value : this.value,
12854                     cls : 'd-none  form-control'
12855                 },
12856                 
12857                 {
12858                     tag: 'input',
12859                     multiple : 'multiple',
12860                     type : 'file',
12861                     cls : 'd-none  roo-card-upload-selector'
12862                 },
12863                 
12864                 {
12865                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12866                 },
12867                 {
12868                     cls : 'card-columns roo-card-uploader-container'
12869                 }
12870
12871             ]
12872         };
12873            
12874          
12875         return cfg;
12876     },
12877     
12878     getChildContainer : function() /// what children are added to.
12879     {
12880         return this.containerEl;
12881     },
12882    
12883     getButtonContainer : function() /// what children are added to.
12884     {
12885         return this.el.select(".roo-card-uploader-button-container").first();
12886     },
12887    
12888     initEvents : function()
12889     {
12890         
12891         Roo.bootstrap.Input.prototype.initEvents.call(this);
12892         
12893         var t = this;
12894         this.addxtype({
12895             xns: Roo.bootstrap,
12896
12897             xtype : 'Button',
12898             container_method : 'getButtonContainer' ,            
12899             html :  this.html, // fix changable?
12900             cls : 'w-100 ',
12901             listeners : {
12902                 'click' : function(btn, e) {
12903                     t.onClick(e);
12904                 }
12905             }
12906         });
12907         
12908         
12909         
12910         
12911         this.urlAPI = (window.createObjectURL && window) || 
12912                                 (window.URL && URL.revokeObjectURL && URL) || 
12913                                 (window.webkitURL && webkitURL);
12914                         
12915          
12916          
12917          
12918         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12919         
12920         this.selectorEl.on('change', this.onFileSelected, this);
12921         if (this.images) {
12922             var t = this;
12923             this.images.forEach(function(img) {
12924                 t.addCard(img)
12925             });
12926             this.images = false;
12927         }
12928         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12929          
12930        
12931     },
12932     
12933    
12934     onClick : function(e)
12935     {
12936         e.preventDefault();
12937          
12938         this.selectorEl.dom.click();
12939          
12940     },
12941     
12942     onFileSelected : function(e)
12943     {
12944         e.preventDefault();
12945         
12946         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12947             return;
12948         }
12949         
12950         Roo.each(this.selectorEl.dom.files, function(file){    
12951             this.addFile(file);
12952         }, this);
12953          
12954     },
12955     
12956       
12957     
12958       
12959     
12960     addFile : function(file)
12961     {
12962            
12963         if(typeof(file) === 'string'){
12964             throw "Add file by name?"; // should not happen
12965             return;
12966         }
12967         
12968         if(!file || !this.urlAPI){
12969             return;
12970         }
12971         
12972         // file;
12973         // file.type;
12974         
12975         var _this = this;
12976         
12977         
12978         var url = _this.urlAPI.createObjectURL( file);
12979            
12980         this.addCard({
12981             id : Roo.bootstrap.CardUploader.ID--,
12982             is_uploaded : false,
12983             src : url,
12984             srcfile : file,
12985             title : file.name,
12986             mimetype : file.type,
12987             preview : false,
12988             is_deleted : 0
12989         });
12990         
12991     },
12992     
12993     /**
12994      * addCard - add an Attachment to the uploader
12995      * @param data - the data about the image to upload
12996      *
12997      * {
12998           id : 123
12999           title : "Title of file",
13000           is_uploaded : false,
13001           src : "http://.....",
13002           srcfile : { the File upload object },
13003           mimetype : file.type,
13004           preview : false,
13005           is_deleted : 0
13006           .. any other data...
13007         }
13008      *
13009      * 
13010     */
13011     
13012     addCard : function (data)
13013     {
13014         // hidden input element?
13015         // if the file is not an image...
13016         //then we need to use something other that and header_image
13017         var t = this;
13018         //   remove.....
13019         var footer = [
13020             {
13021                 xns : Roo.bootstrap,
13022                 xtype : 'CardFooter',
13023                  items: [
13024                     {
13025                         xns : Roo.bootstrap,
13026                         xtype : 'Element',
13027                         cls : 'd-flex',
13028                         items : [
13029                             
13030                             {
13031                                 xns : Roo.bootstrap,
13032                                 xtype : 'Button',
13033                                 html : String.format("<small>{0}</small>", data.title),
13034                                 cls : 'col-10 text-left',
13035                                 size: 'sm',
13036                                 weight: 'link',
13037                                 fa : 'download',
13038                                 listeners : {
13039                                     click : function() {
13040                                      
13041                                         t.fireEvent( "download", t, data );
13042                                     }
13043                                 }
13044                             },
13045                           
13046                             {
13047                                 xns : Roo.bootstrap,
13048                                 xtype : 'Button',
13049                                 style: 'max-height: 28px; ',
13050                                 size : 'sm',
13051                                 weight: 'danger',
13052                                 cls : 'col-2',
13053                                 fa : 'times',
13054                                 listeners : {
13055                                     click : function() {
13056                                         t.removeCard(data.id)
13057                                     }
13058                                 }
13059                             }
13060                         ]
13061                     }
13062                     
13063                 ] 
13064             }
13065             
13066         ];
13067         
13068         var cn = this.addxtype(
13069             {
13070                  
13071                 xns : Roo.bootstrap,
13072                 xtype : 'Card',
13073                 closeable : true,
13074                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13075                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13076                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13077                 data : data,
13078                 html : false,
13079                  
13080                 items : footer,
13081                 initEvents : function() {
13082                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13083                     var card = this;
13084                     this.imgEl = this.el.select('.card-img-top').first();
13085                     if (this.imgEl) {
13086                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13087                         this.imgEl.set({ 'pointer' : 'cursor' });
13088                                   
13089                     }
13090                     this.getCardFooter().addClass('p-1');
13091                     
13092                   
13093                 }
13094                 
13095             }
13096         );
13097         // dont' really need ot update items.
13098         // this.items.push(cn);
13099         this.fileCollection.add(cn);
13100         
13101         if (!data.srcfile) {
13102             this.updateInput();
13103             return;
13104         }
13105             
13106         var _t = this;
13107         var reader = new FileReader();
13108         reader.addEventListener("load", function() {  
13109             data.srcdata =  reader.result;
13110             _t.updateInput();
13111         });
13112         reader.readAsDataURL(data.srcfile);
13113         
13114         
13115         
13116     },
13117     removeCard : function(id)
13118     {
13119         
13120         var card  = this.fileCollection.get(id);
13121         card.data.is_deleted = 1;
13122         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13123         //this.fileCollection.remove(card);
13124         //this.items = this.items.filter(function(e) { return e != card });
13125         // dont' really need ot update items.
13126         card.el.dom.parentNode.removeChild(card.el.dom);
13127         this.updateInput();
13128
13129         
13130     },
13131     reset: function()
13132     {
13133         this.fileCollection.each(function(card) {
13134             if (card.el.dom && card.el.dom.parentNode) {
13135                 card.el.dom.parentNode.removeChild(card.el.dom);
13136             }
13137         });
13138         this.fileCollection.clear();
13139         this.updateInput();
13140     },
13141     
13142     updateInput : function()
13143     {
13144          var data = [];
13145         this.fileCollection.each(function(e) {
13146             data.push(e.data);
13147             
13148         });
13149         this.inputEl().dom.value = JSON.stringify(data);
13150         
13151         
13152         
13153     }
13154     
13155     
13156 });
13157
13158
13159 Roo.bootstrap.CardUploader.ID = -1;/*
13160  * Based on:
13161  * Ext JS Library 1.1.1
13162  * Copyright(c) 2006-2007, Ext JS, LLC.
13163  *
13164  * Originally Released Under LGPL - original licence link has changed is not relivant.
13165  *
13166  * Fork - LGPL
13167  * <script type="text/javascript">
13168  */
13169
13170
13171 /**
13172  * @class Roo.data.SortTypes
13173  * @singleton
13174  * Defines the default sorting (casting?) comparison functions used when sorting data.
13175  */
13176 Roo.data.SortTypes = {
13177     /**
13178      * Default sort that does nothing
13179      * @param {Mixed} s The value being converted
13180      * @return {Mixed} The comparison value
13181      */
13182     none : function(s){
13183         return s;
13184     },
13185     
13186     /**
13187      * The regular expression used to strip tags
13188      * @type {RegExp}
13189      * @property
13190      */
13191     stripTagsRE : /<\/?[^>]+>/gi,
13192     
13193     /**
13194      * Strips all HTML tags to sort on text only
13195      * @param {Mixed} s The value being converted
13196      * @return {String} The comparison value
13197      */
13198     asText : function(s){
13199         return String(s).replace(this.stripTagsRE, "");
13200     },
13201     
13202     /**
13203      * Strips all HTML tags to sort on text only - Case insensitive
13204      * @param {Mixed} s The value being converted
13205      * @return {String} The comparison value
13206      */
13207     asUCText : function(s){
13208         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13209     },
13210     
13211     /**
13212      * Case insensitive string
13213      * @param {Mixed} s The value being converted
13214      * @return {String} The comparison value
13215      */
13216     asUCString : function(s) {
13217         return String(s).toUpperCase();
13218     },
13219     
13220     /**
13221      * Date sorting
13222      * @param {Mixed} s The value being converted
13223      * @return {Number} The comparison value
13224      */
13225     asDate : function(s) {
13226         if(!s){
13227             return 0;
13228         }
13229         if(s instanceof Date){
13230             return s.getTime();
13231         }
13232         return Date.parse(String(s));
13233     },
13234     
13235     /**
13236      * Float sorting
13237      * @param {Mixed} s The value being converted
13238      * @return {Float} The comparison value
13239      */
13240     asFloat : function(s) {
13241         var val = parseFloat(String(s).replace(/,/g, ""));
13242         if(isNaN(val)) {
13243             val = 0;
13244         }
13245         return val;
13246     },
13247     
13248     /**
13249      * Integer sorting
13250      * @param {Mixed} s The value being converted
13251      * @return {Number} The comparison value
13252      */
13253     asInt : function(s) {
13254         var val = parseInt(String(s).replace(/,/g, ""));
13255         if(isNaN(val)) {
13256             val = 0;
13257         }
13258         return val;
13259     }
13260 };/*
13261  * Based on:
13262  * Ext JS Library 1.1.1
13263  * Copyright(c) 2006-2007, Ext JS, LLC.
13264  *
13265  * Originally Released Under LGPL - original licence link has changed is not relivant.
13266  *
13267  * Fork - LGPL
13268  * <script type="text/javascript">
13269  */
13270
13271 /**
13272 * @class Roo.data.Record
13273  * Instances of this class encapsulate both record <em>definition</em> information, and record
13274  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13275  * to access Records cached in an {@link Roo.data.Store} object.<br>
13276  * <p>
13277  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13278  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13279  * objects.<br>
13280  * <p>
13281  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13282  * @constructor
13283  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13284  * {@link #create}. The parameters are the same.
13285  * @param {Array} data An associative Array of data values keyed by the field name.
13286  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13287  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13288  * not specified an integer id is generated.
13289  */
13290 Roo.data.Record = function(data, id){
13291     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13292     this.data = data;
13293 };
13294
13295 /**
13296  * Generate a constructor for a specific record layout.
13297  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13298  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13299  * Each field definition object may contain the following properties: <ul>
13300  * <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,
13301  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13302  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13303  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13304  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13305  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13306  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13307  * this may be omitted.</p></li>
13308  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13309  * <ul><li>auto (Default, implies no conversion)</li>
13310  * <li>string</li>
13311  * <li>int</li>
13312  * <li>float</li>
13313  * <li>boolean</li>
13314  * <li>date</li></ul></p></li>
13315  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13316  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13317  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13318  * by the Reader into an object that will be stored in the Record. It is passed the
13319  * following parameters:<ul>
13320  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13321  * </ul></p></li>
13322  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13323  * </ul>
13324  * <br>usage:<br><pre><code>
13325 var TopicRecord = Roo.data.Record.create(
13326     {name: 'title', mapping: 'topic_title'},
13327     {name: 'author', mapping: 'username'},
13328     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13329     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13330     {name: 'lastPoster', mapping: 'user2'},
13331     {name: 'excerpt', mapping: 'post_text'}
13332 );
13333
13334 var myNewRecord = new TopicRecord({
13335     title: 'Do my job please',
13336     author: 'noobie',
13337     totalPosts: 1,
13338     lastPost: new Date(),
13339     lastPoster: 'Animal',
13340     excerpt: 'No way dude!'
13341 });
13342 myStore.add(myNewRecord);
13343 </code></pre>
13344  * @method create
13345  * @static
13346  */
13347 Roo.data.Record.create = function(o){
13348     var f = function(){
13349         f.superclass.constructor.apply(this, arguments);
13350     };
13351     Roo.extend(f, Roo.data.Record);
13352     var p = f.prototype;
13353     p.fields = new Roo.util.MixedCollection(false, function(field){
13354         return field.name;
13355     });
13356     for(var i = 0, len = o.length; i < len; i++){
13357         p.fields.add(new Roo.data.Field(o[i]));
13358     }
13359     f.getField = function(name){
13360         return p.fields.get(name);  
13361     };
13362     return f;
13363 };
13364
13365 Roo.data.Record.AUTO_ID = 1000;
13366 Roo.data.Record.EDIT = 'edit';
13367 Roo.data.Record.REJECT = 'reject';
13368 Roo.data.Record.COMMIT = 'commit';
13369
13370 Roo.data.Record.prototype = {
13371     /**
13372      * Readonly flag - true if this record has been modified.
13373      * @type Boolean
13374      */
13375     dirty : false,
13376     editing : false,
13377     error: null,
13378     modified: null,
13379
13380     // private
13381     join : function(store){
13382         this.store = store;
13383     },
13384
13385     /**
13386      * Set the named field to the specified value.
13387      * @param {String} name The name of the field to set.
13388      * @param {Object} value The value to set the field to.
13389      */
13390     set : function(name, value){
13391         if(this.data[name] == value){
13392             return;
13393         }
13394         this.dirty = true;
13395         if(!this.modified){
13396             this.modified = {};
13397         }
13398         if(typeof this.modified[name] == 'undefined'){
13399             this.modified[name] = this.data[name];
13400         }
13401         this.data[name] = value;
13402         if(!this.editing && this.store){
13403             this.store.afterEdit(this);
13404         }       
13405     },
13406
13407     /**
13408      * Get the value of the named field.
13409      * @param {String} name The name of the field to get the value of.
13410      * @return {Object} The value of the field.
13411      */
13412     get : function(name){
13413         return this.data[name]; 
13414     },
13415
13416     // private
13417     beginEdit : function(){
13418         this.editing = true;
13419         this.modified = {}; 
13420     },
13421
13422     // private
13423     cancelEdit : function(){
13424         this.editing = false;
13425         delete this.modified;
13426     },
13427
13428     // private
13429     endEdit : function(){
13430         this.editing = false;
13431         if(this.dirty && this.store){
13432             this.store.afterEdit(this);
13433         }
13434     },
13435
13436     /**
13437      * Usually called by the {@link Roo.data.Store} which owns the Record.
13438      * Rejects all changes made to the Record since either creation, or the last commit operation.
13439      * Modified fields are reverted to their original values.
13440      * <p>
13441      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13442      * of reject operations.
13443      */
13444     reject : function(){
13445         var m = this.modified;
13446         for(var n in m){
13447             if(typeof m[n] != "function"){
13448                 this.data[n] = m[n];
13449             }
13450         }
13451         this.dirty = false;
13452         delete this.modified;
13453         this.editing = false;
13454         if(this.store){
13455             this.store.afterReject(this);
13456         }
13457     },
13458
13459     /**
13460      * Usually called by the {@link Roo.data.Store} which owns the Record.
13461      * Commits all changes made to the Record since either creation, or the last commit operation.
13462      * <p>
13463      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13464      * of commit operations.
13465      */
13466     commit : function(){
13467         this.dirty = false;
13468         delete this.modified;
13469         this.editing = false;
13470         if(this.store){
13471             this.store.afterCommit(this);
13472         }
13473     },
13474
13475     // private
13476     hasError : function(){
13477         return this.error != null;
13478     },
13479
13480     // private
13481     clearError : function(){
13482         this.error = null;
13483     },
13484
13485     /**
13486      * Creates a copy of this record.
13487      * @param {String} id (optional) A new record id if you don't want to use this record's id
13488      * @return {Record}
13489      */
13490     copy : function(newId) {
13491         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13492     }
13493 };/*
13494  * Based on:
13495  * Ext JS Library 1.1.1
13496  * Copyright(c) 2006-2007, Ext JS, LLC.
13497  *
13498  * Originally Released Under LGPL - original licence link has changed is not relivant.
13499  *
13500  * Fork - LGPL
13501  * <script type="text/javascript">
13502  */
13503
13504
13505
13506 /**
13507  * @class Roo.data.Store
13508  * @extends Roo.util.Observable
13509  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13510  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13511  * <p>
13512  * 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
13513  * has no knowledge of the format of the data returned by the Proxy.<br>
13514  * <p>
13515  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13516  * instances from the data object. These records are cached and made available through accessor functions.
13517  * @constructor
13518  * Creates a new Store.
13519  * @param {Object} config A config object containing the objects needed for the Store to access data,
13520  * and read the data into Records.
13521  */
13522 Roo.data.Store = function(config){
13523     this.data = new Roo.util.MixedCollection(false);
13524     this.data.getKey = function(o){
13525         return o.id;
13526     };
13527     this.baseParams = {};
13528     // private
13529     this.paramNames = {
13530         "start" : "start",
13531         "limit" : "limit",
13532         "sort" : "sort",
13533         "dir" : "dir",
13534         "multisort" : "_multisort"
13535     };
13536
13537     if(config && config.data){
13538         this.inlineData = config.data;
13539         delete config.data;
13540     }
13541
13542     Roo.apply(this, config);
13543     
13544     if(this.reader){ // reader passed
13545         this.reader = Roo.factory(this.reader, Roo.data);
13546         this.reader.xmodule = this.xmodule || false;
13547         if(!this.recordType){
13548             this.recordType = this.reader.recordType;
13549         }
13550         if(this.reader.onMetaChange){
13551             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13552         }
13553     }
13554
13555     if(this.recordType){
13556         this.fields = this.recordType.prototype.fields;
13557     }
13558     this.modified = [];
13559
13560     this.addEvents({
13561         /**
13562          * @event datachanged
13563          * Fires when the data cache has changed, and a widget which is using this Store
13564          * as a Record cache should refresh its view.
13565          * @param {Store} this
13566          */
13567         datachanged : true,
13568         /**
13569          * @event metachange
13570          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13571          * @param {Store} this
13572          * @param {Object} meta The JSON metadata
13573          */
13574         metachange : true,
13575         /**
13576          * @event add
13577          * Fires when Records have been added to the Store
13578          * @param {Store} this
13579          * @param {Roo.data.Record[]} records The array of Records added
13580          * @param {Number} index The index at which the record(s) were added
13581          */
13582         add : true,
13583         /**
13584          * @event remove
13585          * Fires when a Record has been removed from the Store
13586          * @param {Store} this
13587          * @param {Roo.data.Record} record The Record that was removed
13588          * @param {Number} index The index at which the record was removed
13589          */
13590         remove : true,
13591         /**
13592          * @event update
13593          * Fires when a Record has been updated
13594          * @param {Store} this
13595          * @param {Roo.data.Record} record The Record that was updated
13596          * @param {String} operation The update operation being performed.  Value may be one of:
13597          * <pre><code>
13598  Roo.data.Record.EDIT
13599  Roo.data.Record.REJECT
13600  Roo.data.Record.COMMIT
13601          * </code></pre>
13602          */
13603         update : true,
13604         /**
13605          * @event clear
13606          * Fires when the data cache has been cleared.
13607          * @param {Store} this
13608          */
13609         clear : true,
13610         /**
13611          * @event beforeload
13612          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13613          * the load action will be canceled.
13614          * @param {Store} this
13615          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13616          */
13617         beforeload : true,
13618         /**
13619          * @event beforeloadadd
13620          * Fires after a new set of Records has been loaded.
13621          * @param {Store} this
13622          * @param {Roo.data.Record[]} records The Records that were loaded
13623          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13624          */
13625         beforeloadadd : true,
13626         /**
13627          * @event load
13628          * Fires after a new set of Records has been loaded, before they are added to the store.
13629          * @param {Store} this
13630          * @param {Roo.data.Record[]} records The Records that were loaded
13631          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13632          * @params {Object} return from reader
13633          */
13634         load : true,
13635         /**
13636          * @event loadexception
13637          * Fires if an exception occurs in the Proxy during loading.
13638          * Called with the signature of the Proxy's "loadexception" event.
13639          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13640          * 
13641          * @param {Proxy} 
13642          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13643          * @param {Object} load options 
13644          * @param {Object} jsonData from your request (normally this contains the Exception)
13645          */
13646         loadexception : true
13647     });
13648     
13649     if(this.proxy){
13650         this.proxy = Roo.factory(this.proxy, Roo.data);
13651         this.proxy.xmodule = this.xmodule || false;
13652         this.relayEvents(this.proxy,  ["loadexception"]);
13653     }
13654     this.sortToggle = {};
13655     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13656
13657     Roo.data.Store.superclass.constructor.call(this);
13658
13659     if(this.inlineData){
13660         this.loadData(this.inlineData);
13661         delete this.inlineData;
13662     }
13663 };
13664
13665 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13666      /**
13667     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13668     * without a remote query - used by combo/forms at present.
13669     */
13670     
13671     /**
13672     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13673     */
13674     /**
13675     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13676     */
13677     /**
13678     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13679     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13680     */
13681     /**
13682     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13683     * on any HTTP request
13684     */
13685     /**
13686     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13687     */
13688     /**
13689     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13690     */
13691     multiSort: false,
13692     /**
13693     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13694     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13695     */
13696     remoteSort : false,
13697
13698     /**
13699     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13700      * loaded or when a record is removed. (defaults to false).
13701     */
13702     pruneModifiedRecords : false,
13703
13704     // private
13705     lastOptions : null,
13706
13707     /**
13708      * Add Records to the Store and fires the add event.
13709      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13710      */
13711     add : function(records){
13712         records = [].concat(records);
13713         for(var i = 0, len = records.length; i < len; i++){
13714             records[i].join(this);
13715         }
13716         var index = this.data.length;
13717         this.data.addAll(records);
13718         this.fireEvent("add", this, records, index);
13719     },
13720
13721     /**
13722      * Remove a Record from the Store and fires the remove event.
13723      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13724      */
13725     remove : function(record){
13726         var index = this.data.indexOf(record);
13727         this.data.removeAt(index);
13728  
13729         if(this.pruneModifiedRecords){
13730             this.modified.remove(record);
13731         }
13732         this.fireEvent("remove", this, record, index);
13733     },
13734
13735     /**
13736      * Remove all Records from the Store and fires the clear event.
13737      */
13738     removeAll : function(){
13739         this.data.clear();
13740         if(this.pruneModifiedRecords){
13741             this.modified = [];
13742         }
13743         this.fireEvent("clear", this);
13744     },
13745
13746     /**
13747      * Inserts Records to the Store at the given index and fires the add event.
13748      * @param {Number} index The start index at which to insert the passed Records.
13749      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13750      */
13751     insert : function(index, records){
13752         records = [].concat(records);
13753         for(var i = 0, len = records.length; i < len; i++){
13754             this.data.insert(index, records[i]);
13755             records[i].join(this);
13756         }
13757         this.fireEvent("add", this, records, index);
13758     },
13759
13760     /**
13761      * Get the index within the cache of the passed Record.
13762      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13763      * @return {Number} The index of the passed Record. Returns -1 if not found.
13764      */
13765     indexOf : function(record){
13766         return this.data.indexOf(record);
13767     },
13768
13769     /**
13770      * Get the index within the cache of the Record with the passed id.
13771      * @param {String} id The id of the Record to find.
13772      * @return {Number} The index of the Record. Returns -1 if not found.
13773      */
13774     indexOfId : function(id){
13775         return this.data.indexOfKey(id);
13776     },
13777
13778     /**
13779      * Get the Record with the specified id.
13780      * @param {String} id The id of the Record to find.
13781      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13782      */
13783     getById : function(id){
13784         return this.data.key(id);
13785     },
13786
13787     /**
13788      * Get the Record at the specified index.
13789      * @param {Number} index The index of the Record to find.
13790      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13791      */
13792     getAt : function(index){
13793         return this.data.itemAt(index);
13794     },
13795
13796     /**
13797      * Returns a range of Records between specified indices.
13798      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13799      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13800      * @return {Roo.data.Record[]} An array of Records
13801      */
13802     getRange : function(start, end){
13803         return this.data.getRange(start, end);
13804     },
13805
13806     // private
13807     storeOptions : function(o){
13808         o = Roo.apply({}, o);
13809         delete o.callback;
13810         delete o.scope;
13811         this.lastOptions = o;
13812     },
13813
13814     /**
13815      * Loads the Record cache from the configured Proxy using the configured Reader.
13816      * <p>
13817      * If using remote paging, then the first load call must specify the <em>start</em>
13818      * and <em>limit</em> properties in the options.params property to establish the initial
13819      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13820      * <p>
13821      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13822      * and this call will return before the new data has been loaded. Perform any post-processing
13823      * in a callback function, or in a "load" event handler.</strong>
13824      * <p>
13825      * @param {Object} options An object containing properties which control loading options:<ul>
13826      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13827      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13828      * passed the following arguments:<ul>
13829      * <li>r : Roo.data.Record[]</li>
13830      * <li>options: Options object from the load call</li>
13831      * <li>success: Boolean success indicator</li></ul></li>
13832      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13833      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13834      * </ul>
13835      */
13836     load : function(options){
13837         options = options || {};
13838         if(this.fireEvent("beforeload", this, options) !== false){
13839             this.storeOptions(options);
13840             var p = Roo.apply(options.params || {}, this.baseParams);
13841             // if meta was not loaded from remote source.. try requesting it.
13842             if (!this.reader.metaFromRemote) {
13843                 p._requestMeta = 1;
13844             }
13845             if(this.sortInfo && this.remoteSort){
13846                 var pn = this.paramNames;
13847                 p[pn["sort"]] = this.sortInfo.field;
13848                 p[pn["dir"]] = this.sortInfo.direction;
13849             }
13850             if (this.multiSort) {
13851                 var pn = this.paramNames;
13852                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13853             }
13854             
13855             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13856         }
13857     },
13858
13859     /**
13860      * Reloads the Record cache from the configured Proxy using the configured Reader and
13861      * the options from the last load operation performed.
13862      * @param {Object} options (optional) An object containing properties which may override the options
13863      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13864      * the most recently used options are reused).
13865      */
13866     reload : function(options){
13867         this.load(Roo.applyIf(options||{}, this.lastOptions));
13868     },
13869
13870     // private
13871     // Called as a callback by the Reader during a load operation.
13872     loadRecords : function(o, options, success){
13873         if(!o || success === false){
13874             if(success !== false){
13875                 this.fireEvent("load", this, [], options, o);
13876             }
13877             if(options.callback){
13878                 options.callback.call(options.scope || this, [], options, false);
13879             }
13880             return;
13881         }
13882         // if data returned failure - throw an exception.
13883         if (o.success === false) {
13884             // show a message if no listener is registered.
13885             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13886                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13887             }
13888             // loadmask wil be hooked into this..
13889             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13890             return;
13891         }
13892         var r = o.records, t = o.totalRecords || r.length;
13893         
13894         this.fireEvent("beforeloadadd", this, r, options, o);
13895         
13896         if(!options || options.add !== true){
13897             if(this.pruneModifiedRecords){
13898                 this.modified = [];
13899             }
13900             for(var i = 0, len = r.length; i < len; i++){
13901                 r[i].join(this);
13902             }
13903             if(this.snapshot){
13904                 this.data = this.snapshot;
13905                 delete this.snapshot;
13906             }
13907             this.data.clear();
13908             this.data.addAll(r);
13909             this.totalLength = t;
13910             this.applySort();
13911             this.fireEvent("datachanged", this);
13912         }else{
13913             this.totalLength = Math.max(t, this.data.length+r.length);
13914             this.add(r);
13915         }
13916         
13917         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13918                 
13919             var e = new Roo.data.Record({});
13920
13921             e.set(this.parent.displayField, this.parent.emptyTitle);
13922             e.set(this.parent.valueField, '');
13923
13924             this.insert(0, e);
13925         }
13926             
13927         this.fireEvent("load", this, r, options, o);
13928         if(options.callback){
13929             options.callback.call(options.scope || this, r, options, true);
13930         }
13931     },
13932
13933
13934     /**
13935      * Loads data from a passed data block. A Reader which understands the format of the data
13936      * must have been configured in the constructor.
13937      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13938      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13939      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13940      */
13941     loadData : function(o, append){
13942         var r = this.reader.readRecords(o);
13943         this.loadRecords(r, {add: append}, true);
13944     },
13945     
13946      /**
13947      * using 'cn' the nested child reader read the child array into it's child stores.
13948      * @param {Object} rec The record with a 'children array
13949      */
13950     loadDataFromChildren : function(rec)
13951     {
13952         this.loadData(this.reader.toLoadData(rec));
13953     },
13954     
13955
13956     /**
13957      * Gets the number of cached records.
13958      * <p>
13959      * <em>If using paging, this may not be the total size of the dataset. If the data object
13960      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13961      * the data set size</em>
13962      */
13963     getCount : function(){
13964         return this.data.length || 0;
13965     },
13966
13967     /**
13968      * Gets the total number of records in the dataset as returned by the server.
13969      * <p>
13970      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13971      * the dataset size</em>
13972      */
13973     getTotalCount : function(){
13974         return this.totalLength || 0;
13975     },
13976
13977     /**
13978      * Returns the sort state of the Store as an object with two properties:
13979      * <pre><code>
13980  field {String} The name of the field by which the Records are sorted
13981  direction {String} The sort order, "ASC" or "DESC"
13982      * </code></pre>
13983      */
13984     getSortState : function(){
13985         return this.sortInfo;
13986     },
13987
13988     // private
13989     applySort : function(){
13990         if(this.sortInfo && !this.remoteSort){
13991             var s = this.sortInfo, f = s.field;
13992             var st = this.fields.get(f).sortType;
13993             var fn = function(r1, r2){
13994                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13995                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13996             };
13997             this.data.sort(s.direction, fn);
13998             if(this.snapshot && this.snapshot != this.data){
13999                 this.snapshot.sort(s.direction, fn);
14000             }
14001         }
14002     },
14003
14004     /**
14005      * Sets the default sort column and order to be used by the next load operation.
14006      * @param {String} fieldName The name of the field to sort by.
14007      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14008      */
14009     setDefaultSort : function(field, dir){
14010         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14011     },
14012
14013     /**
14014      * Sort the Records.
14015      * If remote sorting is used, the sort is performed on the server, and the cache is
14016      * reloaded. If local sorting is used, the cache is sorted internally.
14017      * @param {String} fieldName The name of the field to sort by.
14018      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14019      */
14020     sort : function(fieldName, dir){
14021         var f = this.fields.get(fieldName);
14022         if(!dir){
14023             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14024             
14025             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14026                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14027             }else{
14028                 dir = f.sortDir;
14029             }
14030         }
14031         this.sortToggle[f.name] = dir;
14032         this.sortInfo = {field: f.name, direction: dir};
14033         if(!this.remoteSort){
14034             this.applySort();
14035             this.fireEvent("datachanged", this);
14036         }else{
14037             this.load(this.lastOptions);
14038         }
14039     },
14040
14041     /**
14042      * Calls the specified function for each of the Records in the cache.
14043      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14044      * Returning <em>false</em> aborts and exits the iteration.
14045      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14046      */
14047     each : function(fn, scope){
14048         this.data.each(fn, scope);
14049     },
14050
14051     /**
14052      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14053      * (e.g., during paging).
14054      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14055      */
14056     getModifiedRecords : function(){
14057         return this.modified;
14058     },
14059
14060     // private
14061     createFilterFn : function(property, value, anyMatch){
14062         if(!value.exec){ // not a regex
14063             value = String(value);
14064             if(value.length == 0){
14065                 return false;
14066             }
14067             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14068         }
14069         return function(r){
14070             return value.test(r.data[property]);
14071         };
14072     },
14073
14074     /**
14075      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14076      * @param {String} property A field on your records
14077      * @param {Number} start The record index to start at (defaults to 0)
14078      * @param {Number} end The last record index to include (defaults to length - 1)
14079      * @return {Number} The sum
14080      */
14081     sum : function(property, start, end){
14082         var rs = this.data.items, v = 0;
14083         start = start || 0;
14084         end = (end || end === 0) ? end : rs.length-1;
14085
14086         for(var i = start; i <= end; i++){
14087             v += (rs[i].data[property] || 0);
14088         }
14089         return v;
14090     },
14091
14092     /**
14093      * Filter the records by a specified property.
14094      * @param {String} field A field on your records
14095      * @param {String/RegExp} value Either a string that the field
14096      * should start with or a RegExp to test against the field
14097      * @param {Boolean} anyMatch True to match any part not just the beginning
14098      */
14099     filter : function(property, value, anyMatch){
14100         var fn = this.createFilterFn(property, value, anyMatch);
14101         return fn ? this.filterBy(fn) : this.clearFilter();
14102     },
14103
14104     /**
14105      * Filter by a function. The specified function will be called with each
14106      * record in this data source. If the function returns true the record is included,
14107      * otherwise it is filtered.
14108      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14109      * @param {Object} scope (optional) The scope of the function (defaults to this)
14110      */
14111     filterBy : function(fn, scope){
14112         this.snapshot = this.snapshot || this.data;
14113         this.data = this.queryBy(fn, scope||this);
14114         this.fireEvent("datachanged", this);
14115     },
14116
14117     /**
14118      * Query the records by a specified property.
14119      * @param {String} field A field on your records
14120      * @param {String/RegExp} value Either a string that the field
14121      * should start with or a RegExp to test against the field
14122      * @param {Boolean} anyMatch True to match any part not just the beginning
14123      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14124      */
14125     query : function(property, value, anyMatch){
14126         var fn = this.createFilterFn(property, value, anyMatch);
14127         return fn ? this.queryBy(fn) : this.data.clone();
14128     },
14129
14130     /**
14131      * Query by a function. The specified function will be called with each
14132      * record in this data source. If the function returns true the record is included
14133      * in the results.
14134      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14135      * @param {Object} scope (optional) The scope of the function (defaults to this)
14136       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14137      **/
14138     queryBy : function(fn, scope){
14139         var data = this.snapshot || this.data;
14140         return data.filterBy(fn, scope||this);
14141     },
14142
14143     /**
14144      * Collects unique values for a particular dataIndex from this store.
14145      * @param {String} dataIndex The property to collect
14146      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14147      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14148      * @return {Array} An array of the unique values
14149      **/
14150     collect : function(dataIndex, allowNull, bypassFilter){
14151         var d = (bypassFilter === true && this.snapshot) ?
14152                 this.snapshot.items : this.data.items;
14153         var v, sv, r = [], l = {};
14154         for(var i = 0, len = d.length; i < len; i++){
14155             v = d[i].data[dataIndex];
14156             sv = String(v);
14157             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14158                 l[sv] = true;
14159                 r[r.length] = v;
14160             }
14161         }
14162         return r;
14163     },
14164
14165     /**
14166      * Revert to a view of the Record cache with no filtering applied.
14167      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14168      */
14169     clearFilter : function(suppressEvent){
14170         if(this.snapshot && this.snapshot != this.data){
14171             this.data = this.snapshot;
14172             delete this.snapshot;
14173             if(suppressEvent !== true){
14174                 this.fireEvent("datachanged", this);
14175             }
14176         }
14177     },
14178
14179     // private
14180     afterEdit : function(record){
14181         if(this.modified.indexOf(record) == -1){
14182             this.modified.push(record);
14183         }
14184         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14185     },
14186     
14187     // private
14188     afterReject : function(record){
14189         this.modified.remove(record);
14190         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14191     },
14192
14193     // private
14194     afterCommit : function(record){
14195         this.modified.remove(record);
14196         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14197     },
14198
14199     /**
14200      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14201      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14202      */
14203     commitChanges : function(){
14204         var m = this.modified.slice(0);
14205         this.modified = [];
14206         for(var i = 0, len = m.length; i < len; i++){
14207             m[i].commit();
14208         }
14209     },
14210
14211     /**
14212      * Cancel outstanding changes on all changed records.
14213      */
14214     rejectChanges : function(){
14215         var m = this.modified.slice(0);
14216         this.modified = [];
14217         for(var i = 0, len = m.length; i < len; i++){
14218             m[i].reject();
14219         }
14220     },
14221
14222     onMetaChange : function(meta, rtype, o){
14223         this.recordType = rtype;
14224         this.fields = rtype.prototype.fields;
14225         delete this.snapshot;
14226         this.sortInfo = meta.sortInfo || this.sortInfo;
14227         this.modified = [];
14228         this.fireEvent('metachange', this, this.reader.meta);
14229     },
14230     
14231     moveIndex : function(data, type)
14232     {
14233         var index = this.indexOf(data);
14234         
14235         var newIndex = index + type;
14236         
14237         this.remove(data);
14238         
14239         this.insert(newIndex, data);
14240         
14241     }
14242 });/*
14243  * Based on:
14244  * Ext JS Library 1.1.1
14245  * Copyright(c) 2006-2007, Ext JS, LLC.
14246  *
14247  * Originally Released Under LGPL - original licence link has changed is not relivant.
14248  *
14249  * Fork - LGPL
14250  * <script type="text/javascript">
14251  */
14252
14253 /**
14254  * @class Roo.data.SimpleStore
14255  * @extends Roo.data.Store
14256  * Small helper class to make creating Stores from Array data easier.
14257  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14258  * @cfg {Array} fields An array of field definition objects, or field name strings.
14259  * @cfg {Object} an existing reader (eg. copied from another store)
14260  * @cfg {Array} data The multi-dimensional array of data
14261  * @constructor
14262  * @param {Object} config
14263  */
14264 Roo.data.SimpleStore = function(config)
14265 {
14266     Roo.data.SimpleStore.superclass.constructor.call(this, {
14267         isLocal : true,
14268         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14269                 id: config.id
14270             },
14271             Roo.data.Record.create(config.fields)
14272         ),
14273         proxy : new Roo.data.MemoryProxy(config.data)
14274     });
14275     this.load();
14276 };
14277 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14278  * Based on:
14279  * Ext JS Library 1.1.1
14280  * Copyright(c) 2006-2007, Ext JS, LLC.
14281  *
14282  * Originally Released Under LGPL - original licence link has changed is not relivant.
14283  *
14284  * Fork - LGPL
14285  * <script type="text/javascript">
14286  */
14287
14288 /**
14289 /**
14290  * @extends Roo.data.Store
14291  * @class Roo.data.JsonStore
14292  * Small helper class to make creating Stores for JSON data easier. <br/>
14293 <pre><code>
14294 var store = new Roo.data.JsonStore({
14295     url: 'get-images.php',
14296     root: 'images',
14297     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14298 });
14299 </code></pre>
14300  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14301  * JsonReader and HttpProxy (unless inline data is provided).</b>
14302  * @cfg {Array} fields An array of field definition objects, or field name strings.
14303  * @constructor
14304  * @param {Object} config
14305  */
14306 Roo.data.JsonStore = function(c){
14307     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14308         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14309         reader: new Roo.data.JsonReader(c, c.fields)
14310     }));
14311 };
14312 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14313  * Based on:
14314  * Ext JS Library 1.1.1
14315  * Copyright(c) 2006-2007, Ext JS, LLC.
14316  *
14317  * Originally Released Under LGPL - original licence link has changed is not relivant.
14318  *
14319  * Fork - LGPL
14320  * <script type="text/javascript">
14321  */
14322
14323  
14324 Roo.data.Field = function(config){
14325     if(typeof config == "string"){
14326         config = {name: config};
14327     }
14328     Roo.apply(this, config);
14329     
14330     if(!this.type){
14331         this.type = "auto";
14332     }
14333     
14334     var st = Roo.data.SortTypes;
14335     // named sortTypes are supported, here we look them up
14336     if(typeof this.sortType == "string"){
14337         this.sortType = st[this.sortType];
14338     }
14339     
14340     // set default sortType for strings and dates
14341     if(!this.sortType){
14342         switch(this.type){
14343             case "string":
14344                 this.sortType = st.asUCString;
14345                 break;
14346             case "date":
14347                 this.sortType = st.asDate;
14348                 break;
14349             default:
14350                 this.sortType = st.none;
14351         }
14352     }
14353
14354     // define once
14355     var stripRe = /[\$,%]/g;
14356
14357     // prebuilt conversion function for this field, instead of
14358     // switching every time we're reading a value
14359     if(!this.convert){
14360         var cv, dateFormat = this.dateFormat;
14361         switch(this.type){
14362             case "":
14363             case "auto":
14364             case undefined:
14365                 cv = function(v){ return v; };
14366                 break;
14367             case "string":
14368                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14369                 break;
14370             case "int":
14371                 cv = function(v){
14372                     return v !== undefined && v !== null && v !== '' ?
14373                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14374                     };
14375                 break;
14376             case "float":
14377                 cv = function(v){
14378                     return v !== undefined && v !== null && v !== '' ?
14379                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14380                     };
14381                 break;
14382             case "bool":
14383             case "boolean":
14384                 cv = function(v){ return v === true || v === "true" || v == 1; };
14385                 break;
14386             case "date":
14387                 cv = function(v){
14388                     if(!v){
14389                         return '';
14390                     }
14391                     if(v instanceof Date){
14392                         return v;
14393                     }
14394                     if(dateFormat){
14395                         if(dateFormat == "timestamp"){
14396                             return new Date(v*1000);
14397                         }
14398                         return Date.parseDate(v, dateFormat);
14399                     }
14400                     var parsed = Date.parse(v);
14401                     return parsed ? new Date(parsed) : null;
14402                 };
14403              break;
14404             
14405         }
14406         this.convert = cv;
14407     }
14408 };
14409
14410 Roo.data.Field.prototype = {
14411     dateFormat: null,
14412     defaultValue: "",
14413     mapping: null,
14414     sortType : null,
14415     sortDir : "ASC"
14416 };/*
14417  * Based on:
14418  * Ext JS Library 1.1.1
14419  * Copyright(c) 2006-2007, Ext JS, LLC.
14420  *
14421  * Originally Released Under LGPL - original licence link has changed is not relivant.
14422  *
14423  * Fork - LGPL
14424  * <script type="text/javascript">
14425  */
14426  
14427 // Base class for reading structured data from a data source.  This class is intended to be
14428 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14429
14430 /**
14431  * @class Roo.data.DataReader
14432  * Base class for reading structured data from a data source.  This class is intended to be
14433  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14434  */
14435
14436 Roo.data.DataReader = function(meta, recordType){
14437     
14438     this.meta = meta;
14439     
14440     this.recordType = recordType instanceof Array ? 
14441         Roo.data.Record.create(recordType) : recordType;
14442 };
14443
14444 Roo.data.DataReader.prototype = {
14445     
14446     
14447     readerType : 'Data',
14448      /**
14449      * Create an empty record
14450      * @param {Object} data (optional) - overlay some values
14451      * @return {Roo.data.Record} record created.
14452      */
14453     newRow :  function(d) {
14454         var da =  {};
14455         this.recordType.prototype.fields.each(function(c) {
14456             switch( c.type) {
14457                 case 'int' : da[c.name] = 0; break;
14458                 case 'date' : da[c.name] = new Date(); break;
14459                 case 'float' : da[c.name] = 0.0; break;
14460                 case 'boolean' : da[c.name] = false; break;
14461                 default : da[c.name] = ""; break;
14462             }
14463             
14464         });
14465         return new this.recordType(Roo.apply(da, d));
14466     }
14467     
14468     
14469 };/*
14470  * Based on:
14471  * Ext JS Library 1.1.1
14472  * Copyright(c) 2006-2007, Ext JS, LLC.
14473  *
14474  * Originally Released Under LGPL - original licence link has changed is not relivant.
14475  *
14476  * Fork - LGPL
14477  * <script type="text/javascript">
14478  */
14479
14480 /**
14481  * @class Roo.data.DataProxy
14482  * @extends Roo.data.Observable
14483  * This class is an abstract base class for implementations which provide retrieval of
14484  * unformatted data objects.<br>
14485  * <p>
14486  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14487  * (of the appropriate type which knows how to parse the data object) to provide a block of
14488  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14489  * <p>
14490  * Custom implementations must implement the load method as described in
14491  * {@link Roo.data.HttpProxy#load}.
14492  */
14493 Roo.data.DataProxy = function(){
14494     this.addEvents({
14495         /**
14496          * @event beforeload
14497          * Fires before a network request is made to retrieve a data object.
14498          * @param {Object} This DataProxy object.
14499          * @param {Object} params The params parameter to the load function.
14500          */
14501         beforeload : true,
14502         /**
14503          * @event load
14504          * Fires before the load method's callback is called.
14505          * @param {Object} This DataProxy object.
14506          * @param {Object} o The data object.
14507          * @param {Object} arg The callback argument object passed to the load function.
14508          */
14509         load : true,
14510         /**
14511          * @event loadexception
14512          * Fires if an Exception occurs during data retrieval.
14513          * @param {Object} This DataProxy object.
14514          * @param {Object} o The data object.
14515          * @param {Object} arg The callback argument object passed to the load function.
14516          * @param {Object} e The Exception.
14517          */
14518         loadexception : true
14519     });
14520     Roo.data.DataProxy.superclass.constructor.call(this);
14521 };
14522
14523 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14524
14525     /**
14526      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14527      */
14528 /*
14529  * Based on:
14530  * Ext JS Library 1.1.1
14531  * Copyright(c) 2006-2007, Ext JS, LLC.
14532  *
14533  * Originally Released Under LGPL - original licence link has changed is not relivant.
14534  *
14535  * Fork - LGPL
14536  * <script type="text/javascript">
14537  */
14538 /**
14539  * @class Roo.data.MemoryProxy
14540  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14541  * to the Reader when its load method is called.
14542  * @constructor
14543  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14544  */
14545 Roo.data.MemoryProxy = function(data){
14546     if (data.data) {
14547         data = data.data;
14548     }
14549     Roo.data.MemoryProxy.superclass.constructor.call(this);
14550     this.data = data;
14551 };
14552
14553 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14554     
14555     /**
14556      * Load data from the requested source (in this case an in-memory
14557      * data object passed to the constructor), read the data object into
14558      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14559      * process that block using the passed callback.
14560      * @param {Object} params This parameter is not used by the MemoryProxy class.
14561      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14562      * object into a block of Roo.data.Records.
14563      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14564      * The function must be passed <ul>
14565      * <li>The Record block object</li>
14566      * <li>The "arg" argument from the load function</li>
14567      * <li>A boolean success indicator</li>
14568      * </ul>
14569      * @param {Object} scope The scope in which to call the callback
14570      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14571      */
14572     load : function(params, reader, callback, scope, arg){
14573         params = params || {};
14574         var result;
14575         try {
14576             result = reader.readRecords(params.data ? params.data :this.data);
14577         }catch(e){
14578             this.fireEvent("loadexception", this, arg, null, e);
14579             callback.call(scope, null, arg, false);
14580             return;
14581         }
14582         callback.call(scope, result, arg, true);
14583     },
14584     
14585     // private
14586     update : function(params, records){
14587         
14588     }
14589 });/*
14590  * Based on:
14591  * Ext JS Library 1.1.1
14592  * Copyright(c) 2006-2007, Ext JS, LLC.
14593  *
14594  * Originally Released Under LGPL - original licence link has changed is not relivant.
14595  *
14596  * Fork - LGPL
14597  * <script type="text/javascript">
14598  */
14599 /**
14600  * @class Roo.data.HttpProxy
14601  * @extends Roo.data.DataProxy
14602  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14603  * configured to reference a certain URL.<br><br>
14604  * <p>
14605  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14606  * from which the running page was served.<br><br>
14607  * <p>
14608  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14609  * <p>
14610  * Be aware that to enable the browser to parse an XML document, the server must set
14611  * the Content-Type header in the HTTP response to "text/xml".
14612  * @constructor
14613  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14614  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14615  * will be used to make the request.
14616  */
14617 Roo.data.HttpProxy = function(conn){
14618     Roo.data.HttpProxy.superclass.constructor.call(this);
14619     // is conn a conn config or a real conn?
14620     this.conn = conn;
14621     this.useAjax = !conn || !conn.events;
14622   
14623 };
14624
14625 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14626     // thse are take from connection...
14627     
14628     /**
14629      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14630      */
14631     /**
14632      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14633      * extra parameters to each request made by this object. (defaults to undefined)
14634      */
14635     /**
14636      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14637      *  to each request made by this object. (defaults to undefined)
14638      */
14639     /**
14640      * @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)
14641      */
14642     /**
14643      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14644      */
14645      /**
14646      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14647      * @type Boolean
14648      */
14649   
14650
14651     /**
14652      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14653      * @type Boolean
14654      */
14655     /**
14656      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14657      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14658      * a finer-grained basis than the DataProxy events.
14659      */
14660     getConnection : function(){
14661         return this.useAjax ? Roo.Ajax : this.conn;
14662     },
14663
14664     /**
14665      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14666      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14667      * process that block using the passed callback.
14668      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14669      * for the request to the remote server.
14670      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14671      * object into a block of Roo.data.Records.
14672      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14673      * The function must be passed <ul>
14674      * <li>The Record block object</li>
14675      * <li>The "arg" argument from the load function</li>
14676      * <li>A boolean success indicator</li>
14677      * </ul>
14678      * @param {Object} scope The scope in which to call the callback
14679      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14680      */
14681     load : function(params, reader, callback, scope, arg){
14682         if(this.fireEvent("beforeload", this, params) !== false){
14683             var  o = {
14684                 params : params || {},
14685                 request: {
14686                     callback : callback,
14687                     scope : scope,
14688                     arg : arg
14689                 },
14690                 reader: reader,
14691                 callback : this.loadResponse,
14692                 scope: this
14693             };
14694             if(this.useAjax){
14695                 Roo.applyIf(o, this.conn);
14696                 if(this.activeRequest){
14697                     Roo.Ajax.abort(this.activeRequest);
14698                 }
14699                 this.activeRequest = Roo.Ajax.request(o);
14700             }else{
14701                 this.conn.request(o);
14702             }
14703         }else{
14704             callback.call(scope||this, null, arg, false);
14705         }
14706     },
14707
14708     // private
14709     loadResponse : function(o, success, response){
14710         delete this.activeRequest;
14711         if(!success){
14712             this.fireEvent("loadexception", this, o, response);
14713             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14714             return;
14715         }
14716         var result;
14717         try {
14718             result = o.reader.read(response);
14719         }catch(e){
14720             this.fireEvent("loadexception", this, o, response, e);
14721             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14722             return;
14723         }
14724         
14725         this.fireEvent("load", this, o, o.request.arg);
14726         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14727     },
14728
14729     // private
14730     update : function(dataSet){
14731
14732     },
14733
14734     // private
14735     updateResponse : function(dataSet){
14736
14737     }
14738 });/*
14739  * Based on:
14740  * Ext JS Library 1.1.1
14741  * Copyright(c) 2006-2007, Ext JS, LLC.
14742  *
14743  * Originally Released Under LGPL - original licence link has changed is not relivant.
14744  *
14745  * Fork - LGPL
14746  * <script type="text/javascript">
14747  */
14748
14749 /**
14750  * @class Roo.data.ScriptTagProxy
14751  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14752  * other than the originating domain of the running page.<br><br>
14753  * <p>
14754  * <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
14755  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14756  * <p>
14757  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14758  * source code that is used as the source inside a &lt;script> tag.<br><br>
14759  * <p>
14760  * In order for the browser to process the returned data, the server must wrap the data object
14761  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14762  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14763  * depending on whether the callback name was passed:
14764  * <p>
14765  * <pre><code>
14766 boolean scriptTag = false;
14767 String cb = request.getParameter("callback");
14768 if (cb != null) {
14769     scriptTag = true;
14770     response.setContentType("text/javascript");
14771 } else {
14772     response.setContentType("application/x-json");
14773 }
14774 Writer out = response.getWriter();
14775 if (scriptTag) {
14776     out.write(cb + "(");
14777 }
14778 out.print(dataBlock.toJsonString());
14779 if (scriptTag) {
14780     out.write(");");
14781 }
14782 </pre></code>
14783  *
14784  * @constructor
14785  * @param {Object} config A configuration object.
14786  */
14787 Roo.data.ScriptTagProxy = function(config){
14788     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14789     Roo.apply(this, config);
14790     this.head = document.getElementsByTagName("head")[0];
14791 };
14792
14793 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14794
14795 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14796     /**
14797      * @cfg {String} url The URL from which to request the data object.
14798      */
14799     /**
14800      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14801      */
14802     timeout : 30000,
14803     /**
14804      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14805      * the server the name of the callback function set up by the load call to process the returned data object.
14806      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14807      * javascript output which calls this named function passing the data object as its only parameter.
14808      */
14809     callbackParam : "callback",
14810     /**
14811      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14812      * name to the request.
14813      */
14814     nocache : true,
14815
14816     /**
14817      * Load data from the configured URL, read the data object into
14818      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14819      * process that block using the passed callback.
14820      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14821      * for the request to the remote server.
14822      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14823      * object into a block of Roo.data.Records.
14824      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14825      * The function must be passed <ul>
14826      * <li>The Record block object</li>
14827      * <li>The "arg" argument from the load function</li>
14828      * <li>A boolean success indicator</li>
14829      * </ul>
14830      * @param {Object} scope The scope in which to call the callback
14831      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14832      */
14833     load : function(params, reader, callback, scope, arg){
14834         if(this.fireEvent("beforeload", this, params) !== false){
14835
14836             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14837
14838             var url = this.url;
14839             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14840             if(this.nocache){
14841                 url += "&_dc=" + (new Date().getTime());
14842             }
14843             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14844             var trans = {
14845                 id : transId,
14846                 cb : "stcCallback"+transId,
14847                 scriptId : "stcScript"+transId,
14848                 params : params,
14849                 arg : arg,
14850                 url : url,
14851                 callback : callback,
14852                 scope : scope,
14853                 reader : reader
14854             };
14855             var conn = this;
14856
14857             window[trans.cb] = function(o){
14858                 conn.handleResponse(o, trans);
14859             };
14860
14861             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14862
14863             if(this.autoAbort !== false){
14864                 this.abort();
14865             }
14866
14867             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14868
14869             var script = document.createElement("script");
14870             script.setAttribute("src", url);
14871             script.setAttribute("type", "text/javascript");
14872             script.setAttribute("id", trans.scriptId);
14873             this.head.appendChild(script);
14874
14875             this.trans = trans;
14876         }else{
14877             callback.call(scope||this, null, arg, false);
14878         }
14879     },
14880
14881     // private
14882     isLoading : function(){
14883         return this.trans ? true : false;
14884     },
14885
14886     /**
14887      * Abort the current server request.
14888      */
14889     abort : function(){
14890         if(this.isLoading()){
14891             this.destroyTrans(this.trans);
14892         }
14893     },
14894
14895     // private
14896     destroyTrans : function(trans, isLoaded){
14897         this.head.removeChild(document.getElementById(trans.scriptId));
14898         clearTimeout(trans.timeoutId);
14899         if(isLoaded){
14900             window[trans.cb] = undefined;
14901             try{
14902                 delete window[trans.cb];
14903             }catch(e){}
14904         }else{
14905             // if hasn't been loaded, wait for load to remove it to prevent script error
14906             window[trans.cb] = function(){
14907                 window[trans.cb] = undefined;
14908                 try{
14909                     delete window[trans.cb];
14910                 }catch(e){}
14911             };
14912         }
14913     },
14914
14915     // private
14916     handleResponse : function(o, trans){
14917         this.trans = false;
14918         this.destroyTrans(trans, true);
14919         var result;
14920         try {
14921             result = trans.reader.readRecords(o);
14922         }catch(e){
14923             this.fireEvent("loadexception", this, o, trans.arg, e);
14924             trans.callback.call(trans.scope||window, null, trans.arg, false);
14925             return;
14926         }
14927         this.fireEvent("load", this, o, trans.arg);
14928         trans.callback.call(trans.scope||window, result, trans.arg, true);
14929     },
14930
14931     // private
14932     handleFailure : function(trans){
14933         this.trans = false;
14934         this.destroyTrans(trans, false);
14935         this.fireEvent("loadexception", this, null, trans.arg);
14936         trans.callback.call(trans.scope||window, null, trans.arg, false);
14937     }
14938 });/*
14939  * Based on:
14940  * Ext JS Library 1.1.1
14941  * Copyright(c) 2006-2007, Ext JS, LLC.
14942  *
14943  * Originally Released Under LGPL - original licence link has changed is not relivant.
14944  *
14945  * Fork - LGPL
14946  * <script type="text/javascript">
14947  */
14948
14949 /**
14950  * @class Roo.data.JsonReader
14951  * @extends Roo.data.DataReader
14952  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14953  * based on mappings in a provided Roo.data.Record constructor.
14954  * 
14955  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14956  * in the reply previously. 
14957  * 
14958  * <p>
14959  * Example code:
14960  * <pre><code>
14961 var RecordDef = Roo.data.Record.create([
14962     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14963     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14964 ]);
14965 var myReader = new Roo.data.JsonReader({
14966     totalProperty: "results",    // The property which contains the total dataset size (optional)
14967     root: "rows",                // The property which contains an Array of row objects
14968     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14969 }, RecordDef);
14970 </code></pre>
14971  * <p>
14972  * This would consume a JSON file like this:
14973  * <pre><code>
14974 { 'results': 2, 'rows': [
14975     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14976     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14977 }
14978 </code></pre>
14979  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14980  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14981  * paged from the remote server.
14982  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14983  * @cfg {String} root name of the property which contains the Array of row objects.
14984  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14985  * @cfg {Array} fields Array of field definition objects
14986  * @constructor
14987  * Create a new JsonReader
14988  * @param {Object} meta Metadata configuration options
14989  * @param {Object} recordType Either an Array of field definition objects,
14990  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14991  */
14992 Roo.data.JsonReader = function(meta, recordType){
14993     
14994     meta = meta || {};
14995     // set some defaults:
14996     Roo.applyIf(meta, {
14997         totalProperty: 'total',
14998         successProperty : 'success',
14999         root : 'data',
15000         id : 'id'
15001     });
15002     
15003     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15004 };
15005 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15006     
15007     readerType : 'Json',
15008     
15009     /**
15010      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
15011      * Used by Store query builder to append _requestMeta to params.
15012      * 
15013      */
15014     metaFromRemote : false,
15015     /**
15016      * This method is only used by a DataProxy which has retrieved data from a remote server.
15017      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15018      * @return {Object} data A data block which is used by an Roo.data.Store object as
15019      * a cache of Roo.data.Records.
15020      */
15021     read : function(response){
15022         var json = response.responseText;
15023        
15024         var o = /* eval:var:o */ eval("("+json+")");
15025         if(!o) {
15026             throw {message: "JsonReader.read: Json object not found"};
15027         }
15028         
15029         if(o.metaData){
15030             
15031             delete this.ef;
15032             this.metaFromRemote = true;
15033             this.meta = o.metaData;
15034             this.recordType = Roo.data.Record.create(o.metaData.fields);
15035             this.onMetaChange(this.meta, this.recordType, o);
15036         }
15037         return this.readRecords(o);
15038     },
15039
15040     // private function a store will implement
15041     onMetaChange : function(meta, recordType, o){
15042
15043     },
15044
15045     /**
15046          * @ignore
15047          */
15048     simpleAccess: function(obj, subsc) {
15049         return obj[subsc];
15050     },
15051
15052         /**
15053          * @ignore
15054          */
15055     getJsonAccessor: function(){
15056         var re = /[\[\.]/;
15057         return function(expr) {
15058             try {
15059                 return(re.test(expr))
15060                     ? new Function("obj", "return obj." + expr)
15061                     : function(obj){
15062                         return obj[expr];
15063                     };
15064             } catch(e){}
15065             return Roo.emptyFn;
15066         };
15067     }(),
15068
15069     /**
15070      * Create a data block containing Roo.data.Records from an XML document.
15071      * @param {Object} o An object which contains an Array of row objects in the property specified
15072      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15073      * which contains the total size of the dataset.
15074      * @return {Object} data A data block which is used by an Roo.data.Store object as
15075      * a cache of Roo.data.Records.
15076      */
15077     readRecords : function(o){
15078         /**
15079          * After any data loads, the raw JSON data is available for further custom processing.
15080          * @type Object
15081          */
15082         this.o = o;
15083         var s = this.meta, Record = this.recordType,
15084             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15085
15086 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15087         if (!this.ef) {
15088             if(s.totalProperty) {
15089                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15090                 }
15091                 if(s.successProperty) {
15092                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15093                 }
15094                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15095                 if (s.id) {
15096                         var g = this.getJsonAccessor(s.id);
15097                         this.getId = function(rec) {
15098                                 var r = g(rec);  
15099                                 return (r === undefined || r === "") ? null : r;
15100                         };
15101                 } else {
15102                         this.getId = function(){return null;};
15103                 }
15104             this.ef = [];
15105             for(var jj = 0; jj < fl; jj++){
15106                 f = fi[jj];
15107                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15108                 this.ef[jj] = this.getJsonAccessor(map);
15109             }
15110         }
15111
15112         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15113         if(s.totalProperty){
15114             var vt = parseInt(this.getTotal(o), 10);
15115             if(!isNaN(vt)){
15116                 totalRecords = vt;
15117             }
15118         }
15119         if(s.successProperty){
15120             var vs = this.getSuccess(o);
15121             if(vs === false || vs === 'false'){
15122                 success = false;
15123             }
15124         }
15125         var records = [];
15126         for(var i = 0; i < c; i++){
15127                 var n = root[i];
15128             var values = {};
15129             var id = this.getId(n);
15130             for(var j = 0; j < fl; j++){
15131                 f = fi[j];
15132             var v = this.ef[j](n);
15133             if (!f.convert) {
15134                 Roo.log('missing convert for ' + f.name);
15135                 Roo.log(f);
15136                 continue;
15137             }
15138             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15139             }
15140             var record = new Record(values, id);
15141             record.json = n;
15142             records[i] = record;
15143         }
15144         return {
15145             raw : o,
15146             success : success,
15147             records : records,
15148             totalRecords : totalRecords
15149         };
15150     },
15151     // used when loading children.. @see loadDataFromChildren
15152     toLoadData: function(rec)
15153     {
15154         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15155         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15156         return { data : data, total : data.length };
15157         
15158     }
15159 });/*
15160  * Based on:
15161  * Ext JS Library 1.1.1
15162  * Copyright(c) 2006-2007, Ext JS, LLC.
15163  *
15164  * Originally Released Under LGPL - original licence link has changed is not relivant.
15165  *
15166  * Fork - LGPL
15167  * <script type="text/javascript">
15168  */
15169
15170 /**
15171  * @class Roo.data.ArrayReader
15172  * @extends Roo.data.DataReader
15173  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15174  * Each element of that Array represents a row of data fields. The
15175  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15176  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15177  * <p>
15178  * Example code:.
15179  * <pre><code>
15180 var RecordDef = Roo.data.Record.create([
15181     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15182     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15183 ]);
15184 var myReader = new Roo.data.ArrayReader({
15185     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15186 }, RecordDef);
15187 </code></pre>
15188  * <p>
15189  * This would consume an Array like this:
15190  * <pre><code>
15191 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15192   </code></pre>
15193  
15194  * @constructor
15195  * Create a new JsonReader
15196  * @param {Object} meta Metadata configuration options.
15197  * @param {Object|Array} recordType Either an Array of field definition objects
15198  * 
15199  * @cfg {Array} fields Array of field definition objects
15200  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15201  * as specified to {@link Roo.data.Record#create},
15202  * or an {@link Roo.data.Record} object
15203  *
15204  * 
15205  * created using {@link Roo.data.Record#create}.
15206  */
15207 Roo.data.ArrayReader = function(meta, recordType)
15208 {    
15209     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15210 };
15211
15212 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15213     
15214       /**
15215      * Create a data block containing Roo.data.Records from an XML document.
15216      * @param {Object} o An Array of row objects which represents the dataset.
15217      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15218      * a cache of Roo.data.Records.
15219      */
15220     readRecords : function(o)
15221     {
15222         var sid = this.meta ? this.meta.id : null;
15223         var recordType = this.recordType, fields = recordType.prototype.fields;
15224         var records = [];
15225         var root = o;
15226         for(var i = 0; i < root.length; i++){
15227             var n = root[i];
15228             var values = {};
15229             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15230             for(var j = 0, jlen = fields.length; j < jlen; j++){
15231                 var f = fields.items[j];
15232                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15233                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15234                 v = f.convert(v);
15235                 values[f.name] = v;
15236             }
15237             var record = new recordType(values, id);
15238             record.json = n;
15239             records[records.length] = record;
15240         }
15241         return {
15242             records : records,
15243             totalRecords : records.length
15244         };
15245     },
15246     // used when loading children.. @see loadDataFromChildren
15247     toLoadData: function(rec)
15248     {
15249         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15250         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15251         
15252     }
15253     
15254     
15255 });/*
15256  * - LGPL
15257  * * 
15258  */
15259
15260 /**
15261  * @class Roo.bootstrap.ComboBox
15262  * @extends Roo.bootstrap.TriggerField
15263  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15264  * @cfg {Boolean} append (true|false) default false
15265  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15266  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15267  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15268  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15269  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15270  * @cfg {Boolean} animate default true
15271  * @cfg {Boolean} emptyResultText only for touch device
15272  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15273  * @cfg {String} emptyTitle default ''
15274  * @cfg {Number} width fixed with? experimental
15275  * @constructor
15276  * Create a new ComboBox.
15277  * @param {Object} config Configuration options
15278  */
15279 Roo.bootstrap.ComboBox = function(config){
15280     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15281     this.addEvents({
15282         /**
15283          * @event expand
15284          * Fires when the dropdown list is expanded
15285         * @param {Roo.bootstrap.ComboBox} combo This combo box
15286         */
15287         'expand' : true,
15288         /**
15289          * @event collapse
15290          * Fires when the dropdown list is collapsed
15291         * @param {Roo.bootstrap.ComboBox} combo This combo box
15292         */
15293         'collapse' : true,
15294         /**
15295          * @event beforeselect
15296          * Fires before a list item is selected. Return false to cancel the selection.
15297         * @param {Roo.bootstrap.ComboBox} combo This combo box
15298         * @param {Roo.data.Record} record The data record returned from the underlying store
15299         * @param {Number} index The index of the selected item in the dropdown list
15300         */
15301         'beforeselect' : true,
15302         /**
15303          * @event select
15304          * Fires when a list item is selected
15305         * @param {Roo.bootstrap.ComboBox} combo This combo box
15306         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15307         * @param {Number} index The index of the selected item in the dropdown list
15308         */
15309         'select' : true,
15310         /**
15311          * @event beforequery
15312          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15313          * The event object passed has these properties:
15314         * @param {Roo.bootstrap.ComboBox} combo This combo box
15315         * @param {String} query The query
15316         * @param {Boolean} forceAll true to force "all" query
15317         * @param {Boolean} cancel true to cancel the query
15318         * @param {Object} e The query event object
15319         */
15320         'beforequery': true,
15321          /**
15322          * @event add
15323          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15324         * @param {Roo.bootstrap.ComboBox} combo This combo box
15325         */
15326         'add' : true,
15327         /**
15328          * @event edit
15329          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15330         * @param {Roo.bootstrap.ComboBox} combo This combo box
15331         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15332         */
15333         'edit' : true,
15334         /**
15335          * @event remove
15336          * Fires when the remove value from the combobox array
15337         * @param {Roo.bootstrap.ComboBox} combo This combo box
15338         */
15339         'remove' : true,
15340         /**
15341          * @event afterremove
15342          * Fires when the remove value from the combobox array
15343         * @param {Roo.bootstrap.ComboBox} combo This combo box
15344         */
15345         'afterremove' : true,
15346         /**
15347          * @event specialfilter
15348          * Fires when specialfilter
15349             * @param {Roo.bootstrap.ComboBox} combo This combo box
15350             */
15351         'specialfilter' : true,
15352         /**
15353          * @event tick
15354          * Fires when tick the element
15355             * @param {Roo.bootstrap.ComboBox} combo This combo box
15356             */
15357         'tick' : true,
15358         /**
15359          * @event touchviewdisplay
15360          * Fires when touch view require special display (default is using displayField)
15361             * @param {Roo.bootstrap.ComboBox} combo This combo box
15362             * @param {Object} cfg set html .
15363             */
15364         'touchviewdisplay' : true
15365         
15366     });
15367     
15368     this.item = [];
15369     this.tickItems = [];
15370     
15371     this.selectedIndex = -1;
15372     if(this.mode == 'local'){
15373         if(config.queryDelay === undefined){
15374             this.queryDelay = 10;
15375         }
15376         if(config.minChars === undefined){
15377             this.minChars = 0;
15378         }
15379     }
15380 };
15381
15382 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15383      
15384     /**
15385      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15386      * rendering into an Roo.Editor, defaults to false)
15387      */
15388     /**
15389      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15390      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15391      */
15392     /**
15393      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15394      */
15395     /**
15396      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15397      * the dropdown list (defaults to undefined, with no header element)
15398      */
15399
15400      /**
15401      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15402      */
15403      
15404      /**
15405      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15406      */
15407     listWidth: undefined,
15408     /**
15409      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15410      * mode = 'remote' or 'text' if mode = 'local')
15411      */
15412     displayField: undefined,
15413     
15414     /**
15415      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15416      * mode = 'remote' or 'value' if mode = 'local'). 
15417      * Note: use of a valueField requires the user make a selection
15418      * in order for a value to be mapped.
15419      */
15420     valueField: undefined,
15421     /**
15422      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15423      */
15424     modalTitle : '',
15425     
15426     /**
15427      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15428      * field's data value (defaults to the underlying DOM element's name)
15429      */
15430     hiddenName: undefined,
15431     /**
15432      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15433      */
15434     listClass: '',
15435     /**
15436      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15437      */
15438     selectedClass: 'active',
15439     
15440     /**
15441      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15442      */
15443     shadow:'sides',
15444     /**
15445      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15446      * anchor positions (defaults to 'tl-bl')
15447      */
15448     listAlign: 'tl-bl?',
15449     /**
15450      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15451      */
15452     maxHeight: 300,
15453     /**
15454      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15455      * query specified by the allQuery config option (defaults to 'query')
15456      */
15457     triggerAction: 'query',
15458     /**
15459      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15460      * (defaults to 4, does not apply if editable = false)
15461      */
15462     minChars : 4,
15463     /**
15464      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15465      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15466      */
15467     typeAhead: false,
15468     /**
15469      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15470      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15471      */
15472     queryDelay: 500,
15473     /**
15474      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15475      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15476      */
15477     pageSize: 0,
15478     /**
15479      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15480      * when editable = true (defaults to false)
15481      */
15482     selectOnFocus:false,
15483     /**
15484      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15485      */
15486     queryParam: 'query',
15487     /**
15488      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15489      * when mode = 'remote' (defaults to 'Loading...')
15490      */
15491     loadingText: 'Loading...',
15492     /**
15493      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15494      */
15495     resizable: false,
15496     /**
15497      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15498      */
15499     handleHeight : 8,
15500     /**
15501      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15502      * traditional select (defaults to true)
15503      */
15504     editable: true,
15505     /**
15506      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15507      */
15508     allQuery: '',
15509     /**
15510      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15511      */
15512     mode: 'remote',
15513     /**
15514      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15515      * listWidth has a higher value)
15516      */
15517     minListWidth : 70,
15518     /**
15519      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15520      * allow the user to set arbitrary text into the field (defaults to false)
15521      */
15522     forceSelection:false,
15523     /**
15524      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15525      * if typeAhead = true (defaults to 250)
15526      */
15527     typeAheadDelay : 250,
15528     /**
15529      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15530      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15531      */
15532     valueNotFoundText : undefined,
15533     /**
15534      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15535      */
15536     blockFocus : false,
15537     
15538     /**
15539      * @cfg {Boolean} disableClear Disable showing of clear button.
15540      */
15541     disableClear : false,
15542     /**
15543      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15544      */
15545     alwaysQuery : false,
15546     
15547     /**
15548      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15549      */
15550     multiple : false,
15551     
15552     /**
15553      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15554      */
15555     invalidClass : "has-warning",
15556     
15557     /**
15558      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15559      */
15560     validClass : "has-success",
15561     
15562     /**
15563      * @cfg {Boolean} specialFilter (true|false) special filter default false
15564      */
15565     specialFilter : false,
15566     
15567     /**
15568      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15569      */
15570     mobileTouchView : true,
15571     
15572     /**
15573      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15574      */
15575     useNativeIOS : false,
15576     
15577     /**
15578      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15579      */
15580     mobile_restrict_height : false,
15581     
15582     ios_options : false,
15583     
15584     //private
15585     addicon : false,
15586     editicon: false,
15587     
15588     page: 0,
15589     hasQuery: false,
15590     append: false,
15591     loadNext: false,
15592     autoFocus : true,
15593     tickable : false,
15594     btnPosition : 'right',
15595     triggerList : true,
15596     showToggleBtn : true,
15597     animate : true,
15598     emptyResultText: 'Empty',
15599     triggerText : 'Select',
15600     emptyTitle : '',
15601     width : false,
15602     
15603     // element that contains real text value.. (when hidden is used..)
15604     
15605     getAutoCreate : function()
15606     {   
15607         var cfg = false;
15608         //render
15609         /*
15610          * Render classic select for iso
15611          */
15612         
15613         if(Roo.isIOS && this.useNativeIOS){
15614             cfg = this.getAutoCreateNativeIOS();
15615             return cfg;
15616         }
15617         
15618         /*
15619          * Touch Devices
15620          */
15621         
15622         if(Roo.isTouch && this.mobileTouchView){
15623             cfg = this.getAutoCreateTouchView();
15624             return cfg;;
15625         }
15626         
15627         /*
15628          *  Normal ComboBox
15629          */
15630         if(!this.tickable){
15631             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15632             return cfg;
15633         }
15634         
15635         /*
15636          *  ComboBox with tickable selections
15637          */
15638              
15639         var align = this.labelAlign || this.parentLabelAlign();
15640         
15641         cfg = {
15642             cls : 'form-group roo-combobox-tickable' //input-group
15643         };
15644         
15645         var btn_text_select = '';
15646         var btn_text_done = '';
15647         var btn_text_cancel = '';
15648         
15649         if (this.btn_text_show) {
15650             btn_text_select = 'Select';
15651             btn_text_done = 'Done';
15652             btn_text_cancel = 'Cancel'; 
15653         }
15654         
15655         var buttons = {
15656             tag : 'div',
15657             cls : 'tickable-buttons',
15658             cn : [
15659                 {
15660                     tag : 'button',
15661                     type : 'button',
15662                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15663                     //html : this.triggerText
15664                     html: btn_text_select
15665                 },
15666                 {
15667                     tag : 'button',
15668                     type : 'button',
15669                     name : 'ok',
15670                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15671                     //html : 'Done'
15672                     html: btn_text_done
15673                 },
15674                 {
15675                     tag : 'button',
15676                     type : 'button',
15677                     name : 'cancel',
15678                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15679                     //html : 'Cancel'
15680                     html: btn_text_cancel
15681                 }
15682             ]
15683         };
15684         
15685         if(this.editable){
15686             buttons.cn.unshift({
15687                 tag: 'input',
15688                 cls: 'roo-select2-search-field-input'
15689             });
15690         }
15691         
15692         var _this = this;
15693         
15694         Roo.each(buttons.cn, function(c){
15695             if (_this.size) {
15696                 c.cls += ' btn-' + _this.size;
15697             }
15698
15699             if (_this.disabled) {
15700                 c.disabled = true;
15701             }
15702         });
15703         
15704         var box = {
15705             tag: 'div',
15706             style : 'display: contents',
15707             cn: [
15708                 {
15709                     tag: 'input',
15710                     type : 'hidden',
15711                     cls: 'form-hidden-field'
15712                 },
15713                 {
15714                     tag: 'ul',
15715                     cls: 'roo-select2-choices',
15716                     cn:[
15717                         {
15718                             tag: 'li',
15719                             cls: 'roo-select2-search-field',
15720                             cn: [
15721                                 buttons
15722                             ]
15723                         }
15724                     ]
15725                 }
15726             ]
15727         };
15728         
15729         var combobox = {
15730             cls: 'roo-select2-container input-group roo-select2-container-multi',
15731             cn: [
15732                 
15733                 box
15734 //                {
15735 //                    tag: 'ul',
15736 //                    cls: 'typeahead typeahead-long dropdown-menu',
15737 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15738 //                }
15739             ]
15740         };
15741         
15742         if(this.hasFeedback && !this.allowBlank){
15743             
15744             var feedback = {
15745                 tag: 'span',
15746                 cls: 'glyphicon form-control-feedback'
15747             };
15748
15749             combobox.cn.push(feedback);
15750         }
15751         
15752         
15753         
15754         var indicator = {
15755             tag : 'i',
15756             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15757             tooltip : 'This field is required'
15758         };
15759         if (Roo.bootstrap.version == 4) {
15760             indicator = {
15761                 tag : 'i',
15762                 style : 'display:none'
15763             };
15764         }
15765         if (align ==='left' && this.fieldLabel.length) {
15766             
15767             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15768             
15769             cfg.cn = [
15770                 indicator,
15771                 {
15772                     tag: 'label',
15773                     'for' :  id,
15774                     cls : 'control-label col-form-label',
15775                     html : this.fieldLabel
15776
15777                 },
15778                 {
15779                     cls : "", 
15780                     cn: [
15781                         combobox
15782                     ]
15783                 }
15784
15785             ];
15786             
15787             var labelCfg = cfg.cn[1];
15788             var contentCfg = cfg.cn[2];
15789             
15790
15791             if(this.indicatorpos == 'right'){
15792                 
15793                 cfg.cn = [
15794                     {
15795                         tag: 'label',
15796                         'for' :  id,
15797                         cls : 'control-label col-form-label',
15798                         cn : [
15799                             {
15800                                 tag : 'span',
15801                                 html : this.fieldLabel
15802                             },
15803                             indicator
15804                         ]
15805                     },
15806                     {
15807                         cls : "",
15808                         cn: [
15809                             combobox
15810                         ]
15811                     }
15812
15813                 ];
15814                 
15815                 
15816                 
15817                 labelCfg = cfg.cn[0];
15818                 contentCfg = cfg.cn[1];
15819             
15820             }
15821             
15822             if(this.labelWidth > 12){
15823                 labelCfg.style = "width: " + this.labelWidth + 'px';
15824             }
15825             if(this.width * 1 > 0){
15826                 contentCfg.style = "width: " + this.width + 'px';
15827             }
15828             if(this.labelWidth < 13 && this.labelmd == 0){
15829                 this.labelmd = this.labelWidth;
15830             }
15831             
15832             if(this.labellg > 0){
15833                 labelCfg.cls += ' col-lg-' + this.labellg;
15834                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15835             }
15836             
15837             if(this.labelmd > 0){
15838                 labelCfg.cls += ' col-md-' + this.labelmd;
15839                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15840             }
15841             
15842             if(this.labelsm > 0){
15843                 labelCfg.cls += ' col-sm-' + this.labelsm;
15844                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15845             }
15846             
15847             if(this.labelxs > 0){
15848                 labelCfg.cls += ' col-xs-' + this.labelxs;
15849                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15850             }
15851                 
15852                 
15853         } else if ( this.fieldLabel.length) {
15854 //                Roo.log(" label");
15855                  cfg.cn = [
15856                    indicator,
15857                     {
15858                         tag: 'label',
15859                         //cls : 'input-group-addon',
15860                         html : this.fieldLabel
15861                     },
15862                     combobox
15863                 ];
15864                 
15865                 if(this.indicatorpos == 'right'){
15866                     cfg.cn = [
15867                         {
15868                             tag: 'label',
15869                             //cls : 'input-group-addon',
15870                             html : this.fieldLabel
15871                         },
15872                         indicator,
15873                         combobox
15874                     ];
15875                     
15876                 }
15877
15878         } else {
15879             
15880 //                Roo.log(" no label && no align");
15881                 cfg = combobox
15882                      
15883                 
15884         }
15885          
15886         var settings=this;
15887         ['xs','sm','md','lg'].map(function(size){
15888             if (settings[size]) {
15889                 cfg.cls += ' col-' + size + '-' + settings[size];
15890             }
15891         });
15892         
15893         return cfg;
15894         
15895     },
15896     
15897     _initEventsCalled : false,
15898     
15899     // private
15900     initEvents: function()
15901     {   
15902         if (this._initEventsCalled) { // as we call render... prevent looping...
15903             return;
15904         }
15905         this._initEventsCalled = true;
15906         
15907         if (!this.store) {
15908             throw "can not find store for combo";
15909         }
15910         
15911         this.indicator = this.indicatorEl();
15912         
15913         this.store = Roo.factory(this.store, Roo.data);
15914         this.store.parent = this;
15915         
15916         // if we are building from html. then this element is so complex, that we can not really
15917         // use the rendered HTML.
15918         // so we have to trash and replace the previous code.
15919         if (Roo.XComponent.build_from_html) {
15920             // remove this element....
15921             var e = this.el.dom, k=0;
15922             while (e ) { e = e.previousSibling;  ++k;}
15923
15924             this.el.remove();
15925             
15926             this.el=false;
15927             this.rendered = false;
15928             
15929             this.render(this.parent().getChildContainer(true), k);
15930         }
15931         
15932         if(Roo.isIOS && this.useNativeIOS){
15933             this.initIOSView();
15934             return;
15935         }
15936         
15937         /*
15938          * Touch Devices
15939          */
15940         
15941         if(Roo.isTouch && this.mobileTouchView){
15942             this.initTouchView();
15943             return;
15944         }
15945         
15946         if(this.tickable){
15947             this.initTickableEvents();
15948             return;
15949         }
15950         
15951         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15952         
15953         if(this.hiddenName){
15954             
15955             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15956             
15957             this.hiddenField.dom.value =
15958                 this.hiddenValue !== undefined ? this.hiddenValue :
15959                 this.value !== undefined ? this.value : '';
15960
15961             // prevent input submission
15962             this.el.dom.removeAttribute('name');
15963             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15964              
15965              
15966         }
15967         //if(Roo.isGecko){
15968         //    this.el.dom.setAttribute('autocomplete', 'off');
15969         //}
15970         
15971         var cls = 'x-combo-list';
15972         
15973         //this.list = new Roo.Layer({
15974         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15975         //});
15976         
15977         var _this = this;
15978         
15979         (function(){
15980             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15981             _this.list.setWidth(lw);
15982         }).defer(100);
15983         
15984         this.list.on('mouseover', this.onViewOver, this);
15985         this.list.on('mousemove', this.onViewMove, this);
15986         this.list.on('scroll', this.onViewScroll, this);
15987         
15988         /*
15989         this.list.swallowEvent('mousewheel');
15990         this.assetHeight = 0;
15991
15992         if(this.title){
15993             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15994             this.assetHeight += this.header.getHeight();
15995         }
15996
15997         this.innerList = this.list.createChild({cls:cls+'-inner'});
15998         this.innerList.on('mouseover', this.onViewOver, this);
15999         this.innerList.on('mousemove', this.onViewMove, this);
16000         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16001         
16002         if(this.allowBlank && !this.pageSize && !this.disableClear){
16003             this.footer = this.list.createChild({cls:cls+'-ft'});
16004             this.pageTb = new Roo.Toolbar(this.footer);
16005            
16006         }
16007         if(this.pageSize){
16008             this.footer = this.list.createChild({cls:cls+'-ft'});
16009             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16010                     {pageSize: this.pageSize});
16011             
16012         }
16013         
16014         if (this.pageTb && this.allowBlank && !this.disableClear) {
16015             var _this = this;
16016             this.pageTb.add(new Roo.Toolbar.Fill(), {
16017                 cls: 'x-btn-icon x-btn-clear',
16018                 text: '&#160;',
16019                 handler: function()
16020                 {
16021                     _this.collapse();
16022                     _this.clearValue();
16023                     _this.onSelect(false, -1);
16024                 }
16025             });
16026         }
16027         if (this.footer) {
16028             this.assetHeight += this.footer.getHeight();
16029         }
16030         */
16031             
16032         if(!this.tpl){
16033             this.tpl = Roo.bootstrap.version == 4 ?
16034                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16035                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16036         }
16037
16038         this.view = new Roo.View(this.list, this.tpl, {
16039             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16040         });
16041         //this.view.wrapEl.setDisplayed(false);
16042         this.view.on('click', this.onViewClick, this);
16043         
16044         
16045         this.store.on('beforeload', this.onBeforeLoad, this);
16046         this.store.on('load', this.onLoad, this);
16047         this.store.on('loadexception', this.onLoadException, this);
16048         /*
16049         if(this.resizable){
16050             this.resizer = new Roo.Resizable(this.list,  {
16051                pinned:true, handles:'se'
16052             });
16053             this.resizer.on('resize', function(r, w, h){
16054                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16055                 this.listWidth = w;
16056                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16057                 this.restrictHeight();
16058             }, this);
16059             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16060         }
16061         */
16062         if(!this.editable){
16063             this.editable = true;
16064             this.setEditable(false);
16065         }
16066         
16067         /*
16068         
16069         if (typeof(this.events.add.listeners) != 'undefined') {
16070             
16071             this.addicon = this.wrap.createChild(
16072                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16073        
16074             this.addicon.on('click', function(e) {
16075                 this.fireEvent('add', this);
16076             }, this);
16077         }
16078         if (typeof(this.events.edit.listeners) != 'undefined') {
16079             
16080             this.editicon = this.wrap.createChild(
16081                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16082             if (this.addicon) {
16083                 this.editicon.setStyle('margin-left', '40px');
16084             }
16085             this.editicon.on('click', function(e) {
16086                 
16087                 // we fire even  if inothing is selected..
16088                 this.fireEvent('edit', this, this.lastData );
16089                 
16090             }, this);
16091         }
16092         */
16093         
16094         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16095             "up" : function(e){
16096                 this.inKeyMode = true;
16097                 this.selectPrev();
16098             },
16099
16100             "down" : function(e){
16101                 if(!this.isExpanded()){
16102                     this.onTriggerClick();
16103                 }else{
16104                     this.inKeyMode = true;
16105                     this.selectNext();
16106                 }
16107             },
16108
16109             "enter" : function(e){
16110 //                this.onViewClick();
16111                 //return true;
16112                 this.collapse();
16113                 
16114                 if(this.fireEvent("specialkey", this, e)){
16115                     this.onViewClick(false);
16116                 }
16117                 
16118                 return true;
16119             },
16120
16121             "esc" : function(e){
16122                 this.collapse();
16123             },
16124
16125             "tab" : function(e){
16126                 this.collapse();
16127                 
16128                 if(this.fireEvent("specialkey", this, e)){
16129                     this.onViewClick(false);
16130                 }
16131                 
16132                 return true;
16133             },
16134
16135             scope : this,
16136
16137             doRelay : function(foo, bar, hname){
16138                 if(hname == 'down' || this.scope.isExpanded()){
16139                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16140                 }
16141                 return true;
16142             },
16143
16144             forceKeyDown: true
16145         });
16146         
16147         
16148         this.queryDelay = Math.max(this.queryDelay || 10,
16149                 this.mode == 'local' ? 10 : 250);
16150         
16151         
16152         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16153         
16154         if(this.typeAhead){
16155             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16156         }
16157         if(this.editable !== false){
16158             this.inputEl().on("keyup", this.onKeyUp, this);
16159         }
16160         if(this.forceSelection){
16161             this.inputEl().on('blur', this.doForce, this);
16162         }
16163         
16164         if(this.multiple){
16165             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16166             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16167         }
16168     },
16169     
16170     initTickableEvents: function()
16171     {   
16172         this.createList();
16173         
16174         if(this.hiddenName){
16175             
16176             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16177             
16178             this.hiddenField.dom.value =
16179                 this.hiddenValue !== undefined ? this.hiddenValue :
16180                 this.value !== undefined ? this.value : '';
16181
16182             // prevent input submission
16183             this.el.dom.removeAttribute('name');
16184             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16185              
16186              
16187         }
16188         
16189 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16190         
16191         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16192         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16193         if(this.triggerList){
16194             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16195         }
16196          
16197         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16198         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16199         
16200         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16201         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16202         
16203         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16204         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16205         
16206         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16207         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16208         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16209         
16210         this.okBtn.hide();
16211         this.cancelBtn.hide();
16212         
16213         var _this = this;
16214         
16215         (function(){
16216             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16217             _this.list.setWidth(lw);
16218         }).defer(100);
16219         
16220         this.list.on('mouseover', this.onViewOver, this);
16221         this.list.on('mousemove', this.onViewMove, this);
16222         
16223         this.list.on('scroll', this.onViewScroll, this);
16224         
16225         if(!this.tpl){
16226             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16227                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16228         }
16229
16230         this.view = new Roo.View(this.list, this.tpl, {
16231             singleSelect:true,
16232             tickable:true,
16233             parent:this,
16234             store: this.store,
16235             selectedClass: this.selectedClass
16236         });
16237         
16238         //this.view.wrapEl.setDisplayed(false);
16239         this.view.on('click', this.onViewClick, this);
16240         
16241         
16242         
16243         this.store.on('beforeload', this.onBeforeLoad, this);
16244         this.store.on('load', this.onLoad, this);
16245         this.store.on('loadexception', this.onLoadException, this);
16246         
16247         if(this.editable){
16248             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16249                 "up" : function(e){
16250                     this.inKeyMode = true;
16251                     this.selectPrev();
16252                 },
16253
16254                 "down" : function(e){
16255                     this.inKeyMode = true;
16256                     this.selectNext();
16257                 },
16258
16259                 "enter" : function(e){
16260                     if(this.fireEvent("specialkey", this, e)){
16261                         this.onViewClick(false);
16262                     }
16263                     
16264                     return true;
16265                 },
16266
16267                 "esc" : function(e){
16268                     this.onTickableFooterButtonClick(e, false, false);
16269                 },
16270
16271                 "tab" : function(e){
16272                     this.fireEvent("specialkey", this, e);
16273                     
16274                     this.onTickableFooterButtonClick(e, false, false);
16275                     
16276                     return true;
16277                 },
16278
16279                 scope : this,
16280
16281                 doRelay : function(e, fn, key){
16282                     if(this.scope.isExpanded()){
16283                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16284                     }
16285                     return true;
16286                 },
16287
16288                 forceKeyDown: true
16289             });
16290         }
16291         
16292         this.queryDelay = Math.max(this.queryDelay || 10,
16293                 this.mode == 'local' ? 10 : 250);
16294         
16295         
16296         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16297         
16298         if(this.typeAhead){
16299             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16300         }
16301         
16302         if(this.editable !== false){
16303             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16304         }
16305         
16306         this.indicator = this.indicatorEl();
16307         
16308         if(this.indicator){
16309             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16310             this.indicator.hide();
16311         }
16312         
16313     },
16314
16315     onDestroy : function(){
16316         if(this.view){
16317             this.view.setStore(null);
16318             this.view.el.removeAllListeners();
16319             this.view.el.remove();
16320             this.view.purgeListeners();
16321         }
16322         if(this.list){
16323             this.list.dom.innerHTML  = '';
16324         }
16325         
16326         if(this.store){
16327             this.store.un('beforeload', this.onBeforeLoad, this);
16328             this.store.un('load', this.onLoad, this);
16329             this.store.un('loadexception', this.onLoadException, this);
16330         }
16331         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16332     },
16333
16334     // private
16335     fireKey : function(e){
16336         if(e.isNavKeyPress() && !this.list.isVisible()){
16337             this.fireEvent("specialkey", this, e);
16338         }
16339     },
16340
16341     // private
16342     onResize: function(w, h)
16343     {
16344         
16345         
16346 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16347 //        
16348 //        if(typeof w != 'number'){
16349 //            // we do not handle it!?!?
16350 //            return;
16351 //        }
16352 //        var tw = this.trigger.getWidth();
16353 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16354 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16355 //        var x = w - tw;
16356 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16357 //            
16358 //        //this.trigger.setStyle('left', x+'px');
16359 //        
16360 //        if(this.list && this.listWidth === undefined){
16361 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16362 //            this.list.setWidth(lw);
16363 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16364 //        }
16365         
16366     
16367         
16368     },
16369
16370     /**
16371      * Allow or prevent the user from directly editing the field text.  If false is passed,
16372      * the user will only be able to select from the items defined in the dropdown list.  This method
16373      * is the runtime equivalent of setting the 'editable' config option at config time.
16374      * @param {Boolean} value True to allow the user to directly edit the field text
16375      */
16376     setEditable : function(value){
16377         if(value == this.editable){
16378             return;
16379         }
16380         this.editable = value;
16381         if(!value){
16382             this.inputEl().dom.setAttribute('readOnly', true);
16383             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16384             this.inputEl().addClass('x-combo-noedit');
16385         }else{
16386             this.inputEl().dom.removeAttribute('readOnly');
16387             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16388             this.inputEl().removeClass('x-combo-noedit');
16389         }
16390     },
16391
16392     // private
16393     
16394     onBeforeLoad : function(combo,opts){
16395         if(!this.hasFocus){
16396             return;
16397         }
16398          if (!opts.add) {
16399             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16400          }
16401         this.restrictHeight();
16402         this.selectedIndex = -1;
16403     },
16404
16405     // private
16406     onLoad : function(){
16407         
16408         this.hasQuery = false;
16409         
16410         if(!this.hasFocus){
16411             return;
16412         }
16413         
16414         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16415             this.loading.hide();
16416         }
16417         
16418         if(this.store.getCount() > 0){
16419             
16420             this.expand();
16421             this.restrictHeight();
16422             if(this.lastQuery == this.allQuery){
16423                 if(this.editable && !this.tickable){
16424                     this.inputEl().dom.select();
16425                 }
16426                 
16427                 if(
16428                     !this.selectByValue(this.value, true) &&
16429                     this.autoFocus && 
16430                     (
16431                         !this.store.lastOptions ||
16432                         typeof(this.store.lastOptions.add) == 'undefined' || 
16433                         this.store.lastOptions.add != true
16434                     )
16435                 ){
16436                     this.select(0, true);
16437                 }
16438             }else{
16439                 if(this.autoFocus){
16440                     this.selectNext();
16441                 }
16442                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16443                     this.taTask.delay(this.typeAheadDelay);
16444                 }
16445             }
16446         }else{
16447             this.onEmptyResults();
16448         }
16449         
16450         //this.el.focus();
16451     },
16452     // private
16453     onLoadException : function()
16454     {
16455         this.hasQuery = false;
16456         
16457         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16458             this.loading.hide();
16459         }
16460         
16461         if(this.tickable && this.editable){
16462             return;
16463         }
16464         
16465         this.collapse();
16466         // only causes errors at present
16467         //Roo.log(this.store.reader.jsonData);
16468         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16469             // fixme
16470             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16471         //}
16472         
16473         
16474     },
16475     // private
16476     onTypeAhead : function(){
16477         if(this.store.getCount() > 0){
16478             var r = this.store.getAt(0);
16479             var newValue = r.data[this.displayField];
16480             var len = newValue.length;
16481             var selStart = this.getRawValue().length;
16482             
16483             if(selStart != len){
16484                 this.setRawValue(newValue);
16485                 this.selectText(selStart, newValue.length);
16486             }
16487         }
16488     },
16489
16490     // private
16491     onSelect : function(record, index){
16492         
16493         if(this.fireEvent('beforeselect', this, record, index) !== false){
16494         
16495             this.setFromData(index > -1 ? record.data : false);
16496             
16497             this.collapse();
16498             this.fireEvent('select', this, record, index);
16499         }
16500     },
16501
16502     /**
16503      * Returns the currently selected field value or empty string if no value is set.
16504      * @return {String} value The selected value
16505      */
16506     getValue : function()
16507     {
16508         if(Roo.isIOS && this.useNativeIOS){
16509             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16510         }
16511         
16512         if(this.multiple){
16513             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16514         }
16515         
16516         if(this.valueField){
16517             return typeof this.value != 'undefined' ? this.value : '';
16518         }else{
16519             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16520         }
16521     },
16522     
16523     getRawValue : function()
16524     {
16525         if(Roo.isIOS && this.useNativeIOS){
16526             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16527         }
16528         
16529         var v = this.inputEl().getValue();
16530         
16531         return v;
16532     },
16533
16534     /**
16535      * Clears any text/value currently set in the field
16536      */
16537     clearValue : function(){
16538         
16539         if(this.hiddenField){
16540             this.hiddenField.dom.value = '';
16541         }
16542         this.value = '';
16543         this.setRawValue('');
16544         this.lastSelectionText = '';
16545         this.lastData = false;
16546         
16547         var close = this.closeTriggerEl();
16548         
16549         if(close){
16550             close.hide();
16551         }
16552         
16553         this.validate();
16554         
16555     },
16556
16557     /**
16558      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16559      * will be displayed in the field.  If the value does not match the data value of an existing item,
16560      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16561      * Otherwise the field will be blank (although the value will still be set).
16562      * @param {String} value The value to match
16563      */
16564     setValue : function(v)
16565     {
16566         if(Roo.isIOS && this.useNativeIOS){
16567             this.setIOSValue(v);
16568             return;
16569         }
16570         
16571         if(this.multiple){
16572             this.syncValue();
16573             return;
16574         }
16575         
16576         var text = v;
16577         if(this.valueField){
16578             var r = this.findRecord(this.valueField, v);
16579             if(r){
16580                 text = r.data[this.displayField];
16581             }else if(this.valueNotFoundText !== undefined){
16582                 text = this.valueNotFoundText;
16583             }
16584         }
16585         this.lastSelectionText = text;
16586         if(this.hiddenField){
16587             this.hiddenField.dom.value = v;
16588         }
16589         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16590         this.value = v;
16591         
16592         var close = this.closeTriggerEl();
16593         
16594         if(close){
16595             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16596         }
16597         
16598         this.validate();
16599     },
16600     /**
16601      * @property {Object} the last set data for the element
16602      */
16603     
16604     lastData : false,
16605     /**
16606      * Sets the value of the field based on a object which is related to the record format for the store.
16607      * @param {Object} value the value to set as. or false on reset?
16608      */
16609     setFromData : function(o){
16610         
16611         if(this.multiple){
16612             this.addItem(o);
16613             return;
16614         }
16615             
16616         var dv = ''; // display value
16617         var vv = ''; // value value..
16618         this.lastData = o;
16619         if (this.displayField) {
16620             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16621         } else {
16622             // this is an error condition!!!
16623             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16624         }
16625         
16626         if(this.valueField){
16627             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16628         }
16629         
16630         var close = this.closeTriggerEl();
16631         
16632         if(close){
16633             if(dv.length || vv * 1 > 0){
16634                 close.show() ;
16635                 this.blockFocus=true;
16636             } else {
16637                 close.hide();
16638             }             
16639         }
16640         
16641         if(this.hiddenField){
16642             this.hiddenField.dom.value = vv;
16643             
16644             this.lastSelectionText = dv;
16645             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16646             this.value = vv;
16647             return;
16648         }
16649         // no hidden field.. - we store the value in 'value', but still display
16650         // display field!!!!
16651         this.lastSelectionText = dv;
16652         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16653         this.value = vv;
16654         
16655         
16656         
16657     },
16658     // private
16659     reset : function(){
16660         // overridden so that last data is reset..
16661         
16662         if(this.multiple){
16663             this.clearItem();
16664             return;
16665         }
16666         
16667         this.setValue(this.originalValue);
16668         //this.clearInvalid();
16669         this.lastData = false;
16670         if (this.view) {
16671             this.view.clearSelections();
16672         }
16673         
16674         this.validate();
16675     },
16676     // private
16677     findRecord : function(prop, value){
16678         var record;
16679         if(this.store.getCount() > 0){
16680             this.store.each(function(r){
16681                 if(r.data[prop] == value){
16682                     record = r;
16683                     return false;
16684                 }
16685                 return true;
16686             });
16687         }
16688         return record;
16689     },
16690     
16691     getName: function()
16692     {
16693         // returns hidden if it's set..
16694         if (!this.rendered) {return ''};
16695         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16696         
16697     },
16698     // private
16699     onViewMove : function(e, t){
16700         this.inKeyMode = false;
16701     },
16702
16703     // private
16704     onViewOver : function(e, t){
16705         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16706             return;
16707         }
16708         var item = this.view.findItemFromChild(t);
16709         
16710         if(item){
16711             var index = this.view.indexOf(item);
16712             this.select(index, false);
16713         }
16714     },
16715
16716     // private
16717     onViewClick : function(view, doFocus, el, e)
16718     {
16719         var index = this.view.getSelectedIndexes()[0];
16720         
16721         var r = this.store.getAt(index);
16722         
16723         if(this.tickable){
16724             
16725             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16726                 return;
16727             }
16728             
16729             var rm = false;
16730             var _this = this;
16731             
16732             Roo.each(this.tickItems, function(v,k){
16733                 
16734                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16735                     Roo.log(v);
16736                     _this.tickItems.splice(k, 1);
16737                     
16738                     if(typeof(e) == 'undefined' && view == false){
16739                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16740                     }
16741                     
16742                     rm = true;
16743                     return;
16744                 }
16745             });
16746             
16747             if(rm){
16748                 return;
16749             }
16750             
16751             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16752                 this.tickItems.push(r.data);
16753             }
16754             
16755             if(typeof(e) == 'undefined' && view == false){
16756                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16757             }
16758                     
16759             return;
16760         }
16761         
16762         if(r){
16763             this.onSelect(r, index);
16764         }
16765         if(doFocus !== false && !this.blockFocus){
16766             this.inputEl().focus();
16767         }
16768     },
16769
16770     // private
16771     restrictHeight : function(){
16772         //this.innerList.dom.style.height = '';
16773         //var inner = this.innerList.dom;
16774         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16775         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16776         //this.list.beginUpdate();
16777         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16778         this.list.alignTo(this.inputEl(), this.listAlign);
16779         this.list.alignTo(this.inputEl(), this.listAlign);
16780         //this.list.endUpdate();
16781     },
16782
16783     // private
16784     onEmptyResults : function(){
16785         
16786         if(this.tickable && this.editable){
16787             this.hasFocus = false;
16788             this.restrictHeight();
16789             return;
16790         }
16791         
16792         this.collapse();
16793     },
16794
16795     /**
16796      * Returns true if the dropdown list is expanded, else false.
16797      */
16798     isExpanded : function(){
16799         return this.list.isVisible();
16800     },
16801
16802     /**
16803      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16804      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16805      * @param {String} value The data value of the item to select
16806      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16807      * selected item if it is not currently in view (defaults to true)
16808      * @return {Boolean} True if the value matched an item in the list, else false
16809      */
16810     selectByValue : function(v, scrollIntoView){
16811         if(v !== undefined && v !== null){
16812             var r = this.findRecord(this.valueField || this.displayField, v);
16813             if(r){
16814                 this.select(this.store.indexOf(r), scrollIntoView);
16815                 return true;
16816             }
16817         }
16818         return false;
16819     },
16820
16821     /**
16822      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16823      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16824      * @param {Number} index The zero-based index of the list item to select
16825      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16826      * selected item if it is not currently in view (defaults to true)
16827      */
16828     select : function(index, scrollIntoView){
16829         this.selectedIndex = index;
16830         this.view.select(index);
16831         if(scrollIntoView !== false){
16832             var el = this.view.getNode(index);
16833             /*
16834              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16835              */
16836             if(el){
16837                 this.list.scrollChildIntoView(el, false);
16838             }
16839         }
16840     },
16841
16842     // private
16843     selectNext : function(){
16844         var ct = this.store.getCount();
16845         if(ct > 0){
16846             if(this.selectedIndex == -1){
16847                 this.select(0);
16848             }else if(this.selectedIndex < ct-1){
16849                 this.select(this.selectedIndex+1);
16850             }
16851         }
16852     },
16853
16854     // private
16855     selectPrev : function(){
16856         var ct = this.store.getCount();
16857         if(ct > 0){
16858             if(this.selectedIndex == -1){
16859                 this.select(0);
16860             }else if(this.selectedIndex != 0){
16861                 this.select(this.selectedIndex-1);
16862             }
16863         }
16864     },
16865
16866     // private
16867     onKeyUp : function(e){
16868         if(this.editable !== false && !e.isSpecialKey()){
16869             this.lastKey = e.getKey();
16870             this.dqTask.delay(this.queryDelay);
16871         }
16872     },
16873
16874     // private
16875     validateBlur : function(){
16876         return !this.list || !this.list.isVisible();   
16877     },
16878
16879     // private
16880     initQuery : function(){
16881         
16882         var v = this.getRawValue();
16883         
16884         if(this.tickable && this.editable){
16885             v = this.tickableInputEl().getValue();
16886         }
16887         
16888         this.doQuery(v);
16889     },
16890
16891     // private
16892     doForce : function(){
16893         if(this.inputEl().dom.value.length > 0){
16894             this.inputEl().dom.value =
16895                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16896              
16897         }
16898     },
16899
16900     /**
16901      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16902      * query allowing the query action to be canceled if needed.
16903      * @param {String} query The SQL query to execute
16904      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16905      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16906      * saved in the current store (defaults to false)
16907      */
16908     doQuery : function(q, forceAll){
16909         
16910         if(q === undefined || q === null){
16911             q = '';
16912         }
16913         var qe = {
16914             query: q,
16915             forceAll: forceAll,
16916             combo: this,
16917             cancel:false
16918         };
16919         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16920             return false;
16921         }
16922         q = qe.query;
16923         
16924         forceAll = qe.forceAll;
16925         if(forceAll === true || (q.length >= this.minChars)){
16926             
16927             this.hasQuery = true;
16928             
16929             if(this.lastQuery != q || this.alwaysQuery){
16930                 this.lastQuery = q;
16931                 if(this.mode == 'local'){
16932                     this.selectedIndex = -1;
16933                     if(forceAll){
16934                         this.store.clearFilter();
16935                     }else{
16936                         
16937                         if(this.specialFilter){
16938                             this.fireEvent('specialfilter', this);
16939                             this.onLoad();
16940                             return;
16941                         }
16942                         
16943                         this.store.filter(this.displayField, q);
16944                     }
16945                     
16946                     this.store.fireEvent("datachanged", this.store);
16947                     
16948                     this.onLoad();
16949                     
16950                     
16951                 }else{
16952                     
16953                     this.store.baseParams[this.queryParam] = q;
16954                     
16955                     var options = {params : this.getParams(q)};
16956                     
16957                     if(this.loadNext){
16958                         options.add = true;
16959                         options.params.start = this.page * this.pageSize;
16960                     }
16961                     
16962                     this.store.load(options);
16963                     
16964                     /*
16965                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16966                      *  we should expand the list on onLoad
16967                      *  so command out it
16968                      */
16969 //                    this.expand();
16970                 }
16971             }else{
16972                 this.selectedIndex = -1;
16973                 this.onLoad();   
16974             }
16975         }
16976         
16977         this.loadNext = false;
16978     },
16979     
16980     // private
16981     getParams : function(q){
16982         var p = {};
16983         //p[this.queryParam] = q;
16984         
16985         if(this.pageSize){
16986             p.start = 0;
16987             p.limit = this.pageSize;
16988         }
16989         return p;
16990     },
16991
16992     /**
16993      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16994      */
16995     collapse : function(){
16996         if(!this.isExpanded()){
16997             return;
16998         }
16999         
17000         this.list.hide();
17001         
17002         this.hasFocus = false;
17003         
17004         if(this.tickable){
17005             this.okBtn.hide();
17006             this.cancelBtn.hide();
17007             this.trigger.show();
17008             
17009             if(this.editable){
17010                 this.tickableInputEl().dom.value = '';
17011                 this.tickableInputEl().blur();
17012             }
17013             
17014         }
17015         
17016         Roo.get(document).un('mousedown', this.collapseIf, this);
17017         Roo.get(document).un('mousewheel', this.collapseIf, this);
17018         if (!this.editable) {
17019             Roo.get(document).un('keydown', this.listKeyPress, this);
17020         }
17021         this.fireEvent('collapse', this);
17022         
17023         this.validate();
17024     },
17025
17026     // private
17027     collapseIf : function(e){
17028         var in_combo  = e.within(this.el);
17029         var in_list =  e.within(this.list);
17030         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17031         
17032         if (in_combo || in_list || is_list) {
17033             //e.stopPropagation();
17034             return;
17035         }
17036         
17037         if(this.tickable){
17038             this.onTickableFooterButtonClick(e, false, false);
17039         }
17040
17041         this.collapse();
17042         
17043     },
17044
17045     /**
17046      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17047      */
17048     expand : function(){
17049        
17050         if(this.isExpanded() || !this.hasFocus){
17051             return;
17052         }
17053         
17054         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17055         this.list.setWidth(lw);
17056         
17057         Roo.log('expand');
17058         
17059         this.list.show();
17060         
17061         this.restrictHeight();
17062         
17063         if(this.tickable){
17064             
17065             this.tickItems = Roo.apply([], this.item);
17066             
17067             this.okBtn.show();
17068             this.cancelBtn.show();
17069             this.trigger.hide();
17070             
17071             if(this.editable){
17072                 this.tickableInputEl().focus();
17073             }
17074             
17075         }
17076         
17077         Roo.get(document).on('mousedown', this.collapseIf, this);
17078         Roo.get(document).on('mousewheel', this.collapseIf, this);
17079         if (!this.editable) {
17080             Roo.get(document).on('keydown', this.listKeyPress, this);
17081         }
17082         
17083         this.fireEvent('expand', this);
17084     },
17085
17086     // private
17087     // Implements the default empty TriggerField.onTriggerClick function
17088     onTriggerClick : function(e)
17089     {
17090         Roo.log('trigger click');
17091         
17092         if(this.disabled || !this.triggerList){
17093             return;
17094         }
17095         
17096         this.page = 0;
17097         this.loadNext = false;
17098         
17099         if(this.isExpanded()){
17100             this.collapse();
17101             if (!this.blockFocus) {
17102                 this.inputEl().focus();
17103             }
17104             
17105         }else {
17106             this.hasFocus = true;
17107             if(this.triggerAction == 'all') {
17108                 this.doQuery(this.allQuery, true);
17109             } else {
17110                 this.doQuery(this.getRawValue());
17111             }
17112             if (!this.blockFocus) {
17113                 this.inputEl().focus();
17114             }
17115         }
17116     },
17117     
17118     onTickableTriggerClick : function(e)
17119     {
17120         if(this.disabled){
17121             return;
17122         }
17123         
17124         this.page = 0;
17125         this.loadNext = false;
17126         this.hasFocus = true;
17127         
17128         if(this.triggerAction == 'all') {
17129             this.doQuery(this.allQuery, true);
17130         } else {
17131             this.doQuery(this.getRawValue());
17132         }
17133     },
17134     
17135     onSearchFieldClick : function(e)
17136     {
17137         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17138             this.onTickableFooterButtonClick(e, false, false);
17139             return;
17140         }
17141         
17142         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17143             return;
17144         }
17145         
17146         this.page = 0;
17147         this.loadNext = false;
17148         this.hasFocus = true;
17149         
17150         if(this.triggerAction == 'all') {
17151             this.doQuery(this.allQuery, true);
17152         } else {
17153             this.doQuery(this.getRawValue());
17154         }
17155     },
17156     
17157     listKeyPress : function(e)
17158     {
17159         //Roo.log('listkeypress');
17160         // scroll to first matching element based on key pres..
17161         if (e.isSpecialKey()) {
17162             return false;
17163         }
17164         var k = String.fromCharCode(e.getKey()).toUpperCase();
17165         //Roo.log(k);
17166         var match  = false;
17167         var csel = this.view.getSelectedNodes();
17168         var cselitem = false;
17169         if (csel.length) {
17170             var ix = this.view.indexOf(csel[0]);
17171             cselitem  = this.store.getAt(ix);
17172             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17173                 cselitem = false;
17174             }
17175             
17176         }
17177         
17178         this.store.each(function(v) { 
17179             if (cselitem) {
17180                 // start at existing selection.
17181                 if (cselitem.id == v.id) {
17182                     cselitem = false;
17183                 }
17184                 return true;
17185             }
17186                 
17187             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17188                 match = this.store.indexOf(v);
17189                 return false;
17190             }
17191             return true;
17192         }, this);
17193         
17194         if (match === false) {
17195             return true; // no more action?
17196         }
17197         // scroll to?
17198         this.view.select(match);
17199         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17200         sn.scrollIntoView(sn.dom.parentNode, false);
17201     },
17202     
17203     onViewScroll : function(e, t){
17204         
17205         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){
17206             return;
17207         }
17208         
17209         this.hasQuery = true;
17210         
17211         this.loading = this.list.select('.loading', true).first();
17212         
17213         if(this.loading === null){
17214             this.list.createChild({
17215                 tag: 'div',
17216                 cls: 'loading roo-select2-more-results roo-select2-active',
17217                 html: 'Loading more results...'
17218             });
17219             
17220             this.loading = this.list.select('.loading', true).first();
17221             
17222             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17223             
17224             this.loading.hide();
17225         }
17226         
17227         this.loading.show();
17228         
17229         var _combo = this;
17230         
17231         this.page++;
17232         this.loadNext = true;
17233         
17234         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17235         
17236         return;
17237     },
17238     
17239     addItem : function(o)
17240     {   
17241         var dv = ''; // display value
17242         
17243         if (this.displayField) {
17244             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17245         } else {
17246             // this is an error condition!!!
17247             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17248         }
17249         
17250         if(!dv.length){
17251             return;
17252         }
17253         
17254         var choice = this.choices.createChild({
17255             tag: 'li',
17256             cls: 'roo-select2-search-choice',
17257             cn: [
17258                 {
17259                     tag: 'div',
17260                     html: dv
17261                 },
17262                 {
17263                     tag: 'a',
17264                     href: '#',
17265                     cls: 'roo-select2-search-choice-close fa fa-times',
17266                     tabindex: '-1'
17267                 }
17268             ]
17269             
17270         }, this.searchField);
17271         
17272         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17273         
17274         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17275         
17276         this.item.push(o);
17277         
17278         this.lastData = o;
17279         
17280         this.syncValue();
17281         
17282         this.inputEl().dom.value = '';
17283         
17284         this.validate();
17285     },
17286     
17287     onRemoveItem : function(e, _self, o)
17288     {
17289         e.preventDefault();
17290         
17291         this.lastItem = Roo.apply([], this.item);
17292         
17293         var index = this.item.indexOf(o.data) * 1;
17294         
17295         if( index < 0){
17296             Roo.log('not this item?!');
17297             return;
17298         }
17299         
17300         this.item.splice(index, 1);
17301         o.item.remove();
17302         
17303         this.syncValue();
17304         
17305         this.fireEvent('remove', this, e);
17306         
17307         this.validate();
17308         
17309     },
17310     
17311     syncValue : function()
17312     {
17313         if(!this.item.length){
17314             this.clearValue();
17315             return;
17316         }
17317             
17318         var value = [];
17319         var _this = this;
17320         Roo.each(this.item, function(i){
17321             if(_this.valueField){
17322                 value.push(i[_this.valueField]);
17323                 return;
17324             }
17325
17326             value.push(i);
17327         });
17328
17329         this.value = value.join(',');
17330
17331         if(this.hiddenField){
17332             this.hiddenField.dom.value = this.value;
17333         }
17334         
17335         this.store.fireEvent("datachanged", this.store);
17336         
17337         this.validate();
17338     },
17339     
17340     clearItem : function()
17341     {
17342         if(!this.multiple){
17343             return;
17344         }
17345         
17346         this.item = [];
17347         
17348         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17349            c.remove();
17350         });
17351         
17352         this.syncValue();
17353         
17354         this.validate();
17355         
17356         if(this.tickable && !Roo.isTouch){
17357             this.view.refresh();
17358         }
17359     },
17360     
17361     inputEl: function ()
17362     {
17363         if(Roo.isIOS && this.useNativeIOS){
17364             return this.el.select('select.roo-ios-select', true).first();
17365         }
17366         
17367         if(Roo.isTouch && this.mobileTouchView){
17368             return this.el.select('input.form-control',true).first();
17369         }
17370         
17371         if(this.tickable){
17372             return this.searchField;
17373         }
17374         
17375         return this.el.select('input.form-control',true).first();
17376     },
17377     
17378     onTickableFooterButtonClick : function(e, btn, el)
17379     {
17380         e.preventDefault();
17381         
17382         this.lastItem = Roo.apply([], this.item);
17383         
17384         if(btn && btn.name == 'cancel'){
17385             this.tickItems = Roo.apply([], this.item);
17386             this.collapse();
17387             return;
17388         }
17389         
17390         this.clearItem();
17391         
17392         var _this = this;
17393         
17394         Roo.each(this.tickItems, function(o){
17395             _this.addItem(o);
17396         });
17397         
17398         this.collapse();
17399         
17400     },
17401     
17402     validate : function()
17403     {
17404         if(this.getVisibilityEl().hasClass('hidden')){
17405             return true;
17406         }
17407         
17408         var v = this.getRawValue();
17409         
17410         if(this.multiple){
17411             v = this.getValue();
17412         }
17413         
17414         if(this.disabled || this.allowBlank || v.length){
17415             this.markValid();
17416             return true;
17417         }
17418         
17419         this.markInvalid();
17420         return false;
17421     },
17422     
17423     tickableInputEl : function()
17424     {
17425         if(!this.tickable || !this.editable){
17426             return this.inputEl();
17427         }
17428         
17429         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17430     },
17431     
17432     
17433     getAutoCreateTouchView : function()
17434     {
17435         var id = Roo.id();
17436         
17437         var cfg = {
17438             cls: 'form-group' //input-group
17439         };
17440         
17441         var input =  {
17442             tag: 'input',
17443             id : id,
17444             type : this.inputType,
17445             cls : 'form-control x-combo-noedit',
17446             autocomplete: 'new-password',
17447             placeholder : this.placeholder || '',
17448             readonly : true
17449         };
17450         
17451         if (this.name) {
17452             input.name = this.name;
17453         }
17454         
17455         if (this.size) {
17456             input.cls += ' input-' + this.size;
17457         }
17458         
17459         if (this.disabled) {
17460             input.disabled = true;
17461         }
17462         
17463         var inputblock = {
17464             cls : 'roo-combobox-wrap',
17465             cn : [
17466                 input
17467             ]
17468         };
17469         
17470         if(this.before){
17471             inputblock.cls += ' input-group';
17472             
17473             inputblock.cn.unshift({
17474                 tag :'span',
17475                 cls : 'input-group-addon input-group-prepend input-group-text',
17476                 html : this.before
17477             });
17478         }
17479         
17480         if(this.removable && !this.multiple){
17481             inputblock.cls += ' roo-removable';
17482             
17483             inputblock.cn.push({
17484                 tag: 'button',
17485                 html : 'x',
17486                 cls : 'roo-combo-removable-btn close'
17487             });
17488         }
17489
17490         if(this.hasFeedback && !this.allowBlank){
17491             
17492             inputblock.cls += ' has-feedback';
17493             
17494             inputblock.cn.push({
17495                 tag: 'span',
17496                 cls: 'glyphicon form-control-feedback'
17497             });
17498             
17499         }
17500         
17501         if (this.after) {
17502             
17503             inputblock.cls += (this.before) ? '' : ' input-group';
17504             
17505             inputblock.cn.push({
17506                 tag :'span',
17507                 cls : 'input-group-addon input-group-append input-group-text',
17508                 html : this.after
17509             });
17510         }
17511
17512         
17513         var ibwrap = inputblock;
17514         
17515         if(this.multiple){
17516             ibwrap = {
17517                 tag: 'ul',
17518                 cls: 'roo-select2-choices',
17519                 cn:[
17520                     {
17521                         tag: 'li',
17522                         cls: 'roo-select2-search-field',
17523                         cn: [
17524
17525                             inputblock
17526                         ]
17527                     }
17528                 ]
17529             };
17530         
17531             
17532         }
17533         
17534         var combobox = {
17535             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17536             cn: [
17537                 {
17538                     tag: 'input',
17539                     type : 'hidden',
17540                     cls: 'form-hidden-field'
17541                 },
17542                 ibwrap
17543             ]
17544         };
17545         
17546         if(!this.multiple && this.showToggleBtn){
17547             
17548             var caret = {
17549                 cls: 'caret'
17550             };
17551             
17552             if (this.caret != false) {
17553                 caret = {
17554                      tag: 'i',
17555                      cls: 'fa fa-' + this.caret
17556                 };
17557                 
17558             }
17559             
17560             combobox.cn.push({
17561                 tag :'span',
17562                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17563                 cn : [
17564                     Roo.bootstrap.version == 3 ? caret : '',
17565                     {
17566                         tag: 'span',
17567                         cls: 'combobox-clear',
17568                         cn  : [
17569                             {
17570                                 tag : 'i',
17571                                 cls: 'icon-remove'
17572                             }
17573                         ]
17574                     }
17575                 ]
17576
17577             })
17578         }
17579         
17580         if(this.multiple){
17581             combobox.cls += ' roo-select2-container-multi';
17582         }
17583         
17584         var required =  this.allowBlank ?  {
17585                     tag : 'i',
17586                     style: 'display: none'
17587                 } : {
17588                    tag : 'i',
17589                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17590                    tooltip : 'This field is required'
17591                 };
17592         
17593         var align = this.labelAlign || this.parentLabelAlign();
17594         
17595         if (align ==='left' && this.fieldLabel.length) {
17596
17597             cfg.cn = [
17598                 required,
17599                 {
17600                     tag: 'label',
17601                     cls : 'control-label col-form-label',
17602                     html : this.fieldLabel
17603
17604                 },
17605                 {
17606                     cls : 'roo-combobox-wrap ', 
17607                     cn: [
17608                         combobox
17609                     ]
17610                 }
17611             ];
17612             
17613             var labelCfg = cfg.cn[1];
17614             var contentCfg = cfg.cn[2];
17615             
17616
17617             if(this.indicatorpos == 'right'){
17618                 cfg.cn = [
17619                     {
17620                         tag: 'label',
17621                         'for' :  id,
17622                         cls : 'control-label col-form-label',
17623                         cn : [
17624                             {
17625                                 tag : 'span',
17626                                 html : this.fieldLabel
17627                             },
17628                             required
17629                         ]
17630                     },
17631                     {
17632                         cls : "roo-combobox-wrap ",
17633                         cn: [
17634                             combobox
17635                         ]
17636                     }
17637
17638                 ];
17639                 
17640                 labelCfg = cfg.cn[0];
17641                 contentCfg = cfg.cn[1];
17642             }
17643             
17644            
17645             
17646             if(this.labelWidth > 12){
17647                 labelCfg.style = "width: " + this.labelWidth + 'px';
17648             }
17649            
17650             if(this.labelWidth < 13 && this.labelmd == 0){
17651                 this.labelmd = this.labelWidth;
17652             }
17653             
17654             if(this.labellg > 0){
17655                 labelCfg.cls += ' col-lg-' + this.labellg;
17656                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17657             }
17658             
17659             if(this.labelmd > 0){
17660                 labelCfg.cls += ' col-md-' + this.labelmd;
17661                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17662             }
17663             
17664             if(this.labelsm > 0){
17665                 labelCfg.cls += ' col-sm-' + this.labelsm;
17666                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17667             }
17668             
17669             if(this.labelxs > 0){
17670                 labelCfg.cls += ' col-xs-' + this.labelxs;
17671                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17672             }
17673                 
17674                 
17675         } else if ( this.fieldLabel.length) {
17676             cfg.cn = [
17677                required,
17678                 {
17679                     tag: 'label',
17680                     cls : 'control-label',
17681                     html : this.fieldLabel
17682
17683                 },
17684                 {
17685                     cls : '', 
17686                     cn: [
17687                         combobox
17688                     ]
17689                 }
17690             ];
17691             
17692             if(this.indicatorpos == 'right'){
17693                 cfg.cn = [
17694                     {
17695                         tag: 'label',
17696                         cls : 'control-label',
17697                         html : this.fieldLabel,
17698                         cn : [
17699                             required
17700                         ]
17701                     },
17702                     {
17703                         cls : '', 
17704                         cn: [
17705                             combobox
17706                         ]
17707                     }
17708                 ];
17709             }
17710         } else {
17711             cfg.cn = combobox;    
17712         }
17713         
17714         
17715         var settings = this;
17716         
17717         ['xs','sm','md','lg'].map(function(size){
17718             if (settings[size]) {
17719                 cfg.cls += ' col-' + size + '-' + settings[size];
17720             }
17721         });
17722         
17723         return cfg;
17724     },
17725     
17726     initTouchView : function()
17727     {
17728         this.renderTouchView();
17729         
17730         this.touchViewEl.on('scroll', function(){
17731             this.el.dom.scrollTop = 0;
17732         }, this);
17733         
17734         this.originalValue = this.getValue();
17735         
17736         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17737         
17738         this.inputEl().on("click", this.showTouchView, this);
17739         if (this.triggerEl) {
17740             this.triggerEl.on("click", this.showTouchView, this);
17741         }
17742         
17743         
17744         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17745         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17746         
17747         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17748         
17749         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17750         this.store.on('load', this.onTouchViewLoad, this);
17751         this.store.on('loadexception', this.onTouchViewLoadException, this);
17752         
17753         if(this.hiddenName){
17754             
17755             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17756             
17757             this.hiddenField.dom.value =
17758                 this.hiddenValue !== undefined ? this.hiddenValue :
17759                 this.value !== undefined ? this.value : '';
17760         
17761             this.el.dom.removeAttribute('name');
17762             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17763         }
17764         
17765         if(this.multiple){
17766             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17767             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17768         }
17769         
17770         if(this.removable && !this.multiple){
17771             var close = this.closeTriggerEl();
17772             if(close){
17773                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17774                 close.on('click', this.removeBtnClick, this, close);
17775             }
17776         }
17777         /*
17778          * fix the bug in Safari iOS8
17779          */
17780         this.inputEl().on("focus", function(e){
17781             document.activeElement.blur();
17782         }, this);
17783         
17784         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17785         
17786         return;
17787         
17788         
17789     },
17790     
17791     renderTouchView : function()
17792     {
17793         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17794         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17795         
17796         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17797         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17798         
17799         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17800         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17801         this.touchViewBodyEl.setStyle('overflow', 'auto');
17802         
17803         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17804         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17805         
17806         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17807         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17808         
17809     },
17810     
17811     showTouchView : function()
17812     {
17813         if(this.disabled){
17814             return;
17815         }
17816         
17817         this.touchViewHeaderEl.hide();
17818
17819         if(this.modalTitle.length){
17820             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17821             this.touchViewHeaderEl.show();
17822         }
17823
17824         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17825         this.touchViewEl.show();
17826
17827         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17828         
17829         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17830         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17831
17832         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17833
17834         if(this.modalTitle.length){
17835             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17836         }
17837         
17838         this.touchViewBodyEl.setHeight(bodyHeight);
17839
17840         if(this.animate){
17841             var _this = this;
17842             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17843         }else{
17844             this.touchViewEl.addClass(['in','show']);
17845         }
17846         
17847         if(this._touchViewMask){
17848             Roo.get(document.body).addClass("x-body-masked");
17849             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17850             this._touchViewMask.setStyle('z-index', 10000);
17851             this._touchViewMask.addClass('show');
17852         }
17853         
17854         this.doTouchViewQuery();
17855         
17856     },
17857     
17858     hideTouchView : function()
17859     {
17860         this.touchViewEl.removeClass(['in','show']);
17861
17862         if(this.animate){
17863             var _this = this;
17864             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17865         }else{
17866             this.touchViewEl.setStyle('display', 'none');
17867         }
17868         
17869         if(this._touchViewMask){
17870             this._touchViewMask.removeClass('show');
17871             Roo.get(document.body).removeClass("x-body-masked");
17872         }
17873     },
17874     
17875     setTouchViewValue : function()
17876     {
17877         if(this.multiple){
17878             this.clearItem();
17879         
17880             var _this = this;
17881
17882             Roo.each(this.tickItems, function(o){
17883                 this.addItem(o);
17884             }, this);
17885         }
17886         
17887         this.hideTouchView();
17888     },
17889     
17890     doTouchViewQuery : function()
17891     {
17892         var qe = {
17893             query: '',
17894             forceAll: true,
17895             combo: this,
17896             cancel:false
17897         };
17898         
17899         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17900             return false;
17901         }
17902         
17903         if(!this.alwaysQuery || this.mode == 'local'){
17904             this.onTouchViewLoad();
17905             return;
17906         }
17907         
17908         this.store.load();
17909     },
17910     
17911     onTouchViewBeforeLoad : function(combo,opts)
17912     {
17913         return;
17914     },
17915
17916     // private
17917     onTouchViewLoad : function()
17918     {
17919         if(this.store.getCount() < 1){
17920             this.onTouchViewEmptyResults();
17921             return;
17922         }
17923         
17924         this.clearTouchView();
17925         
17926         var rawValue = this.getRawValue();
17927         
17928         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17929         
17930         this.tickItems = [];
17931         
17932         this.store.data.each(function(d, rowIndex){
17933             var row = this.touchViewListGroup.createChild(template);
17934             
17935             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17936                 row.addClass(d.data.cls);
17937             }
17938             
17939             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17940                 var cfg = {
17941                     data : d.data,
17942                     html : d.data[this.displayField]
17943                 };
17944                 
17945                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17946                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17947                 }
17948             }
17949             row.removeClass('selected');
17950             if(!this.multiple && this.valueField &&
17951                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17952             {
17953                 // radio buttons..
17954                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17955                 row.addClass('selected');
17956             }
17957             
17958             if(this.multiple && this.valueField &&
17959                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17960             {
17961                 
17962                 // checkboxes...
17963                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17964                 this.tickItems.push(d.data);
17965             }
17966             
17967             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17968             
17969         }, this);
17970         
17971         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17972         
17973         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17974
17975         if(this.modalTitle.length){
17976             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17977         }
17978
17979         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17980         
17981         if(this.mobile_restrict_height && listHeight < bodyHeight){
17982             this.touchViewBodyEl.setHeight(listHeight);
17983         }
17984         
17985         var _this = this;
17986         
17987         if(firstChecked && listHeight > bodyHeight){
17988             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17989         }
17990         
17991     },
17992     
17993     onTouchViewLoadException : function()
17994     {
17995         this.hideTouchView();
17996     },
17997     
17998     onTouchViewEmptyResults : function()
17999     {
18000         this.clearTouchView();
18001         
18002         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18003         
18004         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18005         
18006     },
18007     
18008     clearTouchView : function()
18009     {
18010         this.touchViewListGroup.dom.innerHTML = '';
18011     },
18012     
18013     onTouchViewClick : function(e, el, o)
18014     {
18015         e.preventDefault();
18016         
18017         var row = o.row;
18018         var rowIndex = o.rowIndex;
18019         
18020         var r = this.store.getAt(rowIndex);
18021         
18022         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18023             
18024             if(!this.multiple){
18025                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18026                     c.dom.removeAttribute('checked');
18027                 }, this);
18028
18029                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18030
18031                 this.setFromData(r.data);
18032
18033                 var close = this.closeTriggerEl();
18034
18035                 if(close){
18036                     close.show();
18037                 }
18038
18039                 this.hideTouchView();
18040
18041                 this.fireEvent('select', this, r, rowIndex);
18042
18043                 return;
18044             }
18045
18046             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18047                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18048                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18049                 return;
18050             }
18051
18052             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18053             this.addItem(r.data);
18054             this.tickItems.push(r.data);
18055         }
18056     },
18057     
18058     getAutoCreateNativeIOS : function()
18059     {
18060         var cfg = {
18061             cls: 'form-group' //input-group,
18062         };
18063         
18064         var combobox =  {
18065             tag: 'select',
18066             cls : 'roo-ios-select'
18067         };
18068         
18069         if (this.name) {
18070             combobox.name = this.name;
18071         }
18072         
18073         if (this.disabled) {
18074             combobox.disabled = true;
18075         }
18076         
18077         var settings = this;
18078         
18079         ['xs','sm','md','lg'].map(function(size){
18080             if (settings[size]) {
18081                 cfg.cls += ' col-' + size + '-' + settings[size];
18082             }
18083         });
18084         
18085         cfg.cn = combobox;
18086         
18087         return cfg;
18088         
18089     },
18090     
18091     initIOSView : function()
18092     {
18093         this.store.on('load', this.onIOSViewLoad, this);
18094         
18095         return;
18096     },
18097     
18098     onIOSViewLoad : function()
18099     {
18100         if(this.store.getCount() < 1){
18101             return;
18102         }
18103         
18104         this.clearIOSView();
18105         
18106         if(this.allowBlank) {
18107             
18108             var default_text = '-- SELECT --';
18109             
18110             if(this.placeholder.length){
18111                 default_text = this.placeholder;
18112             }
18113             
18114             if(this.emptyTitle.length){
18115                 default_text += ' - ' + this.emptyTitle + ' -';
18116             }
18117             
18118             var opt = this.inputEl().createChild({
18119                 tag: 'option',
18120                 value : 0,
18121                 html : default_text
18122             });
18123             
18124             var o = {};
18125             o[this.valueField] = 0;
18126             o[this.displayField] = default_text;
18127             
18128             this.ios_options.push({
18129                 data : o,
18130                 el : opt
18131             });
18132             
18133         }
18134         
18135         this.store.data.each(function(d, rowIndex){
18136             
18137             var html = '';
18138             
18139             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18140                 html = d.data[this.displayField];
18141             }
18142             
18143             var value = '';
18144             
18145             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18146                 value = d.data[this.valueField];
18147             }
18148             
18149             var option = {
18150                 tag: 'option',
18151                 value : value,
18152                 html : html
18153             };
18154             
18155             if(this.value == d.data[this.valueField]){
18156                 option['selected'] = true;
18157             }
18158             
18159             var opt = this.inputEl().createChild(option);
18160             
18161             this.ios_options.push({
18162                 data : d.data,
18163                 el : opt
18164             });
18165             
18166         }, this);
18167         
18168         this.inputEl().on('change', function(){
18169            this.fireEvent('select', this);
18170         }, this);
18171         
18172     },
18173     
18174     clearIOSView: function()
18175     {
18176         this.inputEl().dom.innerHTML = '';
18177         
18178         this.ios_options = [];
18179     },
18180     
18181     setIOSValue: function(v)
18182     {
18183         this.value = v;
18184         
18185         if(!this.ios_options){
18186             return;
18187         }
18188         
18189         Roo.each(this.ios_options, function(opts){
18190            
18191            opts.el.dom.removeAttribute('selected');
18192            
18193            if(opts.data[this.valueField] != v){
18194                return;
18195            }
18196            
18197            opts.el.dom.setAttribute('selected', true);
18198            
18199         }, this);
18200     }
18201
18202     /** 
18203     * @cfg {Boolean} grow 
18204     * @hide 
18205     */
18206     /** 
18207     * @cfg {Number} growMin 
18208     * @hide 
18209     */
18210     /** 
18211     * @cfg {Number} growMax 
18212     * @hide 
18213     */
18214     /**
18215      * @hide
18216      * @method autoSize
18217      */
18218 });
18219
18220 Roo.apply(Roo.bootstrap.ComboBox,  {
18221     
18222     header : {
18223         tag: 'div',
18224         cls: 'modal-header',
18225         cn: [
18226             {
18227                 tag: 'h4',
18228                 cls: 'modal-title'
18229             }
18230         ]
18231     },
18232     
18233     body : {
18234         tag: 'div',
18235         cls: 'modal-body',
18236         cn: [
18237             {
18238                 tag: 'ul',
18239                 cls: 'list-group'
18240             }
18241         ]
18242     },
18243     
18244     listItemRadio : {
18245         tag: 'li',
18246         cls: 'list-group-item',
18247         cn: [
18248             {
18249                 tag: 'span',
18250                 cls: 'roo-combobox-list-group-item-value'
18251             },
18252             {
18253                 tag: 'div',
18254                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18255                 cn: [
18256                     {
18257                         tag: 'input',
18258                         type: 'radio'
18259                     },
18260                     {
18261                         tag: 'label'
18262                     }
18263                 ]
18264             }
18265         ]
18266     },
18267     
18268     listItemCheckbox : {
18269         tag: 'li',
18270         cls: 'list-group-item',
18271         cn: [
18272             {
18273                 tag: 'span',
18274                 cls: 'roo-combobox-list-group-item-value'
18275             },
18276             {
18277                 tag: 'div',
18278                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18279                 cn: [
18280                     {
18281                         tag: 'input',
18282                         type: 'checkbox'
18283                     },
18284                     {
18285                         tag: 'label'
18286                     }
18287                 ]
18288             }
18289         ]
18290     },
18291     
18292     emptyResult : {
18293         tag: 'div',
18294         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18295     },
18296     
18297     footer : {
18298         tag: 'div',
18299         cls: 'modal-footer',
18300         cn: [
18301             {
18302                 tag: 'div',
18303                 cls: 'row',
18304                 cn: [
18305                     {
18306                         tag: 'div',
18307                         cls: 'col-xs-6 text-left',
18308                         cn: {
18309                             tag: 'button',
18310                             cls: 'btn btn-danger roo-touch-view-cancel',
18311                             html: 'Cancel'
18312                         }
18313                     },
18314                     {
18315                         tag: 'div',
18316                         cls: 'col-xs-6 text-right',
18317                         cn: {
18318                             tag: 'button',
18319                             cls: 'btn btn-success roo-touch-view-ok',
18320                             html: 'OK'
18321                         }
18322                     }
18323                 ]
18324             }
18325         ]
18326         
18327     }
18328 });
18329
18330 Roo.apply(Roo.bootstrap.ComboBox,  {
18331     
18332     touchViewTemplate : {
18333         tag: 'div',
18334         cls: 'modal fade roo-combobox-touch-view',
18335         cn: [
18336             {
18337                 tag: 'div',
18338                 cls: 'modal-dialog',
18339                 style : 'position:fixed', // we have to fix position....
18340                 cn: [
18341                     {
18342                         tag: 'div',
18343                         cls: 'modal-content',
18344                         cn: [
18345                             Roo.bootstrap.ComboBox.header,
18346                             Roo.bootstrap.ComboBox.body,
18347                             Roo.bootstrap.ComboBox.footer
18348                         ]
18349                     }
18350                 ]
18351             }
18352         ]
18353     }
18354 });/*
18355  * Based on:
18356  * Ext JS Library 1.1.1
18357  * Copyright(c) 2006-2007, Ext JS, LLC.
18358  *
18359  * Originally Released Under LGPL - original licence link has changed is not relivant.
18360  *
18361  * Fork - LGPL
18362  * <script type="text/javascript">
18363  */
18364
18365 /**
18366  * @class Roo.View
18367  * @extends Roo.util.Observable
18368  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18369  * This class also supports single and multi selection modes. <br>
18370  * Create a data model bound view:
18371  <pre><code>
18372  var store = new Roo.data.Store(...);
18373
18374  var view = new Roo.View({
18375     el : "my-element",
18376     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18377  
18378     singleSelect: true,
18379     selectedClass: "ydataview-selected",
18380     store: store
18381  });
18382
18383  // listen for node click?
18384  view.on("click", function(vw, index, node, e){
18385  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18386  });
18387
18388  // load XML data
18389  dataModel.load("foobar.xml");
18390  </code></pre>
18391  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18392  * <br><br>
18393  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18394  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18395  * 
18396  * Note: old style constructor is still suported (container, template, config)
18397  * 
18398  * @constructor
18399  * Create a new View
18400  * @param {Object} config The config object
18401  * 
18402  */
18403 Roo.View = function(config, depreciated_tpl, depreciated_config){
18404     
18405     this.parent = false;
18406     
18407     if (typeof(depreciated_tpl) == 'undefined') {
18408         // new way.. - universal constructor.
18409         Roo.apply(this, config);
18410         this.el  = Roo.get(this.el);
18411     } else {
18412         // old format..
18413         this.el  = Roo.get(config);
18414         this.tpl = depreciated_tpl;
18415         Roo.apply(this, depreciated_config);
18416     }
18417     this.wrapEl  = this.el.wrap().wrap();
18418     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18419     
18420     
18421     if(typeof(this.tpl) == "string"){
18422         this.tpl = new Roo.Template(this.tpl);
18423     } else {
18424         // support xtype ctors..
18425         this.tpl = new Roo.factory(this.tpl, Roo);
18426     }
18427     
18428     
18429     this.tpl.compile();
18430     
18431     /** @private */
18432     this.addEvents({
18433         /**
18434          * @event beforeclick
18435          * Fires before a click is processed. Returns false to cancel the default action.
18436          * @param {Roo.View} this
18437          * @param {Number} index The index of the target node
18438          * @param {HTMLElement} node The target node
18439          * @param {Roo.EventObject} e The raw event object
18440          */
18441             "beforeclick" : true,
18442         /**
18443          * @event click
18444          * Fires when a template node is clicked.
18445          * @param {Roo.View} this
18446          * @param {Number} index The index of the target node
18447          * @param {HTMLElement} node The target node
18448          * @param {Roo.EventObject} e The raw event object
18449          */
18450             "click" : true,
18451         /**
18452          * @event dblclick
18453          * Fires when a template node is double clicked.
18454          * @param {Roo.View} this
18455          * @param {Number} index The index of the target node
18456          * @param {HTMLElement} node The target node
18457          * @param {Roo.EventObject} e The raw event object
18458          */
18459             "dblclick" : true,
18460         /**
18461          * @event contextmenu
18462          * Fires when a template node is right clicked.
18463          * @param {Roo.View} this
18464          * @param {Number} index The index of the target node
18465          * @param {HTMLElement} node The target node
18466          * @param {Roo.EventObject} e The raw event object
18467          */
18468             "contextmenu" : true,
18469         /**
18470          * @event selectionchange
18471          * Fires when the selected nodes change.
18472          * @param {Roo.View} this
18473          * @param {Array} selections Array of the selected nodes
18474          */
18475             "selectionchange" : true,
18476     
18477         /**
18478          * @event beforeselect
18479          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18480          * @param {Roo.View} this
18481          * @param {HTMLElement} node The node to be selected
18482          * @param {Array} selections Array of currently selected nodes
18483          */
18484             "beforeselect" : true,
18485         /**
18486          * @event preparedata
18487          * Fires on every row to render, to allow you to change the data.
18488          * @param {Roo.View} this
18489          * @param {Object} data to be rendered (change this)
18490          */
18491           "preparedata" : true
18492           
18493           
18494         });
18495
18496
18497
18498     this.el.on({
18499         "click": this.onClick,
18500         "dblclick": this.onDblClick,
18501         "contextmenu": this.onContextMenu,
18502         scope:this
18503     });
18504
18505     this.selections = [];
18506     this.nodes = [];
18507     this.cmp = new Roo.CompositeElementLite([]);
18508     if(this.store){
18509         this.store = Roo.factory(this.store, Roo.data);
18510         this.setStore(this.store, true);
18511     }
18512     
18513     if ( this.footer && this.footer.xtype) {
18514            
18515          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18516         
18517         this.footer.dataSource = this.store;
18518         this.footer.container = fctr;
18519         this.footer = Roo.factory(this.footer, Roo);
18520         fctr.insertFirst(this.el);
18521         
18522         // this is a bit insane - as the paging toolbar seems to detach the el..
18523 //        dom.parentNode.parentNode.parentNode
18524          // they get detached?
18525     }
18526     
18527     
18528     Roo.View.superclass.constructor.call(this);
18529     
18530     
18531 };
18532
18533 Roo.extend(Roo.View, Roo.util.Observable, {
18534     
18535      /**
18536      * @cfg {Roo.data.Store} store Data store to load data from.
18537      */
18538     store : false,
18539     
18540     /**
18541      * @cfg {String|Roo.Element} el The container element.
18542      */
18543     el : '',
18544     
18545     /**
18546      * @cfg {String|Roo.Template} tpl The template used by this View 
18547      */
18548     tpl : false,
18549     /**
18550      * @cfg {String} dataName the named area of the template to use as the data area
18551      *                          Works with domtemplates roo-name="name"
18552      */
18553     dataName: false,
18554     /**
18555      * @cfg {String} selectedClass The css class to add to selected nodes
18556      */
18557     selectedClass : "x-view-selected",
18558      /**
18559      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18560      */
18561     emptyText : "",
18562     
18563     /**
18564      * @cfg {String} text to display on mask (default Loading)
18565      */
18566     mask : false,
18567     /**
18568      * @cfg {Boolean} multiSelect Allow multiple selection
18569      */
18570     multiSelect : false,
18571     /**
18572      * @cfg {Boolean} singleSelect Allow single selection
18573      */
18574     singleSelect:  false,
18575     
18576     /**
18577      * @cfg {Boolean} toggleSelect - selecting 
18578      */
18579     toggleSelect : false,
18580     
18581     /**
18582      * @cfg {Boolean} tickable - selecting 
18583      */
18584     tickable : false,
18585     
18586     /**
18587      * Returns the element this view is bound to.
18588      * @return {Roo.Element}
18589      */
18590     getEl : function(){
18591         return this.wrapEl;
18592     },
18593     
18594     
18595
18596     /**
18597      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18598      */
18599     refresh : function(){
18600         //Roo.log('refresh');
18601         var t = this.tpl;
18602         
18603         // if we are using something like 'domtemplate', then
18604         // the what gets used is:
18605         // t.applySubtemplate(NAME, data, wrapping data..)
18606         // the outer template then get' applied with
18607         //     the store 'extra data'
18608         // and the body get's added to the
18609         //      roo-name="data" node?
18610         //      <span class='roo-tpl-{name}'></span> ?????
18611         
18612         
18613         
18614         this.clearSelections();
18615         this.el.update("");
18616         var html = [];
18617         var records = this.store.getRange();
18618         if(records.length < 1) {
18619             
18620             // is this valid??  = should it render a template??
18621             
18622             this.el.update(this.emptyText);
18623             return;
18624         }
18625         var el = this.el;
18626         if (this.dataName) {
18627             this.el.update(t.apply(this.store.meta)); //????
18628             el = this.el.child('.roo-tpl-' + this.dataName);
18629         }
18630         
18631         for(var i = 0, len = records.length; i < len; i++){
18632             var data = this.prepareData(records[i].data, i, records[i]);
18633             this.fireEvent("preparedata", this, data, i, records[i]);
18634             
18635             var d = Roo.apply({}, data);
18636             
18637             if(this.tickable){
18638                 Roo.apply(d, {'roo-id' : Roo.id()});
18639                 
18640                 var _this = this;
18641             
18642                 Roo.each(this.parent.item, function(item){
18643                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18644                         return;
18645                     }
18646                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18647                 });
18648             }
18649             
18650             html[html.length] = Roo.util.Format.trim(
18651                 this.dataName ?
18652                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18653                     t.apply(d)
18654             );
18655         }
18656         
18657         
18658         
18659         el.update(html.join(""));
18660         this.nodes = el.dom.childNodes;
18661         this.updateIndexes(0);
18662     },
18663     
18664
18665     /**
18666      * Function to override to reformat the data that is sent to
18667      * the template for each node.
18668      * DEPRICATED - use the preparedata event handler.
18669      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18670      * a JSON object for an UpdateManager bound view).
18671      */
18672     prepareData : function(data, index, record)
18673     {
18674         this.fireEvent("preparedata", this, data, index, record);
18675         return data;
18676     },
18677
18678     onUpdate : function(ds, record){
18679         // Roo.log('on update');   
18680         this.clearSelections();
18681         var index = this.store.indexOf(record);
18682         var n = this.nodes[index];
18683         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18684         n.parentNode.removeChild(n);
18685         this.updateIndexes(index, index);
18686     },
18687
18688     
18689     
18690 // --------- FIXME     
18691     onAdd : function(ds, records, index)
18692     {
18693         //Roo.log(['on Add', ds, records, index] );        
18694         this.clearSelections();
18695         if(this.nodes.length == 0){
18696             this.refresh();
18697             return;
18698         }
18699         var n = this.nodes[index];
18700         for(var i = 0, len = records.length; i < len; i++){
18701             var d = this.prepareData(records[i].data, i, records[i]);
18702             if(n){
18703                 this.tpl.insertBefore(n, d);
18704             }else{
18705                 
18706                 this.tpl.append(this.el, d);
18707             }
18708         }
18709         this.updateIndexes(index);
18710     },
18711
18712     onRemove : function(ds, record, index){
18713        // Roo.log('onRemove');
18714         this.clearSelections();
18715         var el = this.dataName  ?
18716             this.el.child('.roo-tpl-' + this.dataName) :
18717             this.el; 
18718         
18719         el.dom.removeChild(this.nodes[index]);
18720         this.updateIndexes(index);
18721     },
18722
18723     /**
18724      * Refresh an individual node.
18725      * @param {Number} index
18726      */
18727     refreshNode : function(index){
18728         this.onUpdate(this.store, this.store.getAt(index));
18729     },
18730
18731     updateIndexes : function(startIndex, endIndex){
18732         var ns = this.nodes;
18733         startIndex = startIndex || 0;
18734         endIndex = endIndex || ns.length - 1;
18735         for(var i = startIndex; i <= endIndex; i++){
18736             ns[i].nodeIndex = i;
18737         }
18738     },
18739
18740     /**
18741      * Changes the data store this view uses and refresh the view.
18742      * @param {Store} store
18743      */
18744     setStore : function(store, initial){
18745         if(!initial && this.store){
18746             this.store.un("datachanged", this.refresh);
18747             this.store.un("add", this.onAdd);
18748             this.store.un("remove", this.onRemove);
18749             this.store.un("update", this.onUpdate);
18750             this.store.un("clear", this.refresh);
18751             this.store.un("beforeload", this.onBeforeLoad);
18752             this.store.un("load", this.onLoad);
18753             this.store.un("loadexception", this.onLoad);
18754         }
18755         if(store){
18756           
18757             store.on("datachanged", this.refresh, this);
18758             store.on("add", this.onAdd, this);
18759             store.on("remove", this.onRemove, this);
18760             store.on("update", this.onUpdate, this);
18761             store.on("clear", this.refresh, this);
18762             store.on("beforeload", this.onBeforeLoad, this);
18763             store.on("load", this.onLoad, this);
18764             store.on("loadexception", this.onLoad, this);
18765         }
18766         
18767         if(store){
18768             this.refresh();
18769         }
18770     },
18771     /**
18772      * onbeforeLoad - masks the loading area.
18773      *
18774      */
18775     onBeforeLoad : function(store,opts)
18776     {
18777          //Roo.log('onBeforeLoad');   
18778         if (!opts.add) {
18779             this.el.update("");
18780         }
18781         this.el.mask(this.mask ? this.mask : "Loading" ); 
18782     },
18783     onLoad : function ()
18784     {
18785         this.el.unmask();
18786     },
18787     
18788
18789     /**
18790      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18791      * @param {HTMLElement} node
18792      * @return {HTMLElement} The template node
18793      */
18794     findItemFromChild : function(node){
18795         var el = this.dataName  ?
18796             this.el.child('.roo-tpl-' + this.dataName,true) :
18797             this.el.dom; 
18798         
18799         if(!node || node.parentNode == el){
18800                     return node;
18801             }
18802             var p = node.parentNode;
18803             while(p && p != el){
18804             if(p.parentNode == el){
18805                 return p;
18806             }
18807             p = p.parentNode;
18808         }
18809             return null;
18810     },
18811
18812     /** @ignore */
18813     onClick : function(e){
18814         var item = this.findItemFromChild(e.getTarget());
18815         if(item){
18816             var index = this.indexOf(item);
18817             if(this.onItemClick(item, index, e) !== false){
18818                 this.fireEvent("click", this, index, item, e);
18819             }
18820         }else{
18821             this.clearSelections();
18822         }
18823     },
18824
18825     /** @ignore */
18826     onContextMenu : function(e){
18827         var item = this.findItemFromChild(e.getTarget());
18828         if(item){
18829             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18830         }
18831     },
18832
18833     /** @ignore */
18834     onDblClick : function(e){
18835         var item = this.findItemFromChild(e.getTarget());
18836         if(item){
18837             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18838         }
18839     },
18840
18841     onItemClick : function(item, index, e)
18842     {
18843         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18844             return false;
18845         }
18846         if (this.toggleSelect) {
18847             var m = this.isSelected(item) ? 'unselect' : 'select';
18848             //Roo.log(m);
18849             var _t = this;
18850             _t[m](item, true, false);
18851             return true;
18852         }
18853         if(this.multiSelect || this.singleSelect){
18854             if(this.multiSelect && e.shiftKey && this.lastSelection){
18855                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18856             }else{
18857                 this.select(item, this.multiSelect && e.ctrlKey);
18858                 this.lastSelection = item;
18859             }
18860             
18861             if(!this.tickable){
18862                 e.preventDefault();
18863             }
18864             
18865         }
18866         return true;
18867     },
18868
18869     /**
18870      * Get the number of selected nodes.
18871      * @return {Number}
18872      */
18873     getSelectionCount : function(){
18874         return this.selections.length;
18875     },
18876
18877     /**
18878      * Get the currently selected nodes.
18879      * @return {Array} An array of HTMLElements
18880      */
18881     getSelectedNodes : function(){
18882         return this.selections;
18883     },
18884
18885     /**
18886      * Get the indexes of the selected nodes.
18887      * @return {Array}
18888      */
18889     getSelectedIndexes : function(){
18890         var indexes = [], s = this.selections;
18891         for(var i = 0, len = s.length; i < len; i++){
18892             indexes.push(s[i].nodeIndex);
18893         }
18894         return indexes;
18895     },
18896
18897     /**
18898      * Clear all selections
18899      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18900      */
18901     clearSelections : function(suppressEvent){
18902         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18903             this.cmp.elements = this.selections;
18904             this.cmp.removeClass(this.selectedClass);
18905             this.selections = [];
18906             if(!suppressEvent){
18907                 this.fireEvent("selectionchange", this, this.selections);
18908             }
18909         }
18910     },
18911
18912     /**
18913      * Returns true if the passed node is selected
18914      * @param {HTMLElement/Number} node The node or node index
18915      * @return {Boolean}
18916      */
18917     isSelected : function(node){
18918         var s = this.selections;
18919         if(s.length < 1){
18920             return false;
18921         }
18922         node = this.getNode(node);
18923         return s.indexOf(node) !== -1;
18924     },
18925
18926     /**
18927      * Selects nodes.
18928      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18929      * @param {Boolean} keepExisting (optional) true to keep existing selections
18930      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18931      */
18932     select : function(nodeInfo, keepExisting, suppressEvent){
18933         if(nodeInfo instanceof Array){
18934             if(!keepExisting){
18935                 this.clearSelections(true);
18936             }
18937             for(var i = 0, len = nodeInfo.length; i < len; i++){
18938                 this.select(nodeInfo[i], true, true);
18939             }
18940             return;
18941         } 
18942         var node = this.getNode(nodeInfo);
18943         if(!node || this.isSelected(node)){
18944             return; // already selected.
18945         }
18946         if(!keepExisting){
18947             this.clearSelections(true);
18948         }
18949         
18950         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18951             Roo.fly(node).addClass(this.selectedClass);
18952             this.selections.push(node);
18953             if(!suppressEvent){
18954                 this.fireEvent("selectionchange", this, this.selections);
18955             }
18956         }
18957         
18958         
18959     },
18960       /**
18961      * Unselects nodes.
18962      * @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
18963      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18964      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18965      */
18966     unselect : function(nodeInfo, keepExisting, suppressEvent)
18967     {
18968         if(nodeInfo instanceof Array){
18969             Roo.each(this.selections, function(s) {
18970                 this.unselect(s, nodeInfo);
18971             }, this);
18972             return;
18973         }
18974         var node = this.getNode(nodeInfo);
18975         if(!node || !this.isSelected(node)){
18976             //Roo.log("not selected");
18977             return; // not selected.
18978         }
18979         // fireevent???
18980         var ns = [];
18981         Roo.each(this.selections, function(s) {
18982             if (s == node ) {
18983                 Roo.fly(node).removeClass(this.selectedClass);
18984
18985                 return;
18986             }
18987             ns.push(s);
18988         },this);
18989         
18990         this.selections= ns;
18991         this.fireEvent("selectionchange", this, this.selections);
18992     },
18993
18994     /**
18995      * Gets a template node.
18996      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18997      * @return {HTMLElement} The node or null if it wasn't found
18998      */
18999     getNode : function(nodeInfo){
19000         if(typeof nodeInfo == "string"){
19001             return document.getElementById(nodeInfo);
19002         }else if(typeof nodeInfo == "number"){
19003             return this.nodes[nodeInfo];
19004         }
19005         return nodeInfo;
19006     },
19007
19008     /**
19009      * Gets a range template nodes.
19010      * @param {Number} startIndex
19011      * @param {Number} endIndex
19012      * @return {Array} An array of nodes
19013      */
19014     getNodes : function(start, end){
19015         var ns = this.nodes;
19016         start = start || 0;
19017         end = typeof end == "undefined" ? ns.length - 1 : end;
19018         var nodes = [];
19019         if(start <= end){
19020             for(var i = start; i <= end; i++){
19021                 nodes.push(ns[i]);
19022             }
19023         } else{
19024             for(var i = start; i >= end; i--){
19025                 nodes.push(ns[i]);
19026             }
19027         }
19028         return nodes;
19029     },
19030
19031     /**
19032      * Finds the index of the passed node
19033      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19034      * @return {Number} The index of the node or -1
19035      */
19036     indexOf : function(node){
19037         node = this.getNode(node);
19038         if(typeof node.nodeIndex == "number"){
19039             return node.nodeIndex;
19040         }
19041         var ns = this.nodes;
19042         for(var i = 0, len = ns.length; i < len; i++){
19043             if(ns[i] == node){
19044                 return i;
19045             }
19046         }
19047         return -1;
19048     }
19049 });
19050 /*
19051  * - LGPL
19052  *
19053  * based on jquery fullcalendar
19054  * 
19055  */
19056
19057 Roo.bootstrap = Roo.bootstrap || {};
19058 /**
19059  * @class Roo.bootstrap.Calendar
19060  * @extends Roo.bootstrap.Component
19061  * Bootstrap Calendar class
19062  * @cfg {Boolean} loadMask (true|false) default false
19063  * @cfg {Object} header generate the user specific header of the calendar, default false
19064
19065  * @constructor
19066  * Create a new Container
19067  * @param {Object} config The config object
19068  */
19069
19070
19071
19072 Roo.bootstrap.Calendar = function(config){
19073     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19074      this.addEvents({
19075         /**
19076              * @event select
19077              * Fires when a date is selected
19078              * @param {DatePicker} this
19079              * @param {Date} date The selected date
19080              */
19081         'select': true,
19082         /**
19083              * @event monthchange
19084              * Fires when the displayed month changes 
19085              * @param {DatePicker} this
19086              * @param {Date} date The selected month
19087              */
19088         'monthchange': true,
19089         /**
19090              * @event evententer
19091              * Fires when mouse over an event
19092              * @param {Calendar} this
19093              * @param {event} Event
19094              */
19095         'evententer': true,
19096         /**
19097              * @event eventleave
19098              * Fires when the mouse leaves an
19099              * @param {Calendar} this
19100              * @param {event}
19101              */
19102         'eventleave': true,
19103         /**
19104              * @event eventclick
19105              * Fires when the mouse click an
19106              * @param {Calendar} this
19107              * @param {event}
19108              */
19109         'eventclick': true
19110         
19111     });
19112
19113 };
19114
19115 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19116     
19117      /**
19118      * @cfg {Number} startDay
19119      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19120      */
19121     startDay : 0,
19122     
19123     loadMask : false,
19124     
19125     header : false,
19126       
19127     getAutoCreate : function(){
19128         
19129         
19130         var fc_button = function(name, corner, style, content ) {
19131             return Roo.apply({},{
19132                 tag : 'span',
19133                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19134                          (corner.length ?
19135                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19136                             ''
19137                         ),
19138                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19139                 unselectable: 'on'
19140             });
19141         };
19142         
19143         var header = {};
19144         
19145         if(!this.header){
19146             header = {
19147                 tag : 'table',
19148                 cls : 'fc-header',
19149                 style : 'width:100%',
19150                 cn : [
19151                     {
19152                         tag: 'tr',
19153                         cn : [
19154                             {
19155                                 tag : 'td',
19156                                 cls : 'fc-header-left',
19157                                 cn : [
19158                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19159                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19160                                     { tag: 'span', cls: 'fc-header-space' },
19161                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19162
19163
19164                                 ]
19165                             },
19166
19167                             {
19168                                 tag : 'td',
19169                                 cls : 'fc-header-center',
19170                                 cn : [
19171                                     {
19172                                         tag: 'span',
19173                                         cls: 'fc-header-title',
19174                                         cn : {
19175                                             tag: 'H2',
19176                                             html : 'month / year'
19177                                         }
19178                                     }
19179
19180                                 ]
19181                             },
19182                             {
19183                                 tag : 'td',
19184                                 cls : 'fc-header-right',
19185                                 cn : [
19186                               /*      fc_button('month', 'left', '', 'month' ),
19187                                     fc_button('week', '', '', 'week' ),
19188                                     fc_button('day', 'right', '', 'day' )
19189                                 */    
19190
19191                                 ]
19192                             }
19193
19194                         ]
19195                     }
19196                 ]
19197             };
19198         }
19199         
19200         header = this.header;
19201         
19202        
19203         var cal_heads = function() {
19204             var ret = [];
19205             // fixme - handle this.
19206             
19207             for (var i =0; i < Date.dayNames.length; i++) {
19208                 var d = Date.dayNames[i];
19209                 ret.push({
19210                     tag: 'th',
19211                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19212                     html : d.substring(0,3)
19213                 });
19214                 
19215             }
19216             ret[0].cls += ' fc-first';
19217             ret[6].cls += ' fc-last';
19218             return ret;
19219         };
19220         var cal_cell = function(n) {
19221             return  {
19222                 tag: 'td',
19223                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19224                 cn : [
19225                     {
19226                         cn : [
19227                             {
19228                                 cls: 'fc-day-number',
19229                                 html: 'D'
19230                             },
19231                             {
19232                                 cls: 'fc-day-content',
19233                              
19234                                 cn : [
19235                                      {
19236                                         style: 'position: relative;' // height: 17px;
19237                                     }
19238                                 ]
19239                             }
19240                             
19241                             
19242                         ]
19243                     }
19244                 ]
19245                 
19246             }
19247         };
19248         var cal_rows = function() {
19249             
19250             var ret = [];
19251             for (var r = 0; r < 6; r++) {
19252                 var row= {
19253                     tag : 'tr',
19254                     cls : 'fc-week',
19255                     cn : []
19256                 };
19257                 
19258                 for (var i =0; i < Date.dayNames.length; i++) {
19259                     var d = Date.dayNames[i];
19260                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19261
19262                 }
19263                 row.cn[0].cls+=' fc-first';
19264                 row.cn[0].cn[0].style = 'min-height:90px';
19265                 row.cn[6].cls+=' fc-last';
19266                 ret.push(row);
19267                 
19268             }
19269             ret[0].cls += ' fc-first';
19270             ret[4].cls += ' fc-prev-last';
19271             ret[5].cls += ' fc-last';
19272             return ret;
19273             
19274         };
19275         
19276         var cal_table = {
19277             tag: 'table',
19278             cls: 'fc-border-separate',
19279             style : 'width:100%',
19280             cellspacing  : 0,
19281             cn : [
19282                 { 
19283                     tag: 'thead',
19284                     cn : [
19285                         { 
19286                             tag: 'tr',
19287                             cls : 'fc-first fc-last',
19288                             cn : cal_heads()
19289                         }
19290                     ]
19291                 },
19292                 { 
19293                     tag: 'tbody',
19294                     cn : cal_rows()
19295                 }
19296                   
19297             ]
19298         };
19299          
19300          var cfg = {
19301             cls : 'fc fc-ltr',
19302             cn : [
19303                 header,
19304                 {
19305                     cls : 'fc-content',
19306                     style : "position: relative;",
19307                     cn : [
19308                         {
19309                             cls : 'fc-view fc-view-month fc-grid',
19310                             style : 'position: relative',
19311                             unselectable : 'on',
19312                             cn : [
19313                                 {
19314                                     cls : 'fc-event-container',
19315                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19316                                 },
19317                                 cal_table
19318                             ]
19319                         }
19320                     ]
19321     
19322                 }
19323            ] 
19324             
19325         };
19326         
19327          
19328         
19329         return cfg;
19330     },
19331     
19332     
19333     initEvents : function()
19334     {
19335         if(!this.store){
19336             throw "can not find store for calendar";
19337         }
19338         
19339         var mark = {
19340             tag: "div",
19341             cls:"x-dlg-mask",
19342             style: "text-align:center",
19343             cn: [
19344                 {
19345                     tag: "div",
19346                     style: "background-color:white;width:50%;margin:250 auto",
19347                     cn: [
19348                         {
19349                             tag: "img",
19350                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19351                         },
19352                         {
19353                             tag: "span",
19354                             html: "Loading"
19355                         }
19356                         
19357                     ]
19358                 }
19359             ]
19360         };
19361         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19362         
19363         var size = this.el.select('.fc-content', true).first().getSize();
19364         this.maskEl.setSize(size.width, size.height);
19365         this.maskEl.enableDisplayMode("block");
19366         if(!this.loadMask){
19367             this.maskEl.hide();
19368         }
19369         
19370         this.store = Roo.factory(this.store, Roo.data);
19371         this.store.on('load', this.onLoad, this);
19372         this.store.on('beforeload', this.onBeforeLoad, this);
19373         
19374         this.resize();
19375         
19376         this.cells = this.el.select('.fc-day',true);
19377         //Roo.log(this.cells);
19378         this.textNodes = this.el.query('.fc-day-number');
19379         this.cells.addClassOnOver('fc-state-hover');
19380         
19381         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19382         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19383         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19384         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19385         
19386         this.on('monthchange', this.onMonthChange, this);
19387         
19388         this.update(new Date().clearTime());
19389     },
19390     
19391     resize : function() {
19392         var sz  = this.el.getSize();
19393         
19394         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19395         this.el.select('.fc-day-content div',true).setHeight(34);
19396     },
19397     
19398     
19399     // private
19400     showPrevMonth : function(e){
19401         this.update(this.activeDate.add("mo", -1));
19402     },
19403     showToday : function(e){
19404         this.update(new Date().clearTime());
19405     },
19406     // private
19407     showNextMonth : function(e){
19408         this.update(this.activeDate.add("mo", 1));
19409     },
19410
19411     // private
19412     showPrevYear : function(){
19413         this.update(this.activeDate.add("y", -1));
19414     },
19415
19416     // private
19417     showNextYear : function(){
19418         this.update(this.activeDate.add("y", 1));
19419     },
19420
19421     
19422    // private
19423     update : function(date)
19424     {
19425         var vd = this.activeDate;
19426         this.activeDate = date;
19427 //        if(vd && this.el){
19428 //            var t = date.getTime();
19429 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19430 //                Roo.log('using add remove');
19431 //                
19432 //                this.fireEvent('monthchange', this, date);
19433 //                
19434 //                this.cells.removeClass("fc-state-highlight");
19435 //                this.cells.each(function(c){
19436 //                   if(c.dateValue == t){
19437 //                       c.addClass("fc-state-highlight");
19438 //                       setTimeout(function(){
19439 //                            try{c.dom.firstChild.focus();}catch(e){}
19440 //                       }, 50);
19441 //                       return false;
19442 //                   }
19443 //                   return true;
19444 //                });
19445 //                return;
19446 //            }
19447 //        }
19448         
19449         var days = date.getDaysInMonth();
19450         
19451         var firstOfMonth = date.getFirstDateOfMonth();
19452         var startingPos = firstOfMonth.getDay()-this.startDay;
19453         
19454         if(startingPos < this.startDay){
19455             startingPos += 7;
19456         }
19457         
19458         var pm = date.add(Date.MONTH, -1);
19459         var prevStart = pm.getDaysInMonth()-startingPos;
19460 //        
19461         this.cells = this.el.select('.fc-day',true);
19462         this.textNodes = this.el.query('.fc-day-number');
19463         this.cells.addClassOnOver('fc-state-hover');
19464         
19465         var cells = this.cells.elements;
19466         var textEls = this.textNodes;
19467         
19468         Roo.each(cells, function(cell){
19469             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19470         });
19471         
19472         days += startingPos;
19473
19474         // convert everything to numbers so it's fast
19475         var day = 86400000;
19476         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19477         //Roo.log(d);
19478         //Roo.log(pm);
19479         //Roo.log(prevStart);
19480         
19481         var today = new Date().clearTime().getTime();
19482         var sel = date.clearTime().getTime();
19483         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19484         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19485         var ddMatch = this.disabledDatesRE;
19486         var ddText = this.disabledDatesText;
19487         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19488         var ddaysText = this.disabledDaysText;
19489         var format = this.format;
19490         
19491         var setCellClass = function(cal, cell){
19492             cell.row = 0;
19493             cell.events = [];
19494             cell.more = [];
19495             //Roo.log('set Cell Class');
19496             cell.title = "";
19497             var t = d.getTime();
19498             
19499             //Roo.log(d);
19500             
19501             cell.dateValue = t;
19502             if(t == today){
19503                 cell.className += " fc-today";
19504                 cell.className += " fc-state-highlight";
19505                 cell.title = cal.todayText;
19506             }
19507             if(t == sel){
19508                 // disable highlight in other month..
19509                 //cell.className += " fc-state-highlight";
19510                 
19511             }
19512             // disabling
19513             if(t < min) {
19514                 cell.className = " fc-state-disabled";
19515                 cell.title = cal.minText;
19516                 return;
19517             }
19518             if(t > max) {
19519                 cell.className = " fc-state-disabled";
19520                 cell.title = cal.maxText;
19521                 return;
19522             }
19523             if(ddays){
19524                 if(ddays.indexOf(d.getDay()) != -1){
19525                     cell.title = ddaysText;
19526                     cell.className = " fc-state-disabled";
19527                 }
19528             }
19529             if(ddMatch && format){
19530                 var fvalue = d.dateFormat(format);
19531                 if(ddMatch.test(fvalue)){
19532                     cell.title = ddText.replace("%0", fvalue);
19533                     cell.className = " fc-state-disabled";
19534                 }
19535             }
19536             
19537             if (!cell.initialClassName) {
19538                 cell.initialClassName = cell.dom.className;
19539             }
19540             
19541             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19542         };
19543
19544         var i = 0;
19545         
19546         for(; i < startingPos; i++) {
19547             textEls[i].innerHTML = (++prevStart);
19548             d.setDate(d.getDate()+1);
19549             
19550             cells[i].className = "fc-past fc-other-month";
19551             setCellClass(this, cells[i]);
19552         }
19553         
19554         var intDay = 0;
19555         
19556         for(; i < days; i++){
19557             intDay = i - startingPos + 1;
19558             textEls[i].innerHTML = (intDay);
19559             d.setDate(d.getDate()+1);
19560             
19561             cells[i].className = ''; // "x-date-active";
19562             setCellClass(this, cells[i]);
19563         }
19564         var extraDays = 0;
19565         
19566         for(; i < 42; i++) {
19567             textEls[i].innerHTML = (++extraDays);
19568             d.setDate(d.getDate()+1);
19569             
19570             cells[i].className = "fc-future fc-other-month";
19571             setCellClass(this, cells[i]);
19572         }
19573         
19574         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19575         
19576         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19577         
19578         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19579         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19580         
19581         if(totalRows != 6){
19582             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19583             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19584         }
19585         
19586         this.fireEvent('monthchange', this, date);
19587         
19588         
19589         /*
19590         if(!this.internalRender){
19591             var main = this.el.dom.firstChild;
19592             var w = main.offsetWidth;
19593             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19594             Roo.fly(main).setWidth(w);
19595             this.internalRender = true;
19596             // opera does not respect the auto grow header center column
19597             // then, after it gets a width opera refuses to recalculate
19598             // without a second pass
19599             if(Roo.isOpera && !this.secondPass){
19600                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19601                 this.secondPass = true;
19602                 this.update.defer(10, this, [date]);
19603             }
19604         }
19605         */
19606         
19607     },
19608     
19609     findCell : function(dt) {
19610         dt = dt.clearTime().getTime();
19611         var ret = false;
19612         this.cells.each(function(c){
19613             //Roo.log("check " +c.dateValue + '?=' + dt);
19614             if(c.dateValue == dt){
19615                 ret = c;
19616                 return false;
19617             }
19618             return true;
19619         });
19620         
19621         return ret;
19622     },
19623     
19624     findCells : function(ev) {
19625         var s = ev.start.clone().clearTime().getTime();
19626        // Roo.log(s);
19627         var e= ev.end.clone().clearTime().getTime();
19628        // Roo.log(e);
19629         var ret = [];
19630         this.cells.each(function(c){
19631              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19632             
19633             if(c.dateValue > e){
19634                 return ;
19635             }
19636             if(c.dateValue < s){
19637                 return ;
19638             }
19639             ret.push(c);
19640         });
19641         
19642         return ret;    
19643     },
19644     
19645 //    findBestRow: function(cells)
19646 //    {
19647 //        var ret = 0;
19648 //        
19649 //        for (var i =0 ; i < cells.length;i++) {
19650 //            ret  = Math.max(cells[i].rows || 0,ret);
19651 //        }
19652 //        return ret;
19653 //        
19654 //    },
19655     
19656     
19657     addItem : function(ev)
19658     {
19659         // look for vertical location slot in
19660         var cells = this.findCells(ev);
19661         
19662 //        ev.row = this.findBestRow(cells);
19663         
19664         // work out the location.
19665         
19666         var crow = false;
19667         var rows = [];
19668         for(var i =0; i < cells.length; i++) {
19669             
19670             cells[i].row = cells[0].row;
19671             
19672             if(i == 0){
19673                 cells[i].row = cells[i].row + 1;
19674             }
19675             
19676             if (!crow) {
19677                 crow = {
19678                     start : cells[i],
19679                     end :  cells[i]
19680                 };
19681                 continue;
19682             }
19683             if (crow.start.getY() == cells[i].getY()) {
19684                 // on same row.
19685                 crow.end = cells[i];
19686                 continue;
19687             }
19688             // different row.
19689             rows.push(crow);
19690             crow = {
19691                 start: cells[i],
19692                 end : cells[i]
19693             };
19694             
19695         }
19696         
19697         rows.push(crow);
19698         ev.els = [];
19699         ev.rows = rows;
19700         ev.cells = cells;
19701         
19702         cells[0].events.push(ev);
19703         
19704         this.calevents.push(ev);
19705     },
19706     
19707     clearEvents: function() {
19708         
19709         if(!this.calevents){
19710             return;
19711         }
19712         
19713         Roo.each(this.cells.elements, function(c){
19714             c.row = 0;
19715             c.events = [];
19716             c.more = [];
19717         });
19718         
19719         Roo.each(this.calevents, function(e) {
19720             Roo.each(e.els, function(el) {
19721                 el.un('mouseenter' ,this.onEventEnter, this);
19722                 el.un('mouseleave' ,this.onEventLeave, this);
19723                 el.remove();
19724             },this);
19725         },this);
19726         
19727         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19728             e.remove();
19729         });
19730         
19731     },
19732     
19733     renderEvents: function()
19734     {   
19735         var _this = this;
19736         
19737         this.cells.each(function(c) {
19738             
19739             if(c.row < 5){
19740                 return;
19741             }
19742             
19743             var ev = c.events;
19744             
19745             var r = 4;
19746             if(c.row != c.events.length){
19747                 r = 4 - (4 - (c.row - c.events.length));
19748             }
19749             
19750             c.events = ev.slice(0, r);
19751             c.more = ev.slice(r);
19752             
19753             if(c.more.length && c.more.length == 1){
19754                 c.events.push(c.more.pop());
19755             }
19756             
19757             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19758             
19759         });
19760             
19761         this.cells.each(function(c) {
19762             
19763             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19764             
19765             
19766             for (var e = 0; e < c.events.length; e++){
19767                 var ev = c.events[e];
19768                 var rows = ev.rows;
19769                 
19770                 for(var i = 0; i < rows.length; i++) {
19771                 
19772                     // how many rows should it span..
19773
19774                     var  cfg = {
19775                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19776                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19777
19778                         unselectable : "on",
19779                         cn : [
19780                             {
19781                                 cls: 'fc-event-inner',
19782                                 cn : [
19783     //                                {
19784     //                                  tag:'span',
19785     //                                  cls: 'fc-event-time',
19786     //                                  html : cells.length > 1 ? '' : ev.time
19787     //                                },
19788                                     {
19789                                       tag:'span',
19790                                       cls: 'fc-event-title',
19791                                       html : String.format('{0}', ev.title)
19792                                     }
19793
19794
19795                                 ]
19796                             },
19797                             {
19798                                 cls: 'ui-resizable-handle ui-resizable-e',
19799                                 html : '&nbsp;&nbsp;&nbsp'
19800                             }
19801
19802                         ]
19803                     };
19804
19805                     if (i == 0) {
19806                         cfg.cls += ' fc-event-start';
19807                     }
19808                     if ((i+1) == rows.length) {
19809                         cfg.cls += ' fc-event-end';
19810                     }
19811
19812                     var ctr = _this.el.select('.fc-event-container',true).first();
19813                     var cg = ctr.createChild(cfg);
19814
19815                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19816                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19817
19818                     var r = (c.more.length) ? 1 : 0;
19819                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19820                     cg.setWidth(ebox.right - sbox.x -2);
19821
19822                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19823                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19824                     cg.on('click', _this.onEventClick, _this, ev);
19825
19826                     ev.els.push(cg);
19827                     
19828                 }
19829                 
19830             }
19831             
19832             
19833             if(c.more.length){
19834                 var  cfg = {
19835                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19836                     style : 'position: absolute',
19837                     unselectable : "on",
19838                     cn : [
19839                         {
19840                             cls: 'fc-event-inner',
19841                             cn : [
19842                                 {
19843                                   tag:'span',
19844                                   cls: 'fc-event-title',
19845                                   html : 'More'
19846                                 }
19847
19848
19849                             ]
19850                         },
19851                         {
19852                             cls: 'ui-resizable-handle ui-resizable-e',
19853                             html : '&nbsp;&nbsp;&nbsp'
19854                         }
19855
19856                     ]
19857                 };
19858
19859                 var ctr = _this.el.select('.fc-event-container',true).first();
19860                 var cg = ctr.createChild(cfg);
19861
19862                 var sbox = c.select('.fc-day-content',true).first().getBox();
19863                 var ebox = c.select('.fc-day-content',true).first().getBox();
19864                 //Roo.log(cg);
19865                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19866                 cg.setWidth(ebox.right - sbox.x -2);
19867
19868                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19869                 
19870             }
19871             
19872         });
19873         
19874         
19875         
19876     },
19877     
19878     onEventEnter: function (e, el,event,d) {
19879         this.fireEvent('evententer', this, el, event);
19880     },
19881     
19882     onEventLeave: function (e, el,event,d) {
19883         this.fireEvent('eventleave', this, el, event);
19884     },
19885     
19886     onEventClick: function (e, el,event,d) {
19887         this.fireEvent('eventclick', this, el, event);
19888     },
19889     
19890     onMonthChange: function () {
19891         this.store.load();
19892     },
19893     
19894     onMoreEventClick: function(e, el, more)
19895     {
19896         var _this = this;
19897         
19898         this.calpopover.placement = 'right';
19899         this.calpopover.setTitle('More');
19900         
19901         this.calpopover.setContent('');
19902         
19903         var ctr = this.calpopover.el.select('.popover-content', true).first();
19904         
19905         Roo.each(more, function(m){
19906             var cfg = {
19907                 cls : 'fc-event-hori fc-event-draggable',
19908                 html : m.title
19909             };
19910             var cg = ctr.createChild(cfg);
19911             
19912             cg.on('click', _this.onEventClick, _this, m);
19913         });
19914         
19915         this.calpopover.show(el);
19916         
19917         
19918     },
19919     
19920     onLoad: function () 
19921     {   
19922         this.calevents = [];
19923         var cal = this;
19924         
19925         if(this.store.getCount() > 0){
19926             this.store.data.each(function(d){
19927                cal.addItem({
19928                     id : d.data.id,
19929                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19930                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19931                     time : d.data.start_time,
19932                     title : d.data.title,
19933                     description : d.data.description,
19934                     venue : d.data.venue
19935                 });
19936             });
19937         }
19938         
19939         this.renderEvents();
19940         
19941         if(this.calevents.length && this.loadMask){
19942             this.maskEl.hide();
19943         }
19944     },
19945     
19946     onBeforeLoad: function()
19947     {
19948         this.clearEvents();
19949         if(this.loadMask){
19950             this.maskEl.show();
19951         }
19952     }
19953 });
19954
19955  
19956  /*
19957  * - LGPL
19958  *
19959  * element
19960  * 
19961  */
19962
19963 /**
19964  * @class Roo.bootstrap.Popover
19965  * @extends Roo.bootstrap.Component
19966  * Bootstrap Popover class
19967  * @cfg {String} html contents of the popover   (or false to use children..)
19968  * @cfg {String} title of popover (or false to hide)
19969  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19970  * @cfg {String} trigger click || hover (or false to trigger manually)
19971  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19972  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19973  *      - if false and it has a 'parent' then it will be automatically added to that element
19974  *      - if string - Roo.get  will be called 
19975  * @cfg {Number} delay - delay before showing
19976  
19977  * @constructor
19978  * Create a new Popover
19979  * @param {Object} config The config object
19980  */
19981
19982 Roo.bootstrap.Popover = function(config){
19983     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19984     
19985     this.addEvents({
19986         // raw events
19987          /**
19988          * @event show
19989          * After the popover show
19990          * 
19991          * @param {Roo.bootstrap.Popover} this
19992          */
19993         "show" : true,
19994         /**
19995          * @event hide
19996          * After the popover hide
19997          * 
19998          * @param {Roo.bootstrap.Popover} this
19999          */
20000         "hide" : true
20001     });
20002 };
20003
20004 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
20005     
20006     title: false,
20007     html: false,
20008     
20009     placement : 'right',
20010     trigger : 'hover', // hover
20011     modal : false,
20012     delay : 0,
20013     
20014     over: false,
20015     
20016     can_build_overlaid : false,
20017     
20018     maskEl : false, // the mask element
20019     headerEl : false,
20020     contentEl : false,
20021     alignEl : false, // when show is called with an element - this get's stored.
20022     
20023     getChildContainer : function()
20024     {
20025         return this.contentEl;
20026         
20027     },
20028     getPopoverHeader : function()
20029     {
20030         this.title = true; // flag not to hide it..
20031         this.headerEl.addClass('p-0');
20032         return this.headerEl
20033     },
20034     
20035     
20036     getAutoCreate : function(){
20037          
20038         var cfg = {
20039            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20040            style: 'display:block',
20041            cn : [
20042                 {
20043                     cls : 'arrow'
20044                 },
20045                 {
20046                     cls : 'popover-inner ',
20047                     cn : [
20048                         {
20049                             tag: 'h3',
20050                             cls: 'popover-title popover-header',
20051                             html : this.title === false ? '' : this.title
20052                         },
20053                         {
20054                             cls : 'popover-content popover-body '  + (this.cls || ''),
20055                             html : this.html || ''
20056                         }
20057                     ]
20058                     
20059                 }
20060            ]
20061         };
20062         
20063         return cfg;
20064     },
20065     /**
20066      * @param {string} the title
20067      */
20068     setTitle: function(str)
20069     {
20070         this.title = str;
20071         if (this.el) {
20072             this.headerEl.dom.innerHTML = str;
20073         }
20074         
20075     },
20076     /**
20077      * @param {string} the body content
20078      */
20079     setContent: function(str)
20080     {
20081         this.html = str;
20082         if (this.contentEl) {
20083             this.contentEl.dom.innerHTML = str;
20084         }
20085         
20086     },
20087     // as it get's added to the bottom of the page.
20088     onRender : function(ct, position)
20089     {
20090         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20091         
20092         
20093         
20094         if(!this.el){
20095             var cfg = Roo.apply({},  this.getAutoCreate());
20096             cfg.id = Roo.id();
20097             
20098             if (this.cls) {
20099                 cfg.cls += ' ' + this.cls;
20100             }
20101             if (this.style) {
20102                 cfg.style = this.style;
20103             }
20104             //Roo.log("adding to ");
20105             this.el = Roo.get(document.body).createChild(cfg, position);
20106 //            Roo.log(this.el);
20107         }
20108         
20109         this.contentEl = this.el.select('.popover-content',true).first();
20110         this.headerEl =  this.el.select('.popover-title',true).first();
20111         
20112         var nitems = [];
20113         if(typeof(this.items) != 'undefined'){
20114             var items = this.items;
20115             delete this.items;
20116
20117             for(var i =0;i < items.length;i++) {
20118                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20119             }
20120         }
20121
20122         this.items = nitems;
20123         
20124         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20125         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20126         
20127         
20128         
20129         this.initEvents();
20130     },
20131     
20132     resizeMask : function()
20133     {
20134         this.maskEl.setSize(
20135             Roo.lib.Dom.getViewWidth(true),
20136             Roo.lib.Dom.getViewHeight(true)
20137         );
20138     },
20139     
20140     initEvents : function()
20141     {
20142         
20143         if (!this.modal) { 
20144             Roo.bootstrap.Popover.register(this);
20145         }
20146          
20147         this.arrowEl = this.el.select('.arrow',true).first();
20148         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20149         this.el.enableDisplayMode('block');
20150         this.el.hide();
20151  
20152         
20153         if (this.over === false && !this.parent()) {
20154             return; 
20155         }
20156         if (this.triggers === false) {
20157             return;
20158         }
20159          
20160         // support parent
20161         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20162         var triggers = this.trigger ? this.trigger.split(' ') : [];
20163         Roo.each(triggers, function(trigger) {
20164         
20165             if (trigger == 'click') {
20166                 on_el.on('click', this.toggle, this);
20167             } else if (trigger != 'manual') {
20168                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20169                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20170       
20171                 on_el.on(eventIn  ,this.enter, this);
20172                 on_el.on(eventOut, this.leave, this);
20173             }
20174         }, this);
20175     },
20176     
20177     
20178     // private
20179     timeout : null,
20180     hoverState : null,
20181     
20182     toggle : function () {
20183         this.hoverState == 'in' ? this.leave() : this.enter();
20184     },
20185     
20186     enter : function () {
20187         
20188         clearTimeout(this.timeout);
20189     
20190         this.hoverState = 'in';
20191     
20192         if (!this.delay || !this.delay.show) {
20193             this.show();
20194             return;
20195         }
20196         var _t = this;
20197         this.timeout = setTimeout(function () {
20198             if (_t.hoverState == 'in') {
20199                 _t.show();
20200             }
20201         }, this.delay.show)
20202     },
20203     
20204     leave : function() {
20205         clearTimeout(this.timeout);
20206     
20207         this.hoverState = 'out';
20208     
20209         if (!this.delay || !this.delay.hide) {
20210             this.hide();
20211             return;
20212         }
20213         var _t = this;
20214         this.timeout = setTimeout(function () {
20215             if (_t.hoverState == 'out') {
20216                 _t.hide();
20217             }
20218         }, this.delay.hide)
20219     },
20220     /**
20221      * Show the popover
20222      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20223      * @param {string} (left|right|top|bottom) position
20224      */
20225     show : function (on_el, placement)
20226     {
20227         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20228         on_el = on_el || false; // default to false
20229          
20230         if (!on_el) {
20231             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20232                 on_el = this.parent().el;
20233             } else if (this.over) {
20234                 on_el = Roo.get(this.over);
20235             }
20236             
20237         }
20238         
20239         this.alignEl = Roo.get( on_el );
20240
20241         if (!this.el) {
20242             this.render(document.body);
20243         }
20244         
20245         
20246          
20247         
20248         if (this.title === false) {
20249             this.headerEl.hide();
20250         }
20251         
20252        
20253         this.el.show();
20254         this.el.dom.style.display = 'block';
20255          
20256  
20257         if (this.alignEl) {
20258             this.updatePosition(this.placement, true);
20259              
20260         } else {
20261             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20262             var es = this.el.getSize();
20263             var x = Roo.lib.Dom.getViewWidth()/2;
20264             var y = Roo.lib.Dom.getViewHeight()/2;
20265             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20266             
20267         }
20268
20269         
20270         //var arrow = this.el.select('.arrow',true).first();
20271         //arrow.set(align[2], 
20272         
20273         this.el.addClass('in');
20274         
20275          
20276         
20277         this.hoverState = 'in';
20278         
20279         if (this.modal) {
20280             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20281             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20282             this.maskEl.dom.style.display = 'block';
20283             this.maskEl.addClass('show');
20284         }
20285         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20286  
20287         this.fireEvent('show', this);
20288         
20289     },
20290     /**
20291      * fire this manually after loading a grid in the table for example
20292      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20293      * @param {Boolean} try and move it if we cant get right position.
20294      */
20295     updatePosition : function(placement, try_move)
20296     {
20297         // allow for calling with no parameters
20298         placement = placement   ? placement :  this.placement;
20299         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20300         
20301         this.el.removeClass([
20302             'fade','top','bottom', 'left', 'right','in',
20303             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20304         ]);
20305         this.el.addClass(placement + ' bs-popover-' + placement);
20306         
20307         if (!this.alignEl ) {
20308             return false;
20309         }
20310         
20311         switch (placement) {
20312             case 'right':
20313                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20314                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20315                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20316                     //normal display... or moved up/down.
20317                     this.el.setXY(offset);
20318                     var xy = this.alignEl.getAnchorXY('tr', false);
20319                     xy[0]+=2;xy[1]+=5;
20320                     this.arrowEl.setXY(xy);
20321                     return true;
20322                 }
20323                 // continue through...
20324                 return this.updatePosition('left', false);
20325                 
20326             
20327             case 'left':
20328                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20329                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20330                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20331                     //normal display... or moved up/down.
20332                     this.el.setXY(offset);
20333                     var xy = this.alignEl.getAnchorXY('tl', false);
20334                     xy[0]-=10;xy[1]+=5; // << fix me
20335                     this.arrowEl.setXY(xy);
20336                     return true;
20337                 }
20338                 // call self...
20339                 return this.updatePosition('right', false);
20340             
20341             case 'top':
20342                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20343                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20344                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20345                     //normal display... or moved up/down.
20346                     this.el.setXY(offset);
20347                     var xy = this.alignEl.getAnchorXY('t', false);
20348                     xy[1]-=10; // << fix me
20349                     this.arrowEl.setXY(xy);
20350                     return true;
20351                 }
20352                 // fall through
20353                return this.updatePosition('bottom', false);
20354             
20355             case 'bottom':
20356                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20357                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20358                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20359                     //normal display... or moved up/down.
20360                     this.el.setXY(offset);
20361                     var xy = this.alignEl.getAnchorXY('b', false);
20362                      xy[1]+=2; // << fix me
20363                     this.arrowEl.setXY(xy);
20364                     return true;
20365                 }
20366                 // fall through
20367                 return this.updatePosition('top', false);
20368                 
20369             
20370         }
20371         
20372         
20373         return false;
20374     },
20375     
20376     hide : function()
20377     {
20378         this.el.setXY([0,0]);
20379         this.el.removeClass('in');
20380         this.el.hide();
20381         this.hoverState = null;
20382         this.maskEl.hide(); // always..
20383         this.fireEvent('hide', this);
20384     }
20385     
20386 });
20387
20388
20389 Roo.apply(Roo.bootstrap.Popover, {
20390
20391     alignment : {
20392         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20393         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20394         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20395         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20396     },
20397     
20398     zIndex : 20001,
20399
20400     clickHander : false,
20401     
20402     
20403
20404     onMouseDown : function(e)
20405     {
20406         if (this.popup.length &&  !e.getTarget(".roo-popover") && this.popup.length) {
20407             /// what is nothing is showing..
20408             this.hideAll();
20409         }
20410          
20411     },
20412     
20413     
20414     popups : [],
20415     
20416     register : function(popup)
20417     {
20418         if (!Roo.bootstrap.Popover.clickHandler) {
20419             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20420         }
20421         // hide other popups.
20422         popup.on('show', Roo.bootstrap.Popover.onShow, Roo.bootstrap.Popover, popup);
20423         popup.on('hide', Roo.bootstrap.Popover.onHide, Roo.bootstrap.Popover, popup);
20424         this.hideAll();
20425         this.popups.push(popup);
20426     },
20427     hideAll : function()
20428     {
20429         this.popups.forEach(function(p) {
20430             p.hide();
20431         });
20432     },
20433     onShow : function(p) {
20434         this.popups.push(p);
20435     }
20436
20437 });/*
20438  * - LGPL
20439  *
20440  * Card header - holder for the card header elements.
20441  * 
20442  */
20443
20444 /**
20445  * @class Roo.bootstrap.PopoverNav
20446  * @extends Roo.bootstrap.NavGroup
20447  * Bootstrap Popover header navigation class
20448  * @constructor
20449  * Create a new Popover Header Navigation 
20450  * @param {Object} config The config object
20451  */
20452
20453 Roo.bootstrap.PopoverNav = function(config){
20454     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20455 };
20456
20457 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20458     
20459     
20460     container_method : 'getPopoverHeader' 
20461     
20462      
20463     
20464     
20465    
20466 });
20467
20468  
20469
20470  /*
20471  * - LGPL
20472  *
20473  * Progress
20474  * 
20475  */
20476
20477 /**
20478  * @class Roo.bootstrap.Progress
20479  * @extends Roo.bootstrap.Component
20480  * Bootstrap Progress class
20481  * @cfg {Boolean} striped striped of the progress bar
20482  * @cfg {Boolean} active animated of the progress bar
20483  * 
20484  * 
20485  * @constructor
20486  * Create a new Progress
20487  * @param {Object} config The config object
20488  */
20489
20490 Roo.bootstrap.Progress = function(config){
20491     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20492 };
20493
20494 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20495     
20496     striped : false,
20497     active: false,
20498     
20499     getAutoCreate : function(){
20500         var cfg = {
20501             tag: 'div',
20502             cls: 'progress'
20503         };
20504         
20505         
20506         if(this.striped){
20507             cfg.cls += ' progress-striped';
20508         }
20509       
20510         if(this.active){
20511             cfg.cls += ' active';
20512         }
20513         
20514         
20515         return cfg;
20516     }
20517    
20518 });
20519
20520  
20521
20522  /*
20523  * - LGPL
20524  *
20525  * ProgressBar
20526  * 
20527  */
20528
20529 /**
20530  * @class Roo.bootstrap.ProgressBar
20531  * @extends Roo.bootstrap.Component
20532  * Bootstrap ProgressBar class
20533  * @cfg {Number} aria_valuenow aria-value now
20534  * @cfg {Number} aria_valuemin aria-value min
20535  * @cfg {Number} aria_valuemax aria-value max
20536  * @cfg {String} label label for the progress bar
20537  * @cfg {String} panel (success | info | warning | danger )
20538  * @cfg {String} role role of the progress bar
20539  * @cfg {String} sr_only text
20540  * 
20541  * 
20542  * @constructor
20543  * Create a new ProgressBar
20544  * @param {Object} config The config object
20545  */
20546
20547 Roo.bootstrap.ProgressBar = function(config){
20548     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20549 };
20550
20551 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20552     
20553     aria_valuenow : 0,
20554     aria_valuemin : 0,
20555     aria_valuemax : 100,
20556     label : false,
20557     panel : false,
20558     role : false,
20559     sr_only: false,
20560     
20561     getAutoCreate : function()
20562     {
20563         
20564         var cfg = {
20565             tag: 'div',
20566             cls: 'progress-bar',
20567             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20568         };
20569         
20570         if(this.sr_only){
20571             cfg.cn = {
20572                 tag: 'span',
20573                 cls: 'sr-only',
20574                 html: this.sr_only
20575             }
20576         }
20577         
20578         if(this.role){
20579             cfg.role = this.role;
20580         }
20581         
20582         if(this.aria_valuenow){
20583             cfg['aria-valuenow'] = this.aria_valuenow;
20584         }
20585         
20586         if(this.aria_valuemin){
20587             cfg['aria-valuemin'] = this.aria_valuemin;
20588         }
20589         
20590         if(this.aria_valuemax){
20591             cfg['aria-valuemax'] = this.aria_valuemax;
20592         }
20593         
20594         if(this.label && !this.sr_only){
20595             cfg.html = this.label;
20596         }
20597         
20598         if(this.panel){
20599             cfg.cls += ' progress-bar-' + this.panel;
20600         }
20601         
20602         return cfg;
20603     },
20604     
20605     update : function(aria_valuenow)
20606     {
20607         this.aria_valuenow = aria_valuenow;
20608         
20609         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20610     }
20611    
20612 });
20613
20614  
20615
20616  /*
20617  * - LGPL
20618  *
20619  * column
20620  * 
20621  */
20622
20623 /**
20624  * @class Roo.bootstrap.TabGroup
20625  * @extends Roo.bootstrap.Column
20626  * Bootstrap Column class
20627  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20628  * @cfg {Boolean} carousel true to make the group behave like a carousel
20629  * @cfg {Boolean} bullets show bullets for the panels
20630  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20631  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20632  * @cfg {Boolean} showarrow (true|false) show arrow default true
20633  * 
20634  * @constructor
20635  * Create a new TabGroup
20636  * @param {Object} config The config object
20637  */
20638
20639 Roo.bootstrap.TabGroup = function(config){
20640     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20641     if (!this.navId) {
20642         this.navId = Roo.id();
20643     }
20644     this.tabs = [];
20645     Roo.bootstrap.TabGroup.register(this);
20646     
20647 };
20648
20649 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20650     
20651     carousel : false,
20652     transition : false,
20653     bullets : 0,
20654     timer : 0,
20655     autoslide : false,
20656     slideFn : false,
20657     slideOnTouch : false,
20658     showarrow : true,
20659     
20660     getAutoCreate : function()
20661     {
20662         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20663         
20664         cfg.cls += ' tab-content';
20665         
20666         if (this.carousel) {
20667             cfg.cls += ' carousel slide';
20668             
20669             cfg.cn = [{
20670                cls : 'carousel-inner',
20671                cn : []
20672             }];
20673         
20674             if(this.bullets  && !Roo.isTouch){
20675                 
20676                 var bullets = {
20677                     cls : 'carousel-bullets',
20678                     cn : []
20679                 };
20680                
20681                 if(this.bullets_cls){
20682                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20683                 }
20684                 
20685                 bullets.cn.push({
20686                     cls : 'clear'
20687                 });
20688                 
20689                 cfg.cn[0].cn.push(bullets);
20690             }
20691             
20692             if(this.showarrow){
20693                 cfg.cn[0].cn.push({
20694                     tag : 'div',
20695                     class : 'carousel-arrow',
20696                     cn : [
20697                         {
20698                             tag : 'div',
20699                             class : 'carousel-prev',
20700                             cn : [
20701                                 {
20702                                     tag : 'i',
20703                                     class : 'fa fa-chevron-left'
20704                                 }
20705                             ]
20706                         },
20707                         {
20708                             tag : 'div',
20709                             class : 'carousel-next',
20710                             cn : [
20711                                 {
20712                                     tag : 'i',
20713                                     class : 'fa fa-chevron-right'
20714                                 }
20715                             ]
20716                         }
20717                     ]
20718                 });
20719             }
20720             
20721         }
20722         
20723         return cfg;
20724     },
20725     
20726     initEvents:  function()
20727     {
20728 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20729 //            this.el.on("touchstart", this.onTouchStart, this);
20730 //        }
20731         
20732         if(this.autoslide){
20733             var _this = this;
20734             
20735             this.slideFn = window.setInterval(function() {
20736                 _this.showPanelNext();
20737             }, this.timer);
20738         }
20739         
20740         if(this.showarrow){
20741             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20742             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20743         }
20744         
20745         
20746     },
20747     
20748 //    onTouchStart : function(e, el, o)
20749 //    {
20750 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20751 //            return;
20752 //        }
20753 //        
20754 //        this.showPanelNext();
20755 //    },
20756     
20757     
20758     getChildContainer : function()
20759     {
20760         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20761     },
20762     
20763     /**
20764     * register a Navigation item
20765     * @param {Roo.bootstrap.NavItem} the navitem to add
20766     */
20767     register : function(item)
20768     {
20769         this.tabs.push( item);
20770         item.navId = this.navId; // not really needed..
20771         this.addBullet();
20772     
20773     },
20774     
20775     getActivePanel : function()
20776     {
20777         var r = false;
20778         Roo.each(this.tabs, function(t) {
20779             if (t.active) {
20780                 r = t;
20781                 return false;
20782             }
20783             return null;
20784         });
20785         return r;
20786         
20787     },
20788     getPanelByName : function(n)
20789     {
20790         var r = false;
20791         Roo.each(this.tabs, function(t) {
20792             if (t.tabId == n) {
20793                 r = t;
20794                 return false;
20795             }
20796             return null;
20797         });
20798         return r;
20799     },
20800     indexOfPanel : function(p)
20801     {
20802         var r = false;
20803         Roo.each(this.tabs, function(t,i) {
20804             if (t.tabId == p.tabId) {
20805                 r = i;
20806                 return false;
20807             }
20808             return null;
20809         });
20810         return r;
20811     },
20812     /**
20813      * show a specific panel
20814      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20815      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20816      */
20817     showPanel : function (pan)
20818     {
20819         if(this.transition || typeof(pan) == 'undefined'){
20820             Roo.log("waiting for the transitionend");
20821             return false;
20822         }
20823         
20824         if (typeof(pan) == 'number') {
20825             pan = this.tabs[pan];
20826         }
20827         
20828         if (typeof(pan) == 'string') {
20829             pan = this.getPanelByName(pan);
20830         }
20831         
20832         var cur = this.getActivePanel();
20833         
20834         if(!pan || !cur){
20835             Roo.log('pan or acitve pan is undefined');
20836             return false;
20837         }
20838         
20839         if (pan.tabId == this.getActivePanel().tabId) {
20840             return true;
20841         }
20842         
20843         if (false === cur.fireEvent('beforedeactivate')) {
20844             return false;
20845         }
20846         
20847         if(this.bullets > 0 && !Roo.isTouch){
20848             this.setActiveBullet(this.indexOfPanel(pan));
20849         }
20850         
20851         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20852             
20853             //class="carousel-item carousel-item-next carousel-item-left"
20854             
20855             this.transition = true;
20856             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20857             var lr = dir == 'next' ? 'left' : 'right';
20858             pan.el.addClass(dir); // or prev
20859             pan.el.addClass('carousel-item-' + dir); // or prev
20860             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20861             cur.el.addClass(lr); // or right
20862             pan.el.addClass(lr);
20863             cur.el.addClass('carousel-item-' +lr); // or right
20864             pan.el.addClass('carousel-item-' +lr);
20865             
20866             
20867             var _this = this;
20868             cur.el.on('transitionend', function() {
20869                 Roo.log("trans end?");
20870                 
20871                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20872                 pan.setActive(true);
20873                 
20874                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20875                 cur.setActive(false);
20876                 
20877                 _this.transition = false;
20878                 
20879             }, this, { single:  true } );
20880             
20881             return true;
20882         }
20883         
20884         cur.setActive(false);
20885         pan.setActive(true);
20886         
20887         return true;
20888         
20889     },
20890     showPanelNext : function()
20891     {
20892         var i = this.indexOfPanel(this.getActivePanel());
20893         
20894         if (i >= this.tabs.length - 1 && !this.autoslide) {
20895             return;
20896         }
20897         
20898         if (i >= this.tabs.length - 1 && this.autoslide) {
20899             i = -1;
20900         }
20901         
20902         this.showPanel(this.tabs[i+1]);
20903     },
20904     
20905     showPanelPrev : function()
20906     {
20907         var i = this.indexOfPanel(this.getActivePanel());
20908         
20909         if (i  < 1 && !this.autoslide) {
20910             return;
20911         }
20912         
20913         if (i < 1 && this.autoslide) {
20914             i = this.tabs.length;
20915         }
20916         
20917         this.showPanel(this.tabs[i-1]);
20918     },
20919     
20920     
20921     addBullet: function()
20922     {
20923         if(!this.bullets || Roo.isTouch){
20924             return;
20925         }
20926         var ctr = this.el.select('.carousel-bullets',true).first();
20927         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20928         var bullet = ctr.createChild({
20929             cls : 'bullet bullet-' + i
20930         },ctr.dom.lastChild);
20931         
20932         
20933         var _this = this;
20934         
20935         bullet.on('click', (function(e, el, o, ii, t){
20936
20937             e.preventDefault();
20938
20939             this.showPanel(ii);
20940
20941             if(this.autoslide && this.slideFn){
20942                 clearInterval(this.slideFn);
20943                 this.slideFn = window.setInterval(function() {
20944                     _this.showPanelNext();
20945                 }, this.timer);
20946             }
20947
20948         }).createDelegate(this, [i, bullet], true));
20949                 
20950         
20951     },
20952      
20953     setActiveBullet : function(i)
20954     {
20955         if(Roo.isTouch){
20956             return;
20957         }
20958         
20959         Roo.each(this.el.select('.bullet', true).elements, function(el){
20960             el.removeClass('selected');
20961         });
20962
20963         var bullet = this.el.select('.bullet-' + i, true).first();
20964         
20965         if(!bullet){
20966             return;
20967         }
20968         
20969         bullet.addClass('selected');
20970     }
20971     
20972     
20973   
20974 });
20975
20976  
20977
20978  
20979  
20980 Roo.apply(Roo.bootstrap.TabGroup, {
20981     
20982     groups: {},
20983      /**
20984     * register a Navigation Group
20985     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20986     */
20987     register : function(navgrp)
20988     {
20989         this.groups[navgrp.navId] = navgrp;
20990         
20991     },
20992     /**
20993     * fetch a Navigation Group based on the navigation ID
20994     * if one does not exist , it will get created.
20995     * @param {string} the navgroup to add
20996     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20997     */
20998     get: function(navId) {
20999         if (typeof(this.groups[navId]) == 'undefined') {
21000             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21001         }
21002         return this.groups[navId] ;
21003     }
21004     
21005     
21006     
21007 });
21008
21009  /*
21010  * - LGPL
21011  *
21012  * TabPanel
21013  * 
21014  */
21015
21016 /**
21017  * @class Roo.bootstrap.TabPanel
21018  * @extends Roo.bootstrap.Component
21019  * Bootstrap TabPanel class
21020  * @cfg {Boolean} active panel active
21021  * @cfg {String} html panel content
21022  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21023  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21024  * @cfg {String} href click to link..
21025  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21026  * 
21027  * 
21028  * @constructor
21029  * Create a new TabPanel
21030  * @param {Object} config The config object
21031  */
21032
21033 Roo.bootstrap.TabPanel = function(config){
21034     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21035     this.addEvents({
21036         /**
21037              * @event changed
21038              * Fires when the active status changes
21039              * @param {Roo.bootstrap.TabPanel} this
21040              * @param {Boolean} state the new state
21041             
21042          */
21043         'changed': true,
21044         /**
21045              * @event beforedeactivate
21046              * Fires before a tab is de-activated - can be used to do validation on a form.
21047              * @param {Roo.bootstrap.TabPanel} this
21048              * @return {Boolean} false if there is an error
21049             
21050          */
21051         'beforedeactivate': true
21052      });
21053     
21054     this.tabId = this.tabId || Roo.id();
21055   
21056 };
21057
21058 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21059     
21060     active: false,
21061     html: false,
21062     tabId: false,
21063     navId : false,
21064     href : '',
21065     touchSlide : false,
21066     getAutoCreate : function(){
21067         
21068         
21069         var cfg = {
21070             tag: 'div',
21071             // item is needed for carousel - not sure if it has any effect otherwise
21072             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21073             html: this.html || ''
21074         };
21075         
21076         if(this.active){
21077             cfg.cls += ' active';
21078         }
21079         
21080         if(this.tabId){
21081             cfg.tabId = this.tabId;
21082         }
21083         
21084         
21085         
21086         return cfg;
21087     },
21088     
21089     initEvents:  function()
21090     {
21091         var p = this.parent();
21092         
21093         this.navId = this.navId || p.navId;
21094         
21095         if (typeof(this.navId) != 'undefined') {
21096             // not really needed.. but just in case.. parent should be a NavGroup.
21097             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21098             
21099             tg.register(this);
21100             
21101             var i = tg.tabs.length - 1;
21102             
21103             if(this.active && tg.bullets > 0 && i < tg.bullets){
21104                 tg.setActiveBullet(i);
21105             }
21106         }
21107         
21108         this.el.on('click', this.onClick, this);
21109         
21110         if(Roo.isTouch && this.touchSlide){
21111             this.el.on("touchstart", this.onTouchStart, this);
21112             this.el.on("touchmove", this.onTouchMove, this);
21113             this.el.on("touchend", this.onTouchEnd, this);
21114         }
21115         
21116     },
21117     
21118     onRender : function(ct, position)
21119     {
21120         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21121     },
21122     
21123     setActive : function(state)
21124     {
21125         Roo.log("panel - set active " + this.tabId + "=" + state);
21126         
21127         this.active = state;
21128         if (!state) {
21129             this.el.removeClass('active');
21130             
21131         } else  if (!this.el.hasClass('active')) {
21132             this.el.addClass('active');
21133         }
21134         
21135         this.fireEvent('changed', this, state);
21136     },
21137     
21138     onClick : function(e)
21139     {
21140         e.preventDefault();
21141         
21142         if(!this.href.length){
21143             return;
21144         }
21145         
21146         window.location.href = this.href;
21147     },
21148     
21149     startX : 0,
21150     startY : 0,
21151     endX : 0,
21152     endY : 0,
21153     swiping : false,
21154     
21155     onTouchStart : function(e)
21156     {
21157         this.swiping = false;
21158         
21159         this.startX = e.browserEvent.touches[0].clientX;
21160         this.startY = e.browserEvent.touches[0].clientY;
21161     },
21162     
21163     onTouchMove : function(e)
21164     {
21165         this.swiping = true;
21166         
21167         this.endX = e.browserEvent.touches[0].clientX;
21168         this.endY = e.browserEvent.touches[0].clientY;
21169     },
21170     
21171     onTouchEnd : function(e)
21172     {
21173         if(!this.swiping){
21174             this.onClick(e);
21175             return;
21176         }
21177         
21178         var tabGroup = this.parent();
21179         
21180         if(this.endX > this.startX){ // swiping right
21181             tabGroup.showPanelPrev();
21182             return;
21183         }
21184         
21185         if(this.startX > this.endX){ // swiping left
21186             tabGroup.showPanelNext();
21187             return;
21188         }
21189     }
21190     
21191     
21192 });
21193  
21194
21195  
21196
21197  /*
21198  * - LGPL
21199  *
21200  * DateField
21201  * 
21202  */
21203
21204 /**
21205  * @class Roo.bootstrap.DateField
21206  * @extends Roo.bootstrap.Input
21207  * Bootstrap DateField class
21208  * @cfg {Number} weekStart default 0
21209  * @cfg {String} viewMode default empty, (months|years)
21210  * @cfg {String} minViewMode default empty, (months|years)
21211  * @cfg {Number} startDate default -Infinity
21212  * @cfg {Number} endDate default Infinity
21213  * @cfg {Boolean} todayHighlight default false
21214  * @cfg {Boolean} todayBtn default false
21215  * @cfg {Boolean} calendarWeeks default false
21216  * @cfg {Object} daysOfWeekDisabled default empty
21217  * @cfg {Boolean} singleMode default false (true | false)
21218  * 
21219  * @cfg {Boolean} keyboardNavigation default true
21220  * @cfg {String} language default en
21221  * 
21222  * @constructor
21223  * Create a new DateField
21224  * @param {Object} config The config object
21225  */
21226
21227 Roo.bootstrap.DateField = function(config){
21228     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21229      this.addEvents({
21230             /**
21231              * @event show
21232              * Fires when this field show.
21233              * @param {Roo.bootstrap.DateField} this
21234              * @param {Mixed} date The date value
21235              */
21236             show : true,
21237             /**
21238              * @event show
21239              * Fires when this field hide.
21240              * @param {Roo.bootstrap.DateField} this
21241              * @param {Mixed} date The date value
21242              */
21243             hide : true,
21244             /**
21245              * @event select
21246              * Fires when select a date.
21247              * @param {Roo.bootstrap.DateField} this
21248              * @param {Mixed} date The date value
21249              */
21250             select : true,
21251             /**
21252              * @event beforeselect
21253              * Fires when before select a date.
21254              * @param {Roo.bootstrap.DateField} this
21255              * @param {Mixed} date The date value
21256              */
21257             beforeselect : true
21258         });
21259 };
21260
21261 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21262     
21263     /**
21264      * @cfg {String} format
21265      * The default date format string which can be overriden for localization support.  The format must be
21266      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21267      */
21268     format : "m/d/y",
21269     /**
21270      * @cfg {String} altFormats
21271      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21272      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21273      */
21274     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21275     
21276     weekStart : 0,
21277     
21278     viewMode : '',
21279     
21280     minViewMode : '',
21281     
21282     todayHighlight : false,
21283     
21284     todayBtn: false,
21285     
21286     language: 'en',
21287     
21288     keyboardNavigation: true,
21289     
21290     calendarWeeks: false,
21291     
21292     startDate: -Infinity,
21293     
21294     endDate: Infinity,
21295     
21296     daysOfWeekDisabled: [],
21297     
21298     _events: [],
21299     
21300     singleMode : false,
21301     
21302     UTCDate: function()
21303     {
21304         return new Date(Date.UTC.apply(Date, arguments));
21305     },
21306     
21307     UTCToday: function()
21308     {
21309         var today = new Date();
21310         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21311     },
21312     
21313     getDate: function() {
21314             var d = this.getUTCDate();
21315             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21316     },
21317     
21318     getUTCDate: function() {
21319             return this.date;
21320     },
21321     
21322     setDate: function(d) {
21323             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21324     },
21325     
21326     setUTCDate: function(d) {
21327             this.date = d;
21328             this.setValue(this.formatDate(this.date));
21329     },
21330         
21331     onRender: function(ct, position)
21332     {
21333         
21334         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21335         
21336         this.language = this.language || 'en';
21337         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21338         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21339         
21340         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21341         this.format = this.format || 'm/d/y';
21342         this.isInline = false;
21343         this.isInput = true;
21344         this.component = this.el.select('.add-on', true).first() || false;
21345         this.component = (this.component && this.component.length === 0) ? false : this.component;
21346         this.hasInput = this.component && this.inputEl().length;
21347         
21348         if (typeof(this.minViewMode === 'string')) {
21349             switch (this.minViewMode) {
21350                 case 'months':
21351                     this.minViewMode = 1;
21352                     break;
21353                 case 'years':
21354                     this.minViewMode = 2;
21355                     break;
21356                 default:
21357                     this.minViewMode = 0;
21358                     break;
21359             }
21360         }
21361         
21362         if (typeof(this.viewMode === 'string')) {
21363             switch (this.viewMode) {
21364                 case 'months':
21365                     this.viewMode = 1;
21366                     break;
21367                 case 'years':
21368                     this.viewMode = 2;
21369                     break;
21370                 default:
21371                     this.viewMode = 0;
21372                     break;
21373             }
21374         }
21375                 
21376         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21377         
21378 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21379         
21380         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21381         
21382         this.picker().on('mousedown', this.onMousedown, this);
21383         this.picker().on('click', this.onClick, this);
21384         
21385         this.picker().addClass('datepicker-dropdown');
21386         
21387         this.startViewMode = this.viewMode;
21388         
21389         if(this.singleMode){
21390             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21391                 v.setVisibilityMode(Roo.Element.DISPLAY);
21392                 v.hide();
21393             });
21394             
21395             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21396                 v.setStyle('width', '189px');
21397             });
21398         }
21399         
21400         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21401             if(!this.calendarWeeks){
21402                 v.remove();
21403                 return;
21404             }
21405             
21406             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21407             v.attr('colspan', function(i, val){
21408                 return parseInt(val) + 1;
21409             });
21410         });
21411                         
21412         
21413         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21414         
21415         this.setStartDate(this.startDate);
21416         this.setEndDate(this.endDate);
21417         
21418         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21419         
21420         this.fillDow();
21421         this.fillMonths();
21422         this.update();
21423         this.showMode();
21424         
21425         if(this.isInline) {
21426             this.showPopup();
21427         }
21428     },
21429     
21430     picker : function()
21431     {
21432         return this.pickerEl;
21433 //        return this.el.select('.datepicker', true).first();
21434     },
21435     
21436     fillDow: function()
21437     {
21438         var dowCnt = this.weekStart;
21439         
21440         var dow = {
21441             tag: 'tr',
21442             cn: [
21443                 
21444             ]
21445         };
21446         
21447         if(this.calendarWeeks){
21448             dow.cn.push({
21449                 tag: 'th',
21450                 cls: 'cw',
21451                 html: '&nbsp;'
21452             })
21453         }
21454         
21455         while (dowCnt < this.weekStart + 7) {
21456             dow.cn.push({
21457                 tag: 'th',
21458                 cls: 'dow',
21459                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21460             });
21461         }
21462         
21463         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21464     },
21465     
21466     fillMonths: function()
21467     {    
21468         var i = 0;
21469         var months = this.picker().select('>.datepicker-months td', true).first();
21470         
21471         months.dom.innerHTML = '';
21472         
21473         while (i < 12) {
21474             var month = {
21475                 tag: 'span',
21476                 cls: 'month',
21477                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21478             };
21479             
21480             months.createChild(month);
21481         }
21482         
21483     },
21484     
21485     update: function()
21486     {
21487         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;
21488         
21489         if (this.date < this.startDate) {
21490             this.viewDate = new Date(this.startDate);
21491         } else if (this.date > this.endDate) {
21492             this.viewDate = new Date(this.endDate);
21493         } else {
21494             this.viewDate = new Date(this.date);
21495         }
21496         
21497         this.fill();
21498     },
21499     
21500     fill: function() 
21501     {
21502         var d = new Date(this.viewDate),
21503                 year = d.getUTCFullYear(),
21504                 month = d.getUTCMonth(),
21505                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21506                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21507                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21508                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21509                 currentDate = this.date && this.date.valueOf(),
21510                 today = this.UTCToday();
21511         
21512         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21513         
21514 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21515         
21516 //        this.picker.select('>tfoot th.today').
21517 //                                              .text(dates[this.language].today)
21518 //                                              .toggle(this.todayBtn !== false);
21519     
21520         this.updateNavArrows();
21521         this.fillMonths();
21522                                                 
21523         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21524         
21525         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21526          
21527         prevMonth.setUTCDate(day);
21528         
21529         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21530         
21531         var nextMonth = new Date(prevMonth);
21532         
21533         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21534         
21535         nextMonth = nextMonth.valueOf();
21536         
21537         var fillMonths = false;
21538         
21539         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21540         
21541         while(prevMonth.valueOf() <= nextMonth) {
21542             var clsName = '';
21543             
21544             if (prevMonth.getUTCDay() === this.weekStart) {
21545                 if(fillMonths){
21546                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21547                 }
21548                     
21549                 fillMonths = {
21550                     tag: 'tr',
21551                     cn: []
21552                 };
21553                 
21554                 if(this.calendarWeeks){
21555                     // ISO 8601: First week contains first thursday.
21556                     // ISO also states week starts on Monday, but we can be more abstract here.
21557                     var
21558                     // Start of current week: based on weekstart/current date
21559                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21560                     // Thursday of this week
21561                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21562                     // First Thursday of year, year from thursday
21563                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21564                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21565                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21566                     
21567                     fillMonths.cn.push({
21568                         tag: 'td',
21569                         cls: 'cw',
21570                         html: calWeek
21571                     });
21572                 }
21573             }
21574             
21575             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21576                 clsName += ' old';
21577             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21578                 clsName += ' new';
21579             }
21580             if (this.todayHighlight &&
21581                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21582                 prevMonth.getUTCMonth() == today.getMonth() &&
21583                 prevMonth.getUTCDate() == today.getDate()) {
21584                 clsName += ' today';
21585             }
21586             
21587             if (currentDate && prevMonth.valueOf() === currentDate) {
21588                 clsName += ' active';
21589             }
21590             
21591             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21592                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21593                     clsName += ' disabled';
21594             }
21595             
21596             fillMonths.cn.push({
21597                 tag: 'td',
21598                 cls: 'day ' + clsName,
21599                 html: prevMonth.getDate()
21600             });
21601             
21602             prevMonth.setDate(prevMonth.getDate()+1);
21603         }
21604           
21605         var currentYear = this.date && this.date.getUTCFullYear();
21606         var currentMonth = this.date && this.date.getUTCMonth();
21607         
21608         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21609         
21610         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21611             v.removeClass('active');
21612             
21613             if(currentYear === year && k === currentMonth){
21614                 v.addClass('active');
21615             }
21616             
21617             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21618                 v.addClass('disabled');
21619             }
21620             
21621         });
21622         
21623         
21624         year = parseInt(year/10, 10) * 10;
21625         
21626         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21627         
21628         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21629         
21630         year -= 1;
21631         for (var i = -1; i < 11; i++) {
21632             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21633                 tag: 'span',
21634                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21635                 html: year
21636             });
21637             
21638             year += 1;
21639         }
21640     },
21641     
21642     showMode: function(dir) 
21643     {
21644         if (dir) {
21645             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21646         }
21647         
21648         Roo.each(this.picker().select('>div',true).elements, function(v){
21649             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21650             v.hide();
21651         });
21652         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21653     },
21654     
21655     place: function()
21656     {
21657         if(this.isInline) {
21658             return;
21659         }
21660         
21661         this.picker().removeClass(['bottom', 'top']);
21662         
21663         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21664             /*
21665              * place to the top of element!
21666              *
21667              */
21668             
21669             this.picker().addClass('top');
21670             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21671             
21672             return;
21673         }
21674         
21675         this.picker().addClass('bottom');
21676         
21677         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21678     },
21679     
21680     parseDate : function(value)
21681     {
21682         if(!value || value instanceof Date){
21683             return value;
21684         }
21685         var v = Date.parseDate(value, this.format);
21686         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21687             v = Date.parseDate(value, 'Y-m-d');
21688         }
21689         if(!v && this.altFormats){
21690             if(!this.altFormatsArray){
21691                 this.altFormatsArray = this.altFormats.split("|");
21692             }
21693             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21694                 v = Date.parseDate(value, this.altFormatsArray[i]);
21695             }
21696         }
21697         return v;
21698     },
21699     
21700     formatDate : function(date, fmt)
21701     {   
21702         return (!date || !(date instanceof Date)) ?
21703         date : date.dateFormat(fmt || this.format);
21704     },
21705     
21706     onFocus : function()
21707     {
21708         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21709         this.showPopup();
21710     },
21711     
21712     onBlur : function()
21713     {
21714         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21715         
21716         var d = this.inputEl().getValue();
21717         
21718         this.setValue(d);
21719                 
21720         this.hidePopup();
21721     },
21722     
21723     showPopup : function()
21724     {
21725         this.picker().show();
21726         this.update();
21727         this.place();
21728         
21729         this.fireEvent('showpopup', this, this.date);
21730     },
21731     
21732     hidePopup : function()
21733     {
21734         if(this.isInline) {
21735             return;
21736         }
21737         this.picker().hide();
21738         this.viewMode = this.startViewMode;
21739         this.showMode();
21740         
21741         this.fireEvent('hidepopup', this, this.date);
21742         
21743     },
21744     
21745     onMousedown: function(e)
21746     {
21747         e.stopPropagation();
21748         e.preventDefault();
21749     },
21750     
21751     keyup: function(e)
21752     {
21753         Roo.bootstrap.DateField.superclass.keyup.call(this);
21754         this.update();
21755     },
21756
21757     setValue: function(v)
21758     {
21759         if(this.fireEvent('beforeselect', this, v) !== false){
21760             var d = new Date(this.parseDate(v) ).clearTime();
21761         
21762             if(isNaN(d.getTime())){
21763                 this.date = this.viewDate = '';
21764                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21765                 return;
21766             }
21767
21768             v = this.formatDate(d);
21769
21770             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21771
21772             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21773
21774             this.update();
21775
21776             this.fireEvent('select', this, this.date);
21777         }
21778     },
21779     
21780     getValue: function()
21781     {
21782         return this.formatDate(this.date);
21783     },
21784     
21785     fireKey: function(e)
21786     {
21787         if (!this.picker().isVisible()){
21788             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21789                 this.showPopup();
21790             }
21791             return;
21792         }
21793         
21794         var dateChanged = false,
21795         dir, day, month,
21796         newDate, newViewDate;
21797         
21798         switch(e.keyCode){
21799             case 27: // escape
21800                 this.hidePopup();
21801                 e.preventDefault();
21802                 break;
21803             case 37: // left
21804             case 39: // right
21805                 if (!this.keyboardNavigation) {
21806                     break;
21807                 }
21808                 dir = e.keyCode == 37 ? -1 : 1;
21809                 
21810                 if (e.ctrlKey){
21811                     newDate = this.moveYear(this.date, dir);
21812                     newViewDate = this.moveYear(this.viewDate, dir);
21813                 } else if (e.shiftKey){
21814                     newDate = this.moveMonth(this.date, dir);
21815                     newViewDate = this.moveMonth(this.viewDate, dir);
21816                 } else {
21817                     newDate = new Date(this.date);
21818                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21819                     newViewDate = new Date(this.viewDate);
21820                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21821                 }
21822                 if (this.dateWithinRange(newDate)){
21823                     this.date = newDate;
21824                     this.viewDate = newViewDate;
21825                     this.setValue(this.formatDate(this.date));
21826 //                    this.update();
21827                     e.preventDefault();
21828                     dateChanged = true;
21829                 }
21830                 break;
21831             case 38: // up
21832             case 40: // down
21833                 if (!this.keyboardNavigation) {
21834                     break;
21835                 }
21836                 dir = e.keyCode == 38 ? -1 : 1;
21837                 if (e.ctrlKey){
21838                     newDate = this.moveYear(this.date, dir);
21839                     newViewDate = this.moveYear(this.viewDate, dir);
21840                 } else if (e.shiftKey){
21841                     newDate = this.moveMonth(this.date, dir);
21842                     newViewDate = this.moveMonth(this.viewDate, dir);
21843                 } else {
21844                     newDate = new Date(this.date);
21845                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21846                     newViewDate = new Date(this.viewDate);
21847                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21848                 }
21849                 if (this.dateWithinRange(newDate)){
21850                     this.date = newDate;
21851                     this.viewDate = newViewDate;
21852                     this.setValue(this.formatDate(this.date));
21853 //                    this.update();
21854                     e.preventDefault();
21855                     dateChanged = true;
21856                 }
21857                 break;
21858             case 13: // enter
21859                 this.setValue(this.formatDate(this.date));
21860                 this.hidePopup();
21861                 e.preventDefault();
21862                 break;
21863             case 9: // tab
21864                 this.setValue(this.formatDate(this.date));
21865                 this.hidePopup();
21866                 break;
21867             case 16: // shift
21868             case 17: // ctrl
21869             case 18: // alt
21870                 break;
21871             default :
21872                 this.hidePopup();
21873                 
21874         }
21875     },
21876     
21877     
21878     onClick: function(e) 
21879     {
21880         e.stopPropagation();
21881         e.preventDefault();
21882         
21883         var target = e.getTarget();
21884         
21885         if(target.nodeName.toLowerCase() === 'i'){
21886             target = Roo.get(target).dom.parentNode;
21887         }
21888         
21889         var nodeName = target.nodeName;
21890         var className = target.className;
21891         var html = target.innerHTML;
21892         //Roo.log(nodeName);
21893         
21894         switch(nodeName.toLowerCase()) {
21895             case 'th':
21896                 switch(className) {
21897                     case 'switch':
21898                         this.showMode(1);
21899                         break;
21900                     case 'prev':
21901                     case 'next':
21902                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21903                         switch(this.viewMode){
21904                                 case 0:
21905                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21906                                         break;
21907                                 case 1:
21908                                 case 2:
21909                                         this.viewDate = this.moveYear(this.viewDate, dir);
21910                                         break;
21911                         }
21912                         this.fill();
21913                         break;
21914                     case 'today':
21915                         var date = new Date();
21916                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21917 //                        this.fill()
21918                         this.setValue(this.formatDate(this.date));
21919                         
21920                         this.hidePopup();
21921                         break;
21922                 }
21923                 break;
21924             case 'span':
21925                 if (className.indexOf('disabled') < 0) {
21926                     this.viewDate.setUTCDate(1);
21927                     if (className.indexOf('month') > -1) {
21928                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21929                     } else {
21930                         var year = parseInt(html, 10) || 0;
21931                         this.viewDate.setUTCFullYear(year);
21932                         
21933                     }
21934                     
21935                     if(this.singleMode){
21936                         this.setValue(this.formatDate(this.viewDate));
21937                         this.hidePopup();
21938                         return;
21939                     }
21940                     
21941                     this.showMode(-1);
21942                     this.fill();
21943                 }
21944                 break;
21945                 
21946             case 'td':
21947                 //Roo.log(className);
21948                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21949                     var day = parseInt(html, 10) || 1;
21950                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21951                         month = (this.viewDate || new Date()).getUTCMonth();
21952
21953                     if (className.indexOf('old') > -1) {
21954                         if(month === 0 ){
21955                             month = 11;
21956                             year -= 1;
21957                         }else{
21958                             month -= 1;
21959                         }
21960                     } else if (className.indexOf('new') > -1) {
21961                         if (month == 11) {
21962                             month = 0;
21963                             year += 1;
21964                         } else {
21965                             month += 1;
21966                         }
21967                     }
21968                     //Roo.log([year,month,day]);
21969                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21970                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21971 //                    this.fill();
21972                     //Roo.log(this.formatDate(this.date));
21973                     this.setValue(this.formatDate(this.date));
21974                     this.hidePopup();
21975                 }
21976                 break;
21977         }
21978     },
21979     
21980     setStartDate: function(startDate)
21981     {
21982         this.startDate = startDate || -Infinity;
21983         if (this.startDate !== -Infinity) {
21984             this.startDate = this.parseDate(this.startDate);
21985         }
21986         this.update();
21987         this.updateNavArrows();
21988     },
21989
21990     setEndDate: function(endDate)
21991     {
21992         this.endDate = endDate || Infinity;
21993         if (this.endDate !== Infinity) {
21994             this.endDate = this.parseDate(this.endDate);
21995         }
21996         this.update();
21997         this.updateNavArrows();
21998     },
21999     
22000     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22001     {
22002         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22003         if (typeof(this.daysOfWeekDisabled) !== 'object') {
22004             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22005         }
22006         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22007             return parseInt(d, 10);
22008         });
22009         this.update();
22010         this.updateNavArrows();
22011     },
22012     
22013     updateNavArrows: function() 
22014     {
22015         if(this.singleMode){
22016             return;
22017         }
22018         
22019         var d = new Date(this.viewDate),
22020         year = d.getUTCFullYear(),
22021         month = d.getUTCMonth();
22022         
22023         Roo.each(this.picker().select('.prev', true).elements, function(v){
22024             v.show();
22025             switch (this.viewMode) {
22026                 case 0:
22027
22028                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22029                         v.hide();
22030                     }
22031                     break;
22032                 case 1:
22033                 case 2:
22034                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22035                         v.hide();
22036                     }
22037                     break;
22038             }
22039         });
22040         
22041         Roo.each(this.picker().select('.next', true).elements, function(v){
22042             v.show();
22043             switch (this.viewMode) {
22044                 case 0:
22045
22046                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22047                         v.hide();
22048                     }
22049                     break;
22050                 case 1:
22051                 case 2:
22052                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22053                         v.hide();
22054                     }
22055                     break;
22056             }
22057         })
22058     },
22059     
22060     moveMonth: function(date, dir)
22061     {
22062         if (!dir) {
22063             return date;
22064         }
22065         var new_date = new Date(date.valueOf()),
22066         day = new_date.getUTCDate(),
22067         month = new_date.getUTCMonth(),
22068         mag = Math.abs(dir),
22069         new_month, test;
22070         dir = dir > 0 ? 1 : -1;
22071         if (mag == 1){
22072             test = dir == -1
22073             // If going back one month, make sure month is not current month
22074             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22075             ? function(){
22076                 return new_date.getUTCMonth() == month;
22077             }
22078             // If going forward one month, make sure month is as expected
22079             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22080             : function(){
22081                 return new_date.getUTCMonth() != new_month;
22082             };
22083             new_month = month + dir;
22084             new_date.setUTCMonth(new_month);
22085             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22086             if (new_month < 0 || new_month > 11) {
22087                 new_month = (new_month + 12) % 12;
22088             }
22089         } else {
22090             // For magnitudes >1, move one month at a time...
22091             for (var i=0; i<mag; i++) {
22092                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22093                 new_date = this.moveMonth(new_date, dir);
22094             }
22095             // ...then reset the day, keeping it in the new month
22096             new_month = new_date.getUTCMonth();
22097             new_date.setUTCDate(day);
22098             test = function(){
22099                 return new_month != new_date.getUTCMonth();
22100             };
22101         }
22102         // Common date-resetting loop -- if date is beyond end of month, make it
22103         // end of month
22104         while (test()){
22105             new_date.setUTCDate(--day);
22106             new_date.setUTCMonth(new_month);
22107         }
22108         return new_date;
22109     },
22110
22111     moveYear: function(date, dir)
22112     {
22113         return this.moveMonth(date, dir*12);
22114     },
22115
22116     dateWithinRange: function(date)
22117     {
22118         return date >= this.startDate && date <= this.endDate;
22119     },
22120
22121     
22122     remove: function() 
22123     {
22124         this.picker().remove();
22125     },
22126     
22127     validateValue : function(value)
22128     {
22129         if(this.getVisibilityEl().hasClass('hidden')){
22130             return true;
22131         }
22132         
22133         if(value.length < 1)  {
22134             if(this.allowBlank){
22135                 return true;
22136             }
22137             return false;
22138         }
22139         
22140         if(value.length < this.minLength){
22141             return false;
22142         }
22143         if(value.length > this.maxLength){
22144             return false;
22145         }
22146         if(this.vtype){
22147             var vt = Roo.form.VTypes;
22148             if(!vt[this.vtype](value, this)){
22149                 return false;
22150             }
22151         }
22152         if(typeof this.validator == "function"){
22153             var msg = this.validator(value);
22154             if(msg !== true){
22155                 return false;
22156             }
22157         }
22158         
22159         if(this.regex && !this.regex.test(value)){
22160             return false;
22161         }
22162         
22163         if(typeof(this.parseDate(value)) == 'undefined'){
22164             return false;
22165         }
22166         
22167         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22168             return false;
22169         }      
22170         
22171         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22172             return false;
22173         } 
22174         
22175         
22176         return true;
22177     },
22178     
22179     reset : function()
22180     {
22181         this.date = this.viewDate = '';
22182         
22183         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22184     }
22185    
22186 });
22187
22188 Roo.apply(Roo.bootstrap.DateField,  {
22189     
22190     head : {
22191         tag: 'thead',
22192         cn: [
22193         {
22194             tag: 'tr',
22195             cn: [
22196             {
22197                 tag: 'th',
22198                 cls: 'prev',
22199                 html: '<i class="fa fa-arrow-left"/>'
22200             },
22201             {
22202                 tag: 'th',
22203                 cls: 'switch',
22204                 colspan: '5'
22205             },
22206             {
22207                 tag: 'th',
22208                 cls: 'next',
22209                 html: '<i class="fa fa-arrow-right"/>'
22210             }
22211
22212             ]
22213         }
22214         ]
22215     },
22216     
22217     content : {
22218         tag: 'tbody',
22219         cn: [
22220         {
22221             tag: 'tr',
22222             cn: [
22223             {
22224                 tag: 'td',
22225                 colspan: '7'
22226             }
22227             ]
22228         }
22229         ]
22230     },
22231     
22232     footer : {
22233         tag: 'tfoot',
22234         cn: [
22235         {
22236             tag: 'tr',
22237             cn: [
22238             {
22239                 tag: 'th',
22240                 colspan: '7',
22241                 cls: 'today'
22242             }
22243                     
22244             ]
22245         }
22246         ]
22247     },
22248     
22249     dates:{
22250         en: {
22251             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22252             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22253             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22254             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22255             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22256             today: "Today"
22257         }
22258     },
22259     
22260     modes: [
22261     {
22262         clsName: 'days',
22263         navFnc: 'Month',
22264         navStep: 1
22265     },
22266     {
22267         clsName: 'months',
22268         navFnc: 'FullYear',
22269         navStep: 1
22270     },
22271     {
22272         clsName: 'years',
22273         navFnc: 'FullYear',
22274         navStep: 10
22275     }]
22276 });
22277
22278 Roo.apply(Roo.bootstrap.DateField,  {
22279   
22280     template : {
22281         tag: 'div',
22282         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22283         cn: [
22284         {
22285             tag: 'div',
22286             cls: 'datepicker-days',
22287             cn: [
22288             {
22289                 tag: 'table',
22290                 cls: 'table-condensed',
22291                 cn:[
22292                 Roo.bootstrap.DateField.head,
22293                 {
22294                     tag: 'tbody'
22295                 },
22296                 Roo.bootstrap.DateField.footer
22297                 ]
22298             }
22299             ]
22300         },
22301         {
22302             tag: 'div',
22303             cls: 'datepicker-months',
22304             cn: [
22305             {
22306                 tag: 'table',
22307                 cls: 'table-condensed',
22308                 cn:[
22309                 Roo.bootstrap.DateField.head,
22310                 Roo.bootstrap.DateField.content,
22311                 Roo.bootstrap.DateField.footer
22312                 ]
22313             }
22314             ]
22315         },
22316         {
22317             tag: 'div',
22318             cls: 'datepicker-years',
22319             cn: [
22320             {
22321                 tag: 'table',
22322                 cls: 'table-condensed',
22323                 cn:[
22324                 Roo.bootstrap.DateField.head,
22325                 Roo.bootstrap.DateField.content,
22326                 Roo.bootstrap.DateField.footer
22327                 ]
22328             }
22329             ]
22330         }
22331         ]
22332     }
22333 });
22334
22335  
22336
22337  /*
22338  * - LGPL
22339  *
22340  * TimeField
22341  * 
22342  */
22343
22344 /**
22345  * @class Roo.bootstrap.TimeField
22346  * @extends Roo.bootstrap.Input
22347  * Bootstrap DateField class
22348  * 
22349  * 
22350  * @constructor
22351  * Create a new TimeField
22352  * @param {Object} config The config object
22353  */
22354
22355 Roo.bootstrap.TimeField = function(config){
22356     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22357     this.addEvents({
22358             /**
22359              * @event show
22360              * Fires when this field show.
22361              * @param {Roo.bootstrap.DateField} thisthis
22362              * @param {Mixed} date The date value
22363              */
22364             show : true,
22365             /**
22366              * @event show
22367              * Fires when this field hide.
22368              * @param {Roo.bootstrap.DateField} this
22369              * @param {Mixed} date The date value
22370              */
22371             hide : true,
22372             /**
22373              * @event select
22374              * Fires when select a date.
22375              * @param {Roo.bootstrap.DateField} this
22376              * @param {Mixed} date The date value
22377              */
22378             select : true
22379         });
22380 };
22381
22382 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22383     
22384     /**
22385      * @cfg {String} format
22386      * The default time format string which can be overriden for localization support.  The format must be
22387      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22388      */
22389     format : "H:i",
22390
22391     getAutoCreate : function()
22392     {
22393         this.after = '<i class="fa far fa-clock"></i>';
22394         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22395         
22396          
22397     },
22398     onRender: function(ct, position)
22399     {
22400         
22401         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22402                 
22403         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22404         
22405         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22406         
22407         this.pop = this.picker().select('>.datepicker-time',true).first();
22408         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22409         
22410         this.picker().on('mousedown', this.onMousedown, this);
22411         this.picker().on('click', this.onClick, this);
22412         
22413         this.picker().addClass('datepicker-dropdown');
22414     
22415         this.fillTime();
22416         this.update();
22417             
22418         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22419         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22420         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22421         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22422         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22423         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22424
22425     },
22426     
22427     fireKey: function(e){
22428         if (!this.picker().isVisible()){
22429             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22430                 this.show();
22431             }
22432             return;
22433         }
22434
22435         e.preventDefault();
22436         
22437         switch(e.keyCode){
22438             case 27: // escape
22439                 this.hide();
22440                 break;
22441             case 37: // left
22442             case 39: // right
22443                 this.onTogglePeriod();
22444                 break;
22445             case 38: // up
22446                 this.onIncrementMinutes();
22447                 break;
22448             case 40: // down
22449                 this.onDecrementMinutes();
22450                 break;
22451             case 13: // enter
22452             case 9: // tab
22453                 this.setTime();
22454                 break;
22455         }
22456     },
22457     
22458     onClick: function(e) {
22459         e.stopPropagation();
22460         e.preventDefault();
22461     },
22462     
22463     picker : function()
22464     {
22465         return this.pickerEl;
22466     },
22467     
22468     fillTime: function()
22469     {    
22470         var time = this.pop.select('tbody', true).first();
22471         
22472         time.dom.innerHTML = '';
22473         
22474         time.createChild({
22475             tag: 'tr',
22476             cn: [
22477                 {
22478                     tag: 'td',
22479                     cn: [
22480                         {
22481                             tag: 'a',
22482                             href: '#',
22483                             cls: 'btn',
22484                             cn: [
22485                                 {
22486                                     tag: 'i',
22487                                     cls: 'hours-up fa fas fa-chevron-up'
22488                                 }
22489                             ]
22490                         } 
22491                     ]
22492                 },
22493                 {
22494                     tag: 'td',
22495                     cls: 'separator'
22496                 },
22497                 {
22498                     tag: 'td',
22499                     cn: [
22500                         {
22501                             tag: 'a',
22502                             href: '#',
22503                             cls: 'btn',
22504                             cn: [
22505                                 {
22506                                     tag: 'i',
22507                                     cls: 'minutes-up fa fas fa-chevron-up'
22508                                 }
22509                             ]
22510                         }
22511                     ]
22512                 },
22513                 {
22514                     tag: 'td',
22515                     cls: 'separator'
22516                 }
22517             ]
22518         });
22519         
22520         time.createChild({
22521             tag: 'tr',
22522             cn: [
22523                 {
22524                     tag: 'td',
22525                     cn: [
22526                         {
22527                             tag: 'span',
22528                             cls: 'timepicker-hour',
22529                             html: '00'
22530                         }  
22531                     ]
22532                 },
22533                 {
22534                     tag: 'td',
22535                     cls: 'separator',
22536                     html: ':'
22537                 },
22538                 {
22539                     tag: 'td',
22540                     cn: [
22541                         {
22542                             tag: 'span',
22543                             cls: 'timepicker-minute',
22544                             html: '00'
22545                         }  
22546                     ]
22547                 },
22548                 {
22549                     tag: 'td',
22550                     cls: 'separator'
22551                 },
22552                 {
22553                     tag: 'td',
22554                     cn: [
22555                         {
22556                             tag: 'button',
22557                             type: 'button',
22558                             cls: 'btn btn-primary period',
22559                             html: 'AM'
22560                             
22561                         }
22562                     ]
22563                 }
22564             ]
22565         });
22566         
22567         time.createChild({
22568             tag: 'tr',
22569             cn: [
22570                 {
22571                     tag: 'td',
22572                     cn: [
22573                         {
22574                             tag: 'a',
22575                             href: '#',
22576                             cls: 'btn',
22577                             cn: [
22578                                 {
22579                                     tag: 'span',
22580                                     cls: 'hours-down fa fas fa-chevron-down'
22581                                 }
22582                             ]
22583                         }
22584                     ]
22585                 },
22586                 {
22587                     tag: 'td',
22588                     cls: 'separator'
22589                 },
22590                 {
22591                     tag: 'td',
22592                     cn: [
22593                         {
22594                             tag: 'a',
22595                             href: '#',
22596                             cls: 'btn',
22597                             cn: [
22598                                 {
22599                                     tag: 'span',
22600                                     cls: 'minutes-down fa fas fa-chevron-down'
22601                                 }
22602                             ]
22603                         }
22604                     ]
22605                 },
22606                 {
22607                     tag: 'td',
22608                     cls: 'separator'
22609                 }
22610             ]
22611         });
22612         
22613     },
22614     
22615     update: function()
22616     {
22617         
22618         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22619         
22620         this.fill();
22621     },
22622     
22623     fill: function() 
22624     {
22625         var hours = this.time.getHours();
22626         var minutes = this.time.getMinutes();
22627         var period = 'AM';
22628         
22629         if(hours > 11){
22630             period = 'PM';
22631         }
22632         
22633         if(hours == 0){
22634             hours = 12;
22635         }
22636         
22637         
22638         if(hours > 12){
22639             hours = hours - 12;
22640         }
22641         
22642         if(hours < 10){
22643             hours = '0' + hours;
22644         }
22645         
22646         if(minutes < 10){
22647             minutes = '0' + minutes;
22648         }
22649         
22650         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22651         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22652         this.pop.select('button', true).first().dom.innerHTML = period;
22653         
22654     },
22655     
22656     place: function()
22657     {   
22658         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22659         
22660         var cls = ['bottom'];
22661         
22662         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22663             cls.pop();
22664             cls.push('top');
22665         }
22666         
22667         cls.push('right');
22668         
22669         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22670             cls.pop();
22671             cls.push('left');
22672         }
22673         //this.picker().setXY(20000,20000);
22674         this.picker().addClass(cls.join('-'));
22675         
22676         var _this = this;
22677         
22678         Roo.each(cls, function(c){
22679             if(c == 'bottom'){
22680                 (function() {
22681                  //  
22682                 }).defer(200);
22683                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22684                 //_this.picker().setTop(_this.inputEl().getHeight());
22685                 return;
22686             }
22687             if(c == 'top'){
22688                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22689                 
22690                 //_this.picker().setTop(0 - _this.picker().getHeight());
22691                 return;
22692             }
22693             /*
22694             if(c == 'left'){
22695                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22696                 return;
22697             }
22698             if(c == 'right'){
22699                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22700                 return;
22701             }
22702             */
22703         });
22704         
22705     },
22706   
22707     onFocus : function()
22708     {
22709         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22710         this.show();
22711     },
22712     
22713     onBlur : function()
22714     {
22715         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22716         this.hide();
22717     },
22718     
22719     show : function()
22720     {
22721         this.picker().show();
22722         this.pop.show();
22723         this.update();
22724         this.place();
22725         
22726         this.fireEvent('show', this, this.date);
22727     },
22728     
22729     hide : function()
22730     {
22731         this.picker().hide();
22732         this.pop.hide();
22733         
22734         this.fireEvent('hide', this, this.date);
22735     },
22736     
22737     setTime : function()
22738     {
22739         this.hide();
22740         this.setValue(this.time.format(this.format));
22741         
22742         this.fireEvent('select', this, this.date);
22743         
22744         
22745     },
22746     
22747     onMousedown: function(e){
22748         e.stopPropagation();
22749         e.preventDefault();
22750     },
22751     
22752     onIncrementHours: function()
22753     {
22754         Roo.log('onIncrementHours');
22755         this.time = this.time.add(Date.HOUR, 1);
22756         this.update();
22757         
22758     },
22759     
22760     onDecrementHours: function()
22761     {
22762         Roo.log('onDecrementHours');
22763         this.time = this.time.add(Date.HOUR, -1);
22764         this.update();
22765     },
22766     
22767     onIncrementMinutes: function()
22768     {
22769         Roo.log('onIncrementMinutes');
22770         this.time = this.time.add(Date.MINUTE, 1);
22771         this.update();
22772     },
22773     
22774     onDecrementMinutes: function()
22775     {
22776         Roo.log('onDecrementMinutes');
22777         this.time = this.time.add(Date.MINUTE, -1);
22778         this.update();
22779     },
22780     
22781     onTogglePeriod: function()
22782     {
22783         Roo.log('onTogglePeriod');
22784         this.time = this.time.add(Date.HOUR, 12);
22785         this.update();
22786     }
22787     
22788    
22789 });
22790  
22791
22792 Roo.apply(Roo.bootstrap.TimeField,  {
22793   
22794     template : {
22795         tag: 'div',
22796         cls: 'datepicker dropdown-menu',
22797         cn: [
22798             {
22799                 tag: 'div',
22800                 cls: 'datepicker-time',
22801                 cn: [
22802                 {
22803                     tag: 'table',
22804                     cls: 'table-condensed',
22805                     cn:[
22806                         {
22807                             tag: 'tbody',
22808                             cn: [
22809                                 {
22810                                     tag: 'tr',
22811                                     cn: [
22812                                     {
22813                                         tag: 'td',
22814                                         colspan: '7'
22815                                     }
22816                                     ]
22817                                 }
22818                             ]
22819                         },
22820                         {
22821                             tag: 'tfoot',
22822                             cn: [
22823                                 {
22824                                     tag: 'tr',
22825                                     cn: [
22826                                     {
22827                                         tag: 'th',
22828                                         colspan: '7',
22829                                         cls: '',
22830                                         cn: [
22831                                             {
22832                                                 tag: 'button',
22833                                                 cls: 'btn btn-info ok',
22834                                                 html: 'OK'
22835                                             }
22836                                         ]
22837                                     }
22838                     
22839                                     ]
22840                                 }
22841                             ]
22842                         }
22843                     ]
22844                 }
22845                 ]
22846             }
22847         ]
22848     }
22849 });
22850
22851  
22852
22853  /*
22854  * - LGPL
22855  *
22856  * MonthField
22857  * 
22858  */
22859
22860 /**
22861  * @class Roo.bootstrap.MonthField
22862  * @extends Roo.bootstrap.Input
22863  * Bootstrap MonthField class
22864  * 
22865  * @cfg {String} language default en
22866  * 
22867  * @constructor
22868  * Create a new MonthField
22869  * @param {Object} config The config object
22870  */
22871
22872 Roo.bootstrap.MonthField = function(config){
22873     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22874     
22875     this.addEvents({
22876         /**
22877          * @event show
22878          * Fires when this field show.
22879          * @param {Roo.bootstrap.MonthField} this
22880          * @param {Mixed} date The date value
22881          */
22882         show : true,
22883         /**
22884          * @event show
22885          * Fires when this field hide.
22886          * @param {Roo.bootstrap.MonthField} this
22887          * @param {Mixed} date The date value
22888          */
22889         hide : true,
22890         /**
22891          * @event select
22892          * Fires when select a date.
22893          * @param {Roo.bootstrap.MonthField} this
22894          * @param {String} oldvalue The old value
22895          * @param {String} newvalue The new value
22896          */
22897         select : true
22898     });
22899 };
22900
22901 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22902     
22903     onRender: function(ct, position)
22904     {
22905         
22906         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22907         
22908         this.language = this.language || 'en';
22909         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22910         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22911         
22912         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22913         this.isInline = false;
22914         this.isInput = true;
22915         this.component = this.el.select('.add-on', true).first() || false;
22916         this.component = (this.component && this.component.length === 0) ? false : this.component;
22917         this.hasInput = this.component && this.inputEL().length;
22918         
22919         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22920         
22921         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22922         
22923         this.picker().on('mousedown', this.onMousedown, this);
22924         this.picker().on('click', this.onClick, this);
22925         
22926         this.picker().addClass('datepicker-dropdown');
22927         
22928         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22929             v.setStyle('width', '189px');
22930         });
22931         
22932         this.fillMonths();
22933         
22934         this.update();
22935         
22936         if(this.isInline) {
22937             this.show();
22938         }
22939         
22940     },
22941     
22942     setValue: function(v, suppressEvent)
22943     {   
22944         var o = this.getValue();
22945         
22946         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22947         
22948         this.update();
22949
22950         if(suppressEvent !== true){
22951             this.fireEvent('select', this, o, v);
22952         }
22953         
22954     },
22955     
22956     getValue: function()
22957     {
22958         return this.value;
22959     },
22960     
22961     onClick: function(e) 
22962     {
22963         e.stopPropagation();
22964         e.preventDefault();
22965         
22966         var target = e.getTarget();
22967         
22968         if(target.nodeName.toLowerCase() === 'i'){
22969             target = Roo.get(target).dom.parentNode;
22970         }
22971         
22972         var nodeName = target.nodeName;
22973         var className = target.className;
22974         var html = target.innerHTML;
22975         
22976         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22977             return;
22978         }
22979         
22980         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22981         
22982         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22983         
22984         this.hide();
22985                         
22986     },
22987     
22988     picker : function()
22989     {
22990         return this.pickerEl;
22991     },
22992     
22993     fillMonths: function()
22994     {    
22995         var i = 0;
22996         var months = this.picker().select('>.datepicker-months td', true).first();
22997         
22998         months.dom.innerHTML = '';
22999         
23000         while (i < 12) {
23001             var month = {
23002                 tag: 'span',
23003                 cls: 'month',
23004                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23005             };
23006             
23007             months.createChild(month);
23008         }
23009         
23010     },
23011     
23012     update: function()
23013     {
23014         var _this = this;
23015         
23016         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23017             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23018         }
23019         
23020         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23021             e.removeClass('active');
23022             
23023             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23024                 e.addClass('active');
23025             }
23026         })
23027     },
23028     
23029     place: function()
23030     {
23031         if(this.isInline) {
23032             return;
23033         }
23034         
23035         this.picker().removeClass(['bottom', 'top']);
23036         
23037         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23038             /*
23039              * place to the top of element!
23040              *
23041              */
23042             
23043             this.picker().addClass('top');
23044             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23045             
23046             return;
23047         }
23048         
23049         this.picker().addClass('bottom');
23050         
23051         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23052     },
23053     
23054     onFocus : function()
23055     {
23056         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23057         this.show();
23058     },
23059     
23060     onBlur : function()
23061     {
23062         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23063         
23064         var d = this.inputEl().getValue();
23065         
23066         this.setValue(d);
23067                 
23068         this.hide();
23069     },
23070     
23071     show : function()
23072     {
23073         this.picker().show();
23074         this.picker().select('>.datepicker-months', true).first().show();
23075         this.update();
23076         this.place();
23077         
23078         this.fireEvent('show', this, this.date);
23079     },
23080     
23081     hide : function()
23082     {
23083         if(this.isInline) {
23084             return;
23085         }
23086         this.picker().hide();
23087         this.fireEvent('hide', this, this.date);
23088         
23089     },
23090     
23091     onMousedown: function(e)
23092     {
23093         e.stopPropagation();
23094         e.preventDefault();
23095     },
23096     
23097     keyup: function(e)
23098     {
23099         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23100         this.update();
23101     },
23102
23103     fireKey: function(e)
23104     {
23105         if (!this.picker().isVisible()){
23106             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23107                 this.show();
23108             }
23109             return;
23110         }
23111         
23112         var dir;
23113         
23114         switch(e.keyCode){
23115             case 27: // escape
23116                 this.hide();
23117                 e.preventDefault();
23118                 break;
23119             case 37: // left
23120             case 39: // right
23121                 dir = e.keyCode == 37 ? -1 : 1;
23122                 
23123                 this.vIndex = this.vIndex + dir;
23124                 
23125                 if(this.vIndex < 0){
23126                     this.vIndex = 0;
23127                 }
23128                 
23129                 if(this.vIndex > 11){
23130                     this.vIndex = 11;
23131                 }
23132                 
23133                 if(isNaN(this.vIndex)){
23134                     this.vIndex = 0;
23135                 }
23136                 
23137                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23138                 
23139                 break;
23140             case 38: // up
23141             case 40: // down
23142                 
23143                 dir = e.keyCode == 38 ? -1 : 1;
23144                 
23145                 this.vIndex = this.vIndex + dir * 4;
23146                 
23147                 if(this.vIndex < 0){
23148                     this.vIndex = 0;
23149                 }
23150                 
23151                 if(this.vIndex > 11){
23152                     this.vIndex = 11;
23153                 }
23154                 
23155                 if(isNaN(this.vIndex)){
23156                     this.vIndex = 0;
23157                 }
23158                 
23159                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23160                 break;
23161                 
23162             case 13: // enter
23163                 
23164                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23165                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23166                 }
23167                 
23168                 this.hide();
23169                 e.preventDefault();
23170                 break;
23171             case 9: // tab
23172                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23173                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23174                 }
23175                 this.hide();
23176                 break;
23177             case 16: // shift
23178             case 17: // ctrl
23179             case 18: // alt
23180                 break;
23181             default :
23182                 this.hide();
23183                 
23184         }
23185     },
23186     
23187     remove: function() 
23188     {
23189         this.picker().remove();
23190     }
23191    
23192 });
23193
23194 Roo.apply(Roo.bootstrap.MonthField,  {
23195     
23196     content : {
23197         tag: 'tbody',
23198         cn: [
23199         {
23200             tag: 'tr',
23201             cn: [
23202             {
23203                 tag: 'td',
23204                 colspan: '7'
23205             }
23206             ]
23207         }
23208         ]
23209     },
23210     
23211     dates:{
23212         en: {
23213             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23214             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23215         }
23216     }
23217 });
23218
23219 Roo.apply(Roo.bootstrap.MonthField,  {
23220   
23221     template : {
23222         tag: 'div',
23223         cls: 'datepicker dropdown-menu roo-dynamic',
23224         cn: [
23225             {
23226                 tag: 'div',
23227                 cls: 'datepicker-months',
23228                 cn: [
23229                 {
23230                     tag: 'table',
23231                     cls: 'table-condensed',
23232                     cn:[
23233                         Roo.bootstrap.DateField.content
23234                     ]
23235                 }
23236                 ]
23237             }
23238         ]
23239     }
23240 });
23241
23242  
23243
23244  
23245  /*
23246  * - LGPL
23247  *
23248  * CheckBox
23249  * 
23250  */
23251
23252 /**
23253  * @class Roo.bootstrap.CheckBox
23254  * @extends Roo.bootstrap.Input
23255  * Bootstrap CheckBox class
23256  * 
23257  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23258  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23259  * @cfg {String} boxLabel The text that appears beside the checkbox
23260  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23261  * @cfg {Boolean} checked initnal the element
23262  * @cfg {Boolean} inline inline the element (default false)
23263  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23264  * @cfg {String} tooltip label tooltip
23265  * 
23266  * @constructor
23267  * Create a new CheckBox
23268  * @param {Object} config The config object
23269  */
23270
23271 Roo.bootstrap.CheckBox = function(config){
23272     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23273    
23274     this.addEvents({
23275         /**
23276         * @event check
23277         * Fires when the element is checked or unchecked.
23278         * @param {Roo.bootstrap.CheckBox} this This input
23279         * @param {Boolean} checked The new checked value
23280         */
23281        check : true,
23282        /**
23283         * @event click
23284         * Fires when the element is click.
23285         * @param {Roo.bootstrap.CheckBox} this This input
23286         */
23287        click : true
23288     });
23289     
23290 };
23291
23292 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23293   
23294     inputType: 'checkbox',
23295     inputValue: 1,
23296     valueOff: 0,
23297     boxLabel: false,
23298     checked: false,
23299     weight : false,
23300     inline: false,
23301     tooltip : '',
23302     
23303     // checkbox success does not make any sense really.. 
23304     invalidClass : "",
23305     validClass : "",
23306     
23307     
23308     getAutoCreate : function()
23309     {
23310         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23311         
23312         var id = Roo.id();
23313         
23314         var cfg = {};
23315         
23316         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23317         
23318         if(this.inline){
23319             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23320         }
23321         
23322         var input =  {
23323             tag: 'input',
23324             id : id,
23325             type : this.inputType,
23326             value : this.inputValue,
23327             cls : 'roo-' + this.inputType, //'form-box',
23328             placeholder : this.placeholder || ''
23329             
23330         };
23331         
23332         if(this.inputType != 'radio'){
23333             var hidden =  {
23334                 tag: 'input',
23335                 type : 'hidden',
23336                 cls : 'roo-hidden-value',
23337                 value : this.checked ? this.inputValue : this.valueOff
23338             };
23339         }
23340         
23341             
23342         if (this.weight) { // Validity check?
23343             cfg.cls += " " + this.inputType + "-" + this.weight;
23344         }
23345         
23346         if (this.disabled) {
23347             input.disabled=true;
23348         }
23349         
23350         if(this.checked){
23351             input.checked = this.checked;
23352         }
23353         
23354         if (this.name) {
23355             
23356             input.name = this.name;
23357             
23358             if(this.inputType != 'radio'){
23359                 hidden.name = this.name;
23360                 input.name = '_hidden_' + this.name;
23361             }
23362         }
23363         
23364         if (this.size) {
23365             input.cls += ' input-' + this.size;
23366         }
23367         
23368         var settings=this;
23369         
23370         ['xs','sm','md','lg'].map(function(size){
23371             if (settings[size]) {
23372                 cfg.cls += ' col-' + size + '-' + settings[size];
23373             }
23374         });
23375         
23376         var inputblock = input;
23377          
23378         if (this.before || this.after) {
23379             
23380             inputblock = {
23381                 cls : 'input-group',
23382                 cn :  [] 
23383             };
23384             
23385             if (this.before) {
23386                 inputblock.cn.push({
23387                     tag :'span',
23388                     cls : 'input-group-addon',
23389                     html : this.before
23390                 });
23391             }
23392             
23393             inputblock.cn.push(input);
23394             
23395             if(this.inputType != 'radio'){
23396                 inputblock.cn.push(hidden);
23397             }
23398             
23399             if (this.after) {
23400                 inputblock.cn.push({
23401                     tag :'span',
23402                     cls : 'input-group-addon',
23403                     html : this.after
23404                 });
23405             }
23406             
23407         }
23408         var boxLabelCfg = false;
23409         
23410         if(this.boxLabel){
23411            
23412             boxLabelCfg = {
23413                 tag: 'label',
23414                 //'for': id, // box label is handled by onclick - so no for...
23415                 cls: 'box-label',
23416                 html: this.boxLabel
23417             };
23418             if(this.tooltip){
23419                 boxLabelCfg.tooltip = this.tooltip;
23420             }
23421              
23422         }
23423         
23424         
23425         if (align ==='left' && this.fieldLabel.length) {
23426 //                Roo.log("left and has label");
23427             cfg.cn = [
23428                 {
23429                     tag: 'label',
23430                     'for' :  id,
23431                     cls : 'control-label',
23432                     html : this.fieldLabel
23433                 },
23434                 {
23435                     cls : "", 
23436                     cn: [
23437                         inputblock
23438                     ]
23439                 }
23440             ];
23441             
23442             if (boxLabelCfg) {
23443                 cfg.cn[1].cn.push(boxLabelCfg);
23444             }
23445             
23446             if(this.labelWidth > 12){
23447                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23448             }
23449             
23450             if(this.labelWidth < 13 && this.labelmd == 0){
23451                 this.labelmd = this.labelWidth;
23452             }
23453             
23454             if(this.labellg > 0){
23455                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23456                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23457             }
23458             
23459             if(this.labelmd > 0){
23460                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23461                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23462             }
23463             
23464             if(this.labelsm > 0){
23465                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23466                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23467             }
23468             
23469             if(this.labelxs > 0){
23470                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23471                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23472             }
23473             
23474         } else if ( this.fieldLabel.length) {
23475 //                Roo.log(" label");
23476                 cfg.cn = [
23477                    
23478                     {
23479                         tag: this.boxLabel ? 'span' : 'label',
23480                         'for': id,
23481                         cls: 'control-label box-input-label',
23482                         //cls : 'input-group-addon',
23483                         html : this.fieldLabel
23484                     },
23485                     
23486                     inputblock
23487                     
23488                 ];
23489                 if (boxLabelCfg) {
23490                     cfg.cn.push(boxLabelCfg);
23491                 }
23492
23493         } else {
23494             
23495 //                Roo.log(" no label && no align");
23496                 cfg.cn = [  inputblock ] ;
23497                 if (boxLabelCfg) {
23498                     cfg.cn.push(boxLabelCfg);
23499                 }
23500
23501                 
23502         }
23503         
23504        
23505         
23506         if(this.inputType != 'radio'){
23507             cfg.cn.push(hidden);
23508         }
23509         
23510         return cfg;
23511         
23512     },
23513     
23514     /**
23515      * return the real input element.
23516      */
23517     inputEl: function ()
23518     {
23519         return this.el.select('input.roo-' + this.inputType,true).first();
23520     },
23521     hiddenEl: function ()
23522     {
23523         return this.el.select('input.roo-hidden-value',true).first();
23524     },
23525     
23526     labelEl: function()
23527     {
23528         return this.el.select('label.control-label',true).first();
23529     },
23530     /* depricated... */
23531     
23532     label: function()
23533     {
23534         return this.labelEl();
23535     },
23536     
23537     boxLabelEl: function()
23538     {
23539         return this.el.select('label.box-label',true).first();
23540     },
23541     
23542     initEvents : function()
23543     {
23544 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23545         
23546         this.inputEl().on('click', this.onClick,  this);
23547         
23548         if (this.boxLabel) { 
23549             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23550         }
23551         
23552         this.startValue = this.getValue();
23553         
23554         if(this.groupId){
23555             Roo.bootstrap.CheckBox.register(this);
23556         }
23557     },
23558     
23559     onClick : function(e)
23560     {   
23561         if(this.fireEvent('click', this, e) !== false){
23562             this.setChecked(!this.checked);
23563         }
23564         
23565     },
23566     
23567     setChecked : function(state,suppressEvent)
23568     {
23569         this.startValue = this.getValue();
23570
23571         if(this.inputType == 'radio'){
23572             
23573             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23574                 e.dom.checked = false;
23575             });
23576             
23577             this.inputEl().dom.checked = true;
23578             
23579             this.inputEl().dom.value = this.inputValue;
23580             
23581             if(suppressEvent !== true){
23582                 this.fireEvent('check', this, true);
23583             }
23584             
23585             this.validate();
23586             
23587             return;
23588         }
23589         
23590         this.checked = state;
23591         
23592         this.inputEl().dom.checked = state;
23593         
23594         
23595         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23596         
23597         if(suppressEvent !== true){
23598             this.fireEvent('check', this, state);
23599         }
23600         
23601         this.validate();
23602     },
23603     
23604     getValue : function()
23605     {
23606         if(this.inputType == 'radio'){
23607             return this.getGroupValue();
23608         }
23609         
23610         return this.hiddenEl().dom.value;
23611         
23612     },
23613     
23614     getGroupValue : function()
23615     {
23616         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23617             return '';
23618         }
23619         
23620         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23621     },
23622     
23623     setValue : function(v,suppressEvent)
23624     {
23625         if(this.inputType == 'radio'){
23626             this.setGroupValue(v, suppressEvent);
23627             return;
23628         }
23629         
23630         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23631         
23632         this.validate();
23633     },
23634     
23635     setGroupValue : function(v, suppressEvent)
23636     {
23637         this.startValue = this.getValue();
23638         
23639         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23640             e.dom.checked = false;
23641             
23642             if(e.dom.value == v){
23643                 e.dom.checked = true;
23644             }
23645         });
23646         
23647         if(suppressEvent !== true){
23648             this.fireEvent('check', this, true);
23649         }
23650
23651         this.validate();
23652         
23653         return;
23654     },
23655     
23656     validate : function()
23657     {
23658         if(this.getVisibilityEl().hasClass('hidden')){
23659             return true;
23660         }
23661         
23662         if(
23663                 this.disabled || 
23664                 (this.inputType == 'radio' && this.validateRadio()) ||
23665                 (this.inputType == 'checkbox' && this.validateCheckbox())
23666         ){
23667             this.markValid();
23668             return true;
23669         }
23670         
23671         this.markInvalid();
23672         return false;
23673     },
23674     
23675     validateRadio : function()
23676     {
23677         if(this.getVisibilityEl().hasClass('hidden')){
23678             return true;
23679         }
23680         
23681         if(this.allowBlank){
23682             return true;
23683         }
23684         
23685         var valid = false;
23686         
23687         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23688             if(!e.dom.checked){
23689                 return;
23690             }
23691             
23692             valid = true;
23693             
23694             return false;
23695         });
23696         
23697         return valid;
23698     },
23699     
23700     validateCheckbox : function()
23701     {
23702         if(!this.groupId){
23703             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23704             //return (this.getValue() == this.inputValue) ? true : false;
23705         }
23706         
23707         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23708         
23709         if(!group){
23710             return false;
23711         }
23712         
23713         var r = false;
23714         
23715         for(var i in group){
23716             if(group[i].el.isVisible(true)){
23717                 r = false;
23718                 break;
23719             }
23720             
23721             r = true;
23722         }
23723         
23724         for(var i in group){
23725             if(r){
23726                 break;
23727             }
23728             
23729             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23730         }
23731         
23732         return r;
23733     },
23734     
23735     /**
23736      * Mark this field as valid
23737      */
23738     markValid : function()
23739     {
23740         var _this = this;
23741         
23742         this.fireEvent('valid', this);
23743         
23744         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23745         
23746         if(this.groupId){
23747             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23748         }
23749         
23750         if(label){
23751             label.markValid();
23752         }
23753
23754         if(this.inputType == 'radio'){
23755             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23756                 var fg = e.findParent('.form-group', false, true);
23757                 if (Roo.bootstrap.version == 3) {
23758                     fg.removeClass([_this.invalidClass, _this.validClass]);
23759                     fg.addClass(_this.validClass);
23760                 } else {
23761                     fg.removeClass(['is-valid', 'is-invalid']);
23762                     fg.addClass('is-valid');
23763                 }
23764             });
23765             
23766             return;
23767         }
23768
23769         if(!this.groupId){
23770             var fg = this.el.findParent('.form-group', false, true);
23771             if (Roo.bootstrap.version == 3) {
23772                 fg.removeClass([this.invalidClass, this.validClass]);
23773                 fg.addClass(this.validClass);
23774             } else {
23775                 fg.removeClass(['is-valid', 'is-invalid']);
23776                 fg.addClass('is-valid');
23777             }
23778             return;
23779         }
23780         
23781         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23782         
23783         if(!group){
23784             return;
23785         }
23786         
23787         for(var i in group){
23788             var fg = group[i].el.findParent('.form-group', false, true);
23789             if (Roo.bootstrap.version == 3) {
23790                 fg.removeClass([this.invalidClass, this.validClass]);
23791                 fg.addClass(this.validClass);
23792             } else {
23793                 fg.removeClass(['is-valid', 'is-invalid']);
23794                 fg.addClass('is-valid');
23795             }
23796         }
23797     },
23798     
23799      /**
23800      * Mark this field as invalid
23801      * @param {String} msg The validation message
23802      */
23803     markInvalid : function(msg)
23804     {
23805         if(this.allowBlank){
23806             return;
23807         }
23808         
23809         var _this = this;
23810         
23811         this.fireEvent('invalid', this, msg);
23812         
23813         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23814         
23815         if(this.groupId){
23816             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23817         }
23818         
23819         if(label){
23820             label.markInvalid();
23821         }
23822             
23823         if(this.inputType == 'radio'){
23824             
23825             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23826                 var fg = e.findParent('.form-group', false, true);
23827                 if (Roo.bootstrap.version == 3) {
23828                     fg.removeClass([_this.invalidClass, _this.validClass]);
23829                     fg.addClass(_this.invalidClass);
23830                 } else {
23831                     fg.removeClass(['is-invalid', 'is-valid']);
23832                     fg.addClass('is-invalid');
23833                 }
23834             });
23835             
23836             return;
23837         }
23838         
23839         if(!this.groupId){
23840             var fg = this.el.findParent('.form-group', false, true);
23841             if (Roo.bootstrap.version == 3) {
23842                 fg.removeClass([_this.invalidClass, _this.validClass]);
23843                 fg.addClass(_this.invalidClass);
23844             } else {
23845                 fg.removeClass(['is-invalid', 'is-valid']);
23846                 fg.addClass('is-invalid');
23847             }
23848             return;
23849         }
23850         
23851         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23852         
23853         if(!group){
23854             return;
23855         }
23856         
23857         for(var i in group){
23858             var fg = group[i].el.findParent('.form-group', false, true);
23859             if (Roo.bootstrap.version == 3) {
23860                 fg.removeClass([_this.invalidClass, _this.validClass]);
23861                 fg.addClass(_this.invalidClass);
23862             } else {
23863                 fg.removeClass(['is-invalid', 'is-valid']);
23864                 fg.addClass('is-invalid');
23865             }
23866         }
23867         
23868     },
23869     
23870     clearInvalid : function()
23871     {
23872         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23873         
23874         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23875         
23876         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23877         
23878         if (label && label.iconEl) {
23879             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23880             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23881         }
23882     },
23883     
23884     disable : function()
23885     {
23886         if(this.inputType != 'radio'){
23887             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23888             return;
23889         }
23890         
23891         var _this = this;
23892         
23893         if(this.rendered){
23894             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23895                 _this.getActionEl().addClass(this.disabledClass);
23896                 e.dom.disabled = true;
23897             });
23898         }
23899         
23900         this.disabled = true;
23901         this.fireEvent("disable", this);
23902         return this;
23903     },
23904
23905     enable : function()
23906     {
23907         if(this.inputType != 'radio'){
23908             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23909             return;
23910         }
23911         
23912         var _this = this;
23913         
23914         if(this.rendered){
23915             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23916                 _this.getActionEl().removeClass(this.disabledClass);
23917                 e.dom.disabled = false;
23918             });
23919         }
23920         
23921         this.disabled = false;
23922         this.fireEvent("enable", this);
23923         return this;
23924     },
23925     
23926     setBoxLabel : function(v)
23927     {
23928         this.boxLabel = v;
23929         
23930         if(this.rendered){
23931             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23932         }
23933     }
23934
23935 });
23936
23937 Roo.apply(Roo.bootstrap.CheckBox, {
23938     
23939     groups: {},
23940     
23941      /**
23942     * register a CheckBox Group
23943     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23944     */
23945     register : function(checkbox)
23946     {
23947         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23948             this.groups[checkbox.groupId] = {};
23949         }
23950         
23951         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23952             return;
23953         }
23954         
23955         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23956         
23957     },
23958     /**
23959     * fetch a CheckBox Group based on the group ID
23960     * @param {string} the group ID
23961     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23962     */
23963     get: function(groupId) {
23964         if (typeof(this.groups[groupId]) == 'undefined') {
23965             return false;
23966         }
23967         
23968         return this.groups[groupId] ;
23969     }
23970     
23971     
23972 });
23973 /*
23974  * - LGPL
23975  *
23976  * RadioItem
23977  * 
23978  */
23979
23980 /**
23981  * @class Roo.bootstrap.Radio
23982  * @extends Roo.bootstrap.Component
23983  * Bootstrap Radio class
23984  * @cfg {String} boxLabel - the label associated
23985  * @cfg {String} value - the value of radio
23986  * 
23987  * @constructor
23988  * Create a new Radio
23989  * @param {Object} config The config object
23990  */
23991 Roo.bootstrap.Radio = function(config){
23992     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23993     
23994 };
23995
23996 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23997     
23998     boxLabel : '',
23999     
24000     value : '',
24001     
24002     getAutoCreate : function()
24003     {
24004         var cfg = {
24005             tag : 'div',
24006             cls : 'form-group radio',
24007             cn : [
24008                 {
24009                     tag : 'label',
24010                     cls : 'box-label',
24011                     html : this.boxLabel
24012                 }
24013             ]
24014         };
24015         
24016         return cfg;
24017     },
24018     
24019     initEvents : function() 
24020     {
24021         this.parent().register(this);
24022         
24023         this.el.on('click', this.onClick, this);
24024         
24025     },
24026     
24027     onClick : function(e)
24028     {
24029         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24030             this.setChecked(true);
24031         }
24032     },
24033     
24034     setChecked : function(state, suppressEvent)
24035     {
24036         this.parent().setValue(this.value, suppressEvent);
24037         
24038     },
24039     
24040     setBoxLabel : function(v)
24041     {
24042         this.boxLabel = v;
24043         
24044         if(this.rendered){
24045             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24046         }
24047     }
24048     
24049 });
24050  
24051
24052  /*
24053  * - LGPL
24054  *
24055  * Input
24056  * 
24057  */
24058
24059 /**
24060  * @class Roo.bootstrap.SecurePass
24061  * @extends Roo.bootstrap.Input
24062  * Bootstrap SecurePass class
24063  *
24064  * 
24065  * @constructor
24066  * Create a new SecurePass
24067  * @param {Object} config The config object
24068  */
24069  
24070 Roo.bootstrap.SecurePass = function (config) {
24071     // these go here, so the translation tool can replace them..
24072     this.errors = {
24073         PwdEmpty: "Please type a password, and then retype it to confirm.",
24074         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24075         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24076         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24077         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24078         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24079         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24080         TooWeak: "Your password is Too Weak."
24081     },
24082     this.meterLabel = "Password strength:";
24083     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24084     this.meterClass = [
24085         "roo-password-meter-tooweak", 
24086         "roo-password-meter-weak", 
24087         "roo-password-meter-medium", 
24088         "roo-password-meter-strong", 
24089         "roo-password-meter-grey"
24090     ];
24091     
24092     this.errors = {};
24093     
24094     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24095 }
24096
24097 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24098     /**
24099      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24100      * {
24101      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24102      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24103      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24104      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24105      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24106      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24107      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24108      * })
24109      */
24110     // private
24111     
24112     meterWidth: 300,
24113     errorMsg :'',    
24114     errors: false,
24115     imageRoot: '/',
24116     /**
24117      * @cfg {String/Object} Label for the strength meter (defaults to
24118      * 'Password strength:')
24119      */
24120     // private
24121     meterLabel: '',
24122     /**
24123      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24124      * ['Weak', 'Medium', 'Strong'])
24125      */
24126     // private    
24127     pwdStrengths: false,    
24128     // private
24129     strength: 0,
24130     // private
24131     _lastPwd: null,
24132     // private
24133     kCapitalLetter: 0,
24134     kSmallLetter: 1,
24135     kDigit: 2,
24136     kPunctuation: 3,
24137     
24138     insecure: false,
24139     // private
24140     initEvents: function ()
24141     {
24142         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24143
24144         if (this.el.is('input[type=password]') && Roo.isSafari) {
24145             this.el.on('keydown', this.SafariOnKeyDown, this);
24146         }
24147
24148         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24149     },
24150     // private
24151     onRender: function (ct, position)
24152     {
24153         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24154         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24155         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24156
24157         this.trigger.createChild({
24158                    cn: [
24159                     {
24160                     //id: 'PwdMeter',
24161                     tag: 'div',
24162                     cls: 'roo-password-meter-grey col-xs-12',
24163                     style: {
24164                         //width: 0,
24165                         //width: this.meterWidth + 'px'                                                
24166                         }
24167                     },
24168                     {                            
24169                          cls: 'roo-password-meter-text'                          
24170                     }
24171                 ]            
24172         });
24173
24174          
24175         if (this.hideTrigger) {
24176             this.trigger.setDisplayed(false);
24177         }
24178         this.setSize(this.width || '', this.height || '');
24179     },
24180     // private
24181     onDestroy: function ()
24182     {
24183         if (this.trigger) {
24184             this.trigger.removeAllListeners();
24185             this.trigger.remove();
24186         }
24187         if (this.wrap) {
24188             this.wrap.remove();
24189         }
24190         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24191     },
24192     // private
24193     checkStrength: function ()
24194     {
24195         var pwd = this.inputEl().getValue();
24196         if (pwd == this._lastPwd) {
24197             return;
24198         }
24199
24200         var strength;
24201         if (this.ClientSideStrongPassword(pwd)) {
24202             strength = 3;
24203         } else if (this.ClientSideMediumPassword(pwd)) {
24204             strength = 2;
24205         } else if (this.ClientSideWeakPassword(pwd)) {
24206             strength = 1;
24207         } else {
24208             strength = 0;
24209         }
24210         
24211         Roo.log('strength1: ' + strength);
24212         
24213         //var pm = this.trigger.child('div/div/div').dom;
24214         var pm = this.trigger.child('div/div');
24215         pm.removeClass(this.meterClass);
24216         pm.addClass(this.meterClass[strength]);
24217                 
24218         
24219         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24220                 
24221         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24222         
24223         this._lastPwd = pwd;
24224     },
24225     reset: function ()
24226     {
24227         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24228         
24229         this._lastPwd = '';
24230         
24231         var pm = this.trigger.child('div/div');
24232         pm.removeClass(this.meterClass);
24233         pm.addClass('roo-password-meter-grey');        
24234         
24235         
24236         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24237         
24238         pt.innerHTML = '';
24239         this.inputEl().dom.type='password';
24240     },
24241     // private
24242     validateValue: function (value)
24243     {
24244         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24245             return false;
24246         }
24247         if (value.length == 0) {
24248             if (this.allowBlank) {
24249                 this.clearInvalid();
24250                 return true;
24251             }
24252
24253             this.markInvalid(this.errors.PwdEmpty);
24254             this.errorMsg = this.errors.PwdEmpty;
24255             return false;
24256         }
24257         
24258         if(this.insecure){
24259             return true;
24260         }
24261         
24262         if (!value.match(/[\x21-\x7e]+/)) {
24263             this.markInvalid(this.errors.PwdBadChar);
24264             this.errorMsg = this.errors.PwdBadChar;
24265             return false;
24266         }
24267         if (value.length < 6) {
24268             this.markInvalid(this.errors.PwdShort);
24269             this.errorMsg = this.errors.PwdShort;
24270             return false;
24271         }
24272         if (value.length > 16) {
24273             this.markInvalid(this.errors.PwdLong);
24274             this.errorMsg = this.errors.PwdLong;
24275             return false;
24276         }
24277         var strength;
24278         if (this.ClientSideStrongPassword(value)) {
24279             strength = 3;
24280         } else if (this.ClientSideMediumPassword(value)) {
24281             strength = 2;
24282         } else if (this.ClientSideWeakPassword(value)) {
24283             strength = 1;
24284         } else {
24285             strength = 0;
24286         }
24287
24288         
24289         if (strength < 2) {
24290             //this.markInvalid(this.errors.TooWeak);
24291             this.errorMsg = this.errors.TooWeak;
24292             //return false;
24293         }
24294         
24295         
24296         console.log('strength2: ' + strength);
24297         
24298         //var pm = this.trigger.child('div/div/div').dom;
24299         
24300         var pm = this.trigger.child('div/div');
24301         pm.removeClass(this.meterClass);
24302         pm.addClass(this.meterClass[strength]);
24303                 
24304         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24305                 
24306         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24307         
24308         this.errorMsg = ''; 
24309         return true;
24310     },
24311     // private
24312     CharacterSetChecks: function (type)
24313     {
24314         this.type = type;
24315         this.fResult = false;
24316     },
24317     // private
24318     isctype: function (character, type)
24319     {
24320         switch (type) {  
24321             case this.kCapitalLetter:
24322                 if (character >= 'A' && character <= 'Z') {
24323                     return true;
24324                 }
24325                 break;
24326             
24327             case this.kSmallLetter:
24328                 if (character >= 'a' && character <= 'z') {
24329                     return true;
24330                 }
24331                 break;
24332             
24333             case this.kDigit:
24334                 if (character >= '0' && character <= '9') {
24335                     return true;
24336                 }
24337                 break;
24338             
24339             case this.kPunctuation:
24340                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24341                     return true;
24342                 }
24343                 break;
24344             
24345             default:
24346                 return false;
24347         }
24348
24349     },
24350     // private
24351     IsLongEnough: function (pwd, size)
24352     {
24353         return !(pwd == null || isNaN(size) || pwd.length < size);
24354     },
24355     // private
24356     SpansEnoughCharacterSets: function (word, nb)
24357     {
24358         if (!this.IsLongEnough(word, nb))
24359         {
24360             return false;
24361         }
24362
24363         var characterSetChecks = new Array(
24364             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24365             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24366         );
24367         
24368         for (var index = 0; index < word.length; ++index) {
24369             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24370                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24371                     characterSetChecks[nCharSet].fResult = true;
24372                     break;
24373                 }
24374             }
24375         }
24376
24377         var nCharSets = 0;
24378         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24379             if (characterSetChecks[nCharSet].fResult) {
24380                 ++nCharSets;
24381             }
24382         }
24383
24384         if (nCharSets < nb) {
24385             return false;
24386         }
24387         return true;
24388     },
24389     // private
24390     ClientSideStrongPassword: function (pwd)
24391     {
24392         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24393     },
24394     // private
24395     ClientSideMediumPassword: function (pwd)
24396     {
24397         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24398     },
24399     // private
24400     ClientSideWeakPassword: function (pwd)
24401     {
24402         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24403     }
24404           
24405 })//<script type="text/javascript">
24406
24407 /*
24408  * Based  Ext JS Library 1.1.1
24409  * Copyright(c) 2006-2007, Ext JS, LLC.
24410  * LGPL
24411  *
24412  */
24413  
24414 /**
24415  * @class Roo.HtmlEditorCore
24416  * @extends Roo.Component
24417  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24418  *
24419  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24420  */
24421
24422 Roo.HtmlEditorCore = function(config){
24423     
24424     
24425     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24426     
24427     
24428     this.addEvents({
24429         /**
24430          * @event initialize
24431          * Fires when the editor is fully initialized (including the iframe)
24432          * @param {Roo.HtmlEditorCore} this
24433          */
24434         initialize: true,
24435         /**
24436          * @event activate
24437          * Fires when the editor is first receives the focus. Any insertion must wait
24438          * until after this event.
24439          * @param {Roo.HtmlEditorCore} this
24440          */
24441         activate: true,
24442          /**
24443          * @event beforesync
24444          * Fires before the textarea is updated with content from the editor iframe. Return false
24445          * to cancel the sync.
24446          * @param {Roo.HtmlEditorCore} this
24447          * @param {String} html
24448          */
24449         beforesync: true,
24450          /**
24451          * @event beforepush
24452          * Fires before the iframe editor is updated with content from the textarea. Return false
24453          * to cancel the push.
24454          * @param {Roo.HtmlEditorCore} this
24455          * @param {String} html
24456          */
24457         beforepush: true,
24458          /**
24459          * @event sync
24460          * Fires when the textarea is updated with content from the editor iframe.
24461          * @param {Roo.HtmlEditorCore} this
24462          * @param {String} html
24463          */
24464         sync: true,
24465          /**
24466          * @event push
24467          * Fires when the iframe editor is updated with content from the textarea.
24468          * @param {Roo.HtmlEditorCore} this
24469          * @param {String} html
24470          */
24471         push: true,
24472         
24473         /**
24474          * @event editorevent
24475          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24476          * @param {Roo.HtmlEditorCore} this
24477          */
24478         editorevent: true
24479         
24480     });
24481     
24482     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24483     
24484     // defaults : white / black...
24485     this.applyBlacklists();
24486     
24487     
24488     
24489 };
24490
24491
24492 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24493
24494
24495      /**
24496      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24497      */
24498     
24499     owner : false,
24500     
24501      /**
24502      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24503      *                        Roo.resizable.
24504      */
24505     resizable : false,
24506      /**
24507      * @cfg {Number} height (in pixels)
24508      */   
24509     height: 300,
24510    /**
24511      * @cfg {Number} width (in pixels)
24512      */   
24513     width: 500,
24514     
24515     /**
24516      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24517      * 
24518      */
24519     stylesheets: false,
24520     
24521     // id of frame..
24522     frameId: false,
24523     
24524     // private properties
24525     validationEvent : false,
24526     deferHeight: true,
24527     initialized : false,
24528     activated : false,
24529     sourceEditMode : false,
24530     onFocus : Roo.emptyFn,
24531     iframePad:3,
24532     hideMode:'offsets',
24533     
24534     clearUp: true,
24535     
24536     // blacklist + whitelisted elements..
24537     black: false,
24538     white: false,
24539      
24540     bodyCls : '',
24541
24542     /**
24543      * Protected method that will not generally be called directly. It
24544      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24545      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24546      */
24547     getDocMarkup : function(){
24548         // body styles..
24549         var st = '';
24550         
24551         // inherit styels from page...?? 
24552         if (this.stylesheets === false) {
24553             
24554             Roo.get(document.head).select('style').each(function(node) {
24555                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24556             });
24557             
24558             Roo.get(document.head).select('link').each(function(node) { 
24559                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24560             });
24561             
24562         } else if (!this.stylesheets.length) {
24563                 // simple..
24564                 st = '<style type="text/css">' +
24565                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24566                    '</style>';
24567         } else {
24568             for (var i in this.stylesheets) { 
24569                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24570             }
24571             
24572         }
24573         
24574         st +=  '<style type="text/css">' +
24575             'IMG { cursor: pointer } ' +
24576         '</style>';
24577
24578         var cls = 'roo-htmleditor-body';
24579         
24580         if(this.bodyCls.length){
24581             cls += ' ' + this.bodyCls;
24582         }
24583         
24584         return '<html><head>' + st  +
24585             //<style type="text/css">' +
24586             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24587             //'</style>' +
24588             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24589     },
24590
24591     // private
24592     onRender : function(ct, position)
24593     {
24594         var _t = this;
24595         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24596         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24597         
24598         
24599         this.el.dom.style.border = '0 none';
24600         this.el.dom.setAttribute('tabIndex', -1);
24601         this.el.addClass('x-hidden hide');
24602         
24603         
24604         
24605         if(Roo.isIE){ // fix IE 1px bogus margin
24606             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24607         }
24608        
24609         
24610         this.frameId = Roo.id();
24611         
24612          
24613         
24614         var iframe = this.owner.wrap.createChild({
24615             tag: 'iframe',
24616             cls: 'form-control', // bootstrap..
24617             id: this.frameId,
24618             name: this.frameId,
24619             frameBorder : 'no',
24620             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24621         }, this.el
24622         );
24623         
24624         
24625         this.iframe = iframe.dom;
24626
24627          this.assignDocWin();
24628         
24629         this.doc.designMode = 'on';
24630        
24631         this.doc.open();
24632         this.doc.write(this.getDocMarkup());
24633         this.doc.close();
24634
24635         
24636         var task = { // must defer to wait for browser to be ready
24637             run : function(){
24638                 //console.log("run task?" + this.doc.readyState);
24639                 this.assignDocWin();
24640                 if(this.doc.body || this.doc.readyState == 'complete'){
24641                     try {
24642                         this.doc.designMode="on";
24643                     } catch (e) {
24644                         return;
24645                     }
24646                     Roo.TaskMgr.stop(task);
24647                     this.initEditor.defer(10, this);
24648                 }
24649             },
24650             interval : 10,
24651             duration: 10000,
24652             scope: this
24653         };
24654         Roo.TaskMgr.start(task);
24655
24656     },
24657
24658     // private
24659     onResize : function(w, h)
24660     {
24661          Roo.log('resize: ' +w + ',' + h );
24662         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24663         if(!this.iframe){
24664             return;
24665         }
24666         if(typeof w == 'number'){
24667             
24668             this.iframe.style.width = w + 'px';
24669         }
24670         if(typeof h == 'number'){
24671             
24672             this.iframe.style.height = h + 'px';
24673             if(this.doc){
24674                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24675             }
24676         }
24677         
24678     },
24679
24680     /**
24681      * Toggles the editor between standard and source edit mode.
24682      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24683      */
24684     toggleSourceEdit : function(sourceEditMode){
24685         
24686         this.sourceEditMode = sourceEditMode === true;
24687         
24688         if(this.sourceEditMode){
24689  
24690             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24691             
24692         }else{
24693             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24694             //this.iframe.className = '';
24695             this.deferFocus();
24696         }
24697         //this.setSize(this.owner.wrap.getSize());
24698         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24699     },
24700
24701     
24702   
24703
24704     /**
24705      * Protected method that will not generally be called directly. If you need/want
24706      * custom HTML cleanup, this is the method you should override.
24707      * @param {String} html The HTML to be cleaned
24708      * return {String} The cleaned HTML
24709      */
24710     cleanHtml : function(html){
24711         html = String(html);
24712         if(html.length > 5){
24713             if(Roo.isSafari){ // strip safari nonsense
24714                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24715             }
24716         }
24717         if(html == '&nbsp;'){
24718             html = '';
24719         }
24720         return html;
24721     },
24722
24723     /**
24724      * HTML Editor -> Textarea
24725      * Protected method that will not generally be called directly. Syncs the contents
24726      * of the editor iframe with the textarea.
24727      */
24728     syncValue : function(){
24729         if(this.initialized){
24730             var bd = (this.doc.body || this.doc.documentElement);
24731             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24732             var html = bd.innerHTML;
24733             if(Roo.isSafari){
24734                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24735                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24736                 if(m && m[1]){
24737                     html = '<div style="'+m[0]+'">' + html + '</div>';
24738                 }
24739             }
24740             html = this.cleanHtml(html);
24741             // fix up the special chars.. normaly like back quotes in word...
24742             // however we do not want to do this with chinese..
24743             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24744                 
24745                 var cc = match.charCodeAt();
24746
24747                 // Get the character value, handling surrogate pairs
24748                 if (match.length == 2) {
24749                     // It's a surrogate pair, calculate the Unicode code point
24750                     var high = match.charCodeAt(0) - 0xD800;
24751                     var low  = match.charCodeAt(1) - 0xDC00;
24752                     cc = (high * 0x400) + low + 0x10000;
24753                 }  else if (
24754                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24755                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24756                     (cc >= 0xf900 && cc < 0xfb00 )
24757                 ) {
24758                         return match;
24759                 }  
24760          
24761                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24762                 return "&#" + cc + ";";
24763                 
24764                 
24765             });
24766             
24767             
24768              
24769             if(this.owner.fireEvent('beforesync', this, html) !== false){
24770                 this.el.dom.value = html;
24771                 this.owner.fireEvent('sync', this, html);
24772             }
24773         }
24774     },
24775
24776     /**
24777      * Protected method that will not generally be called directly. Pushes the value of the textarea
24778      * into the iframe editor.
24779      */
24780     pushValue : function(){
24781         if(this.initialized){
24782             var v = this.el.dom.value.trim();
24783             
24784 //            if(v.length < 1){
24785 //                v = '&#160;';
24786 //            }
24787             
24788             if(this.owner.fireEvent('beforepush', this, v) !== false){
24789                 var d = (this.doc.body || this.doc.documentElement);
24790                 d.innerHTML = v;
24791                 this.cleanUpPaste();
24792                 this.el.dom.value = d.innerHTML;
24793                 this.owner.fireEvent('push', this, v);
24794             }
24795         }
24796     },
24797
24798     // private
24799     deferFocus : function(){
24800         this.focus.defer(10, this);
24801     },
24802
24803     // doc'ed in Field
24804     focus : function(){
24805         if(this.win && !this.sourceEditMode){
24806             this.win.focus();
24807         }else{
24808             this.el.focus();
24809         }
24810     },
24811     
24812     assignDocWin: function()
24813     {
24814         var iframe = this.iframe;
24815         
24816          if(Roo.isIE){
24817             this.doc = iframe.contentWindow.document;
24818             this.win = iframe.contentWindow;
24819         } else {
24820 //            if (!Roo.get(this.frameId)) {
24821 //                return;
24822 //            }
24823 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24824 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24825             
24826             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24827                 return;
24828             }
24829             
24830             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24831             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24832         }
24833     },
24834     
24835     // private
24836     initEditor : function(){
24837         //console.log("INIT EDITOR");
24838         this.assignDocWin();
24839         
24840         
24841         
24842         this.doc.designMode="on";
24843         this.doc.open();
24844         this.doc.write(this.getDocMarkup());
24845         this.doc.close();
24846         
24847         var dbody = (this.doc.body || this.doc.documentElement);
24848         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24849         // this copies styles from the containing element into thsi one..
24850         // not sure why we need all of this..
24851         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24852         
24853         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24854         //ss['background-attachment'] = 'fixed'; // w3c
24855         dbody.bgProperties = 'fixed'; // ie
24856         //Roo.DomHelper.applyStyles(dbody, ss);
24857         Roo.EventManager.on(this.doc, {
24858             //'mousedown': this.onEditorEvent,
24859             'mouseup': this.onEditorEvent,
24860             'dblclick': this.onEditorEvent,
24861             'click': this.onEditorEvent,
24862             'keyup': this.onEditorEvent,
24863             buffer:100,
24864             scope: this
24865         });
24866         if(Roo.isGecko){
24867             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24868         }
24869         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24870             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24871         }
24872         this.initialized = true;
24873
24874         this.owner.fireEvent('initialize', this);
24875         this.pushValue();
24876     },
24877
24878     // private
24879     onDestroy : function(){
24880         
24881         
24882         
24883         if(this.rendered){
24884             
24885             //for (var i =0; i < this.toolbars.length;i++) {
24886             //    // fixme - ask toolbars for heights?
24887             //    this.toolbars[i].onDestroy();
24888            // }
24889             
24890             //this.wrap.dom.innerHTML = '';
24891             //this.wrap.remove();
24892         }
24893     },
24894
24895     // private
24896     onFirstFocus : function(){
24897         
24898         this.assignDocWin();
24899         
24900         
24901         this.activated = true;
24902          
24903     
24904         if(Roo.isGecko){ // prevent silly gecko errors
24905             this.win.focus();
24906             var s = this.win.getSelection();
24907             if(!s.focusNode || s.focusNode.nodeType != 3){
24908                 var r = s.getRangeAt(0);
24909                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24910                 r.collapse(true);
24911                 this.deferFocus();
24912             }
24913             try{
24914                 this.execCmd('useCSS', true);
24915                 this.execCmd('styleWithCSS', false);
24916             }catch(e){}
24917         }
24918         this.owner.fireEvent('activate', this);
24919     },
24920
24921     // private
24922     adjustFont: function(btn){
24923         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24924         //if(Roo.isSafari){ // safari
24925         //    adjust *= 2;
24926        // }
24927         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24928         if(Roo.isSafari){ // safari
24929             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24930             v =  (v < 10) ? 10 : v;
24931             v =  (v > 48) ? 48 : v;
24932             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24933             
24934         }
24935         
24936         
24937         v = Math.max(1, v+adjust);
24938         
24939         this.execCmd('FontSize', v  );
24940     },
24941
24942     onEditorEvent : function(e)
24943     {
24944         this.owner.fireEvent('editorevent', this, e);
24945       //  this.updateToolbar();
24946         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24947     },
24948
24949     insertTag : function(tg)
24950     {
24951         // could be a bit smarter... -> wrap the current selected tRoo..
24952         if (tg.toLowerCase() == 'span' ||
24953             tg.toLowerCase() == 'code' ||
24954             tg.toLowerCase() == 'sup' ||
24955             tg.toLowerCase() == 'sub' 
24956             ) {
24957             
24958             range = this.createRange(this.getSelection());
24959             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24960             wrappingNode.appendChild(range.extractContents());
24961             range.insertNode(wrappingNode);
24962
24963             return;
24964             
24965             
24966             
24967         }
24968         this.execCmd("formatblock",   tg);
24969         
24970     },
24971     
24972     insertText : function(txt)
24973     {
24974         
24975         
24976         var range = this.createRange();
24977         range.deleteContents();
24978                //alert(Sender.getAttribute('label'));
24979                
24980         range.insertNode(this.doc.createTextNode(txt));
24981     } ,
24982     
24983      
24984
24985     /**
24986      * Executes a Midas editor command on the editor document and performs necessary focus and
24987      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24988      * @param {String} cmd The Midas command
24989      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24990      */
24991     relayCmd : function(cmd, value){
24992         this.win.focus();
24993         this.execCmd(cmd, value);
24994         this.owner.fireEvent('editorevent', this);
24995         //this.updateToolbar();
24996         this.owner.deferFocus();
24997     },
24998
24999     /**
25000      * Executes a Midas editor command directly on the editor document.
25001      * For visual commands, you should use {@link #relayCmd} instead.
25002      * <b>This should only be called after the editor is initialized.</b>
25003      * @param {String} cmd The Midas command
25004      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25005      */
25006     execCmd : function(cmd, value){
25007         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25008         this.syncValue();
25009     },
25010  
25011  
25012    
25013     /**
25014      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25015      * to insert tRoo.
25016      * @param {String} text | dom node.. 
25017      */
25018     insertAtCursor : function(text)
25019     {
25020         
25021         if(!this.activated){
25022             return;
25023         }
25024         /*
25025         if(Roo.isIE){
25026             this.win.focus();
25027             var r = this.doc.selection.createRange();
25028             if(r){
25029                 r.collapse(true);
25030                 r.pasteHTML(text);
25031                 this.syncValue();
25032                 this.deferFocus();
25033             
25034             }
25035             return;
25036         }
25037         */
25038         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25039             this.win.focus();
25040             
25041             
25042             // from jquery ui (MIT licenced)
25043             var range, node;
25044             var win = this.win;
25045             
25046             if (win.getSelection && win.getSelection().getRangeAt) {
25047                 range = win.getSelection().getRangeAt(0);
25048                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25049                 range.insertNode(node);
25050             } else if (win.document.selection && win.document.selection.createRange) {
25051                 // no firefox support
25052                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25053                 win.document.selection.createRange().pasteHTML(txt);
25054             } else {
25055                 // no firefox support
25056                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25057                 this.execCmd('InsertHTML', txt);
25058             } 
25059             
25060             this.syncValue();
25061             
25062             this.deferFocus();
25063         }
25064     },
25065  // private
25066     mozKeyPress : function(e){
25067         if(e.ctrlKey){
25068             var c = e.getCharCode(), cmd;
25069           
25070             if(c > 0){
25071                 c = String.fromCharCode(c).toLowerCase();
25072                 switch(c){
25073                     case 'b':
25074                         cmd = 'bold';
25075                         break;
25076                     case 'i':
25077                         cmd = 'italic';
25078                         break;
25079                     
25080                     case 'u':
25081                         cmd = 'underline';
25082                         break;
25083                     
25084                     case 'v':
25085                         this.cleanUpPaste.defer(100, this);
25086                         return;
25087                         
25088                 }
25089                 if(cmd){
25090                     this.win.focus();
25091                     this.execCmd(cmd);
25092                     this.deferFocus();
25093                     e.preventDefault();
25094                 }
25095                 
25096             }
25097         }
25098     },
25099
25100     // private
25101     fixKeys : function(){ // load time branching for fastest keydown performance
25102         if(Roo.isIE){
25103             return function(e){
25104                 var k = e.getKey(), r;
25105                 if(k == e.TAB){
25106                     e.stopEvent();
25107                     r = this.doc.selection.createRange();
25108                     if(r){
25109                         r.collapse(true);
25110                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25111                         this.deferFocus();
25112                     }
25113                     return;
25114                 }
25115                 
25116                 if(k == e.ENTER){
25117                     r = this.doc.selection.createRange();
25118                     if(r){
25119                         var target = r.parentElement();
25120                         if(!target || target.tagName.toLowerCase() != 'li'){
25121                             e.stopEvent();
25122                             r.pasteHTML('<br />');
25123                             r.collapse(false);
25124                             r.select();
25125                         }
25126                     }
25127                 }
25128                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25129                     this.cleanUpPaste.defer(100, this);
25130                     return;
25131                 }
25132                 
25133                 
25134             };
25135         }else if(Roo.isOpera){
25136             return function(e){
25137                 var k = e.getKey();
25138                 if(k == e.TAB){
25139                     e.stopEvent();
25140                     this.win.focus();
25141                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25142                     this.deferFocus();
25143                 }
25144                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25145                     this.cleanUpPaste.defer(100, this);
25146                     return;
25147                 }
25148                 
25149             };
25150         }else if(Roo.isSafari){
25151             return function(e){
25152                 var k = e.getKey();
25153                 
25154                 if(k == e.TAB){
25155                     e.stopEvent();
25156                     this.execCmd('InsertText','\t');
25157                     this.deferFocus();
25158                     return;
25159                 }
25160                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25161                     this.cleanUpPaste.defer(100, this);
25162                     return;
25163                 }
25164                 
25165              };
25166         }
25167     }(),
25168     
25169     getAllAncestors: function()
25170     {
25171         var p = this.getSelectedNode();
25172         var a = [];
25173         if (!p) {
25174             a.push(p); // push blank onto stack..
25175             p = this.getParentElement();
25176         }
25177         
25178         
25179         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25180             a.push(p);
25181             p = p.parentNode;
25182         }
25183         a.push(this.doc.body);
25184         return a;
25185     },
25186     lastSel : false,
25187     lastSelNode : false,
25188     
25189     
25190     getSelection : function() 
25191     {
25192         this.assignDocWin();
25193         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25194     },
25195     
25196     getSelectedNode: function() 
25197     {
25198         // this may only work on Gecko!!!
25199         
25200         // should we cache this!!!!
25201         
25202         
25203         
25204          
25205         var range = this.createRange(this.getSelection()).cloneRange();
25206         
25207         if (Roo.isIE) {
25208             var parent = range.parentElement();
25209             while (true) {
25210                 var testRange = range.duplicate();
25211                 testRange.moveToElementText(parent);
25212                 if (testRange.inRange(range)) {
25213                     break;
25214                 }
25215                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25216                     break;
25217                 }
25218                 parent = parent.parentElement;
25219             }
25220             return parent;
25221         }
25222         
25223         // is ancestor a text element.
25224         var ac =  range.commonAncestorContainer;
25225         if (ac.nodeType == 3) {
25226             ac = ac.parentNode;
25227         }
25228         
25229         var ar = ac.childNodes;
25230          
25231         var nodes = [];
25232         var other_nodes = [];
25233         var has_other_nodes = false;
25234         for (var i=0;i<ar.length;i++) {
25235             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25236                 continue;
25237             }
25238             // fullly contained node.
25239             
25240             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25241                 nodes.push(ar[i]);
25242                 continue;
25243             }
25244             
25245             // probably selected..
25246             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25247                 other_nodes.push(ar[i]);
25248                 continue;
25249             }
25250             // outer..
25251             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25252                 continue;
25253             }
25254             
25255             
25256             has_other_nodes = true;
25257         }
25258         if (!nodes.length && other_nodes.length) {
25259             nodes= other_nodes;
25260         }
25261         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25262             return false;
25263         }
25264         
25265         return nodes[0];
25266     },
25267     createRange: function(sel)
25268     {
25269         // this has strange effects when using with 
25270         // top toolbar - not sure if it's a great idea.
25271         //this.editor.contentWindow.focus();
25272         if (typeof sel != "undefined") {
25273             try {
25274                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25275             } catch(e) {
25276                 return this.doc.createRange();
25277             }
25278         } else {
25279             return this.doc.createRange();
25280         }
25281     },
25282     getParentElement: function()
25283     {
25284         
25285         this.assignDocWin();
25286         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25287         
25288         var range = this.createRange(sel);
25289          
25290         try {
25291             var p = range.commonAncestorContainer;
25292             while (p.nodeType == 3) { // text node
25293                 p = p.parentNode;
25294             }
25295             return p;
25296         } catch (e) {
25297             return null;
25298         }
25299     
25300     },
25301     /***
25302      *
25303      * Range intersection.. the hard stuff...
25304      *  '-1' = before
25305      *  '0' = hits..
25306      *  '1' = after.
25307      *         [ -- selected range --- ]
25308      *   [fail]                        [fail]
25309      *
25310      *    basically..
25311      *      if end is before start or  hits it. fail.
25312      *      if start is after end or hits it fail.
25313      *
25314      *   if either hits (but other is outside. - then it's not 
25315      *   
25316      *    
25317      **/
25318     
25319     
25320     // @see http://www.thismuchiknow.co.uk/?p=64.
25321     rangeIntersectsNode : function(range, node)
25322     {
25323         var nodeRange = node.ownerDocument.createRange();
25324         try {
25325             nodeRange.selectNode(node);
25326         } catch (e) {
25327             nodeRange.selectNodeContents(node);
25328         }
25329     
25330         var rangeStartRange = range.cloneRange();
25331         rangeStartRange.collapse(true);
25332     
25333         var rangeEndRange = range.cloneRange();
25334         rangeEndRange.collapse(false);
25335     
25336         var nodeStartRange = nodeRange.cloneRange();
25337         nodeStartRange.collapse(true);
25338     
25339         var nodeEndRange = nodeRange.cloneRange();
25340         nodeEndRange.collapse(false);
25341     
25342         return rangeStartRange.compareBoundaryPoints(
25343                  Range.START_TO_START, nodeEndRange) == -1 &&
25344                rangeEndRange.compareBoundaryPoints(
25345                  Range.START_TO_START, nodeStartRange) == 1;
25346         
25347          
25348     },
25349     rangeCompareNode : function(range, node)
25350     {
25351         var nodeRange = node.ownerDocument.createRange();
25352         try {
25353             nodeRange.selectNode(node);
25354         } catch (e) {
25355             nodeRange.selectNodeContents(node);
25356         }
25357         
25358         
25359         range.collapse(true);
25360     
25361         nodeRange.collapse(true);
25362      
25363         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25364         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25365          
25366         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25367         
25368         var nodeIsBefore   =  ss == 1;
25369         var nodeIsAfter    = ee == -1;
25370         
25371         if (nodeIsBefore && nodeIsAfter) {
25372             return 0; // outer
25373         }
25374         if (!nodeIsBefore && nodeIsAfter) {
25375             return 1; //right trailed.
25376         }
25377         
25378         if (nodeIsBefore && !nodeIsAfter) {
25379             return 2;  // left trailed.
25380         }
25381         // fully contined.
25382         return 3;
25383     },
25384
25385     // private? - in a new class?
25386     cleanUpPaste :  function()
25387     {
25388         // cleans up the whole document..
25389         Roo.log('cleanuppaste');
25390         
25391         this.cleanUpChildren(this.doc.body);
25392         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25393         if (clean != this.doc.body.innerHTML) {
25394             this.doc.body.innerHTML = clean;
25395         }
25396         
25397     },
25398     
25399     cleanWordChars : function(input) {// change the chars to hex code
25400         var he = Roo.HtmlEditorCore;
25401         
25402         var output = input;
25403         Roo.each(he.swapCodes, function(sw) { 
25404             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25405             
25406             output = output.replace(swapper, sw[1]);
25407         });
25408         
25409         return output;
25410     },
25411     
25412     
25413     cleanUpChildren : function (n)
25414     {
25415         if (!n.childNodes.length) {
25416             return;
25417         }
25418         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25419            this.cleanUpChild(n.childNodes[i]);
25420         }
25421     },
25422     
25423     
25424         
25425     
25426     cleanUpChild : function (node)
25427     {
25428         var ed = this;
25429         //console.log(node);
25430         if (node.nodeName == "#text") {
25431             // clean up silly Windows -- stuff?
25432             return; 
25433         }
25434         if (node.nodeName == "#comment") {
25435             node.parentNode.removeChild(node);
25436             // clean up silly Windows -- stuff?
25437             return; 
25438         }
25439         var lcname = node.tagName.toLowerCase();
25440         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25441         // whitelist of tags..
25442         
25443         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25444             // remove node.
25445             node.parentNode.removeChild(node);
25446             return;
25447             
25448         }
25449         
25450         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25451         
25452         // spans with no attributes - just remove them..
25453         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25454             remove_keep_children = true;
25455         }
25456         
25457         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25458         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25459         
25460         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25461         //    remove_keep_children = true;
25462         //}
25463         
25464         if (remove_keep_children) {
25465             this.cleanUpChildren(node);
25466             // inserts everything just before this node...
25467             while (node.childNodes.length) {
25468                 var cn = node.childNodes[0];
25469                 node.removeChild(cn);
25470                 node.parentNode.insertBefore(cn, node);
25471             }
25472             node.parentNode.removeChild(node);
25473             return;
25474         }
25475         
25476         if (!node.attributes || !node.attributes.length) {
25477             
25478           
25479             
25480             
25481             this.cleanUpChildren(node);
25482             return;
25483         }
25484         
25485         function cleanAttr(n,v)
25486         {
25487             
25488             if (v.match(/^\./) || v.match(/^\//)) {
25489                 return;
25490             }
25491             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25492                 return;
25493             }
25494             if (v.match(/^#/)) {
25495                 return;
25496             }
25497             if (v.match(/^\{/)) { // allow template editing.
25498                 return;
25499             }
25500 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25501             node.removeAttribute(n);
25502             
25503         }
25504         
25505         var cwhite = this.cwhite;
25506         var cblack = this.cblack;
25507             
25508         function cleanStyle(n,v)
25509         {
25510             if (v.match(/expression/)) { //XSS?? should we even bother..
25511                 node.removeAttribute(n);
25512                 return;
25513             }
25514             
25515             var parts = v.split(/;/);
25516             var clean = [];
25517             
25518             Roo.each(parts, function(p) {
25519                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25520                 if (!p.length) {
25521                     return true;
25522                 }
25523                 var l = p.split(':').shift().replace(/\s+/g,'');
25524                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25525                 
25526                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25527 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25528                     //node.removeAttribute(n);
25529                     return true;
25530                 }
25531                 //Roo.log()
25532                 // only allow 'c whitelisted system attributes'
25533                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25534 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25535                     //node.removeAttribute(n);
25536                     return true;
25537                 }
25538                 
25539                 
25540                  
25541                 
25542                 clean.push(p);
25543                 return true;
25544             });
25545             if (clean.length) { 
25546                 node.setAttribute(n, clean.join(';'));
25547             } else {
25548                 node.removeAttribute(n);
25549             }
25550             
25551         }
25552         
25553         
25554         for (var i = node.attributes.length-1; i > -1 ; i--) {
25555             var a = node.attributes[i];
25556             //console.log(a);
25557             
25558             if (a.name.toLowerCase().substr(0,2)=='on')  {
25559                 node.removeAttribute(a.name);
25560                 continue;
25561             }
25562             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25563                 node.removeAttribute(a.name);
25564                 continue;
25565             }
25566             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25567                 cleanAttr(a.name,a.value); // fixme..
25568                 continue;
25569             }
25570             if (a.name == 'style') {
25571                 cleanStyle(a.name,a.value);
25572                 continue;
25573             }
25574             /// clean up MS crap..
25575             // tecnically this should be a list of valid class'es..
25576             
25577             
25578             if (a.name == 'class') {
25579                 if (a.value.match(/^Mso/)) {
25580                     node.removeAttribute('class');
25581                 }
25582                 
25583                 if (a.value.match(/^body$/)) {
25584                     node.removeAttribute('class');
25585                 }
25586                 continue;
25587             }
25588             
25589             // style cleanup!?
25590             // class cleanup?
25591             
25592         }
25593         
25594         
25595         this.cleanUpChildren(node);
25596         
25597         
25598     },
25599     
25600     /**
25601      * Clean up MS wordisms...
25602      */
25603     cleanWord : function(node)
25604     {
25605         if (!node) {
25606             this.cleanWord(this.doc.body);
25607             return;
25608         }
25609         
25610         if(
25611                 node.nodeName == 'SPAN' &&
25612                 !node.hasAttributes() &&
25613                 node.childNodes.length == 1 &&
25614                 node.firstChild.nodeName == "#text"  
25615         ) {
25616             var textNode = node.firstChild;
25617             node.removeChild(textNode);
25618             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25619                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25620             }
25621             node.parentNode.insertBefore(textNode, node);
25622             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25623                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25624             }
25625             node.parentNode.removeChild(node);
25626         }
25627         
25628         if (node.nodeName == "#text") {
25629             // clean up silly Windows -- stuff?
25630             return; 
25631         }
25632         if (node.nodeName == "#comment") {
25633             node.parentNode.removeChild(node);
25634             // clean up silly Windows -- stuff?
25635             return; 
25636         }
25637         
25638         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25639             node.parentNode.removeChild(node);
25640             return;
25641         }
25642         //Roo.log(node.tagName);
25643         // remove - but keep children..
25644         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25645             //Roo.log('-- removed');
25646             while (node.childNodes.length) {
25647                 var cn = node.childNodes[0];
25648                 node.removeChild(cn);
25649                 node.parentNode.insertBefore(cn, node);
25650                 // move node to parent - and clean it..
25651                 this.cleanWord(cn);
25652             }
25653             node.parentNode.removeChild(node);
25654             /// no need to iterate chidlren = it's got none..
25655             //this.iterateChildren(node, this.cleanWord);
25656             return;
25657         }
25658         // clean styles
25659         if (node.className.length) {
25660             
25661             var cn = node.className.split(/\W+/);
25662             var cna = [];
25663             Roo.each(cn, function(cls) {
25664                 if (cls.match(/Mso[a-zA-Z]+/)) {
25665                     return;
25666                 }
25667                 cna.push(cls);
25668             });
25669             node.className = cna.length ? cna.join(' ') : '';
25670             if (!cna.length) {
25671                 node.removeAttribute("class");
25672             }
25673         }
25674         
25675         if (node.hasAttribute("lang")) {
25676             node.removeAttribute("lang");
25677         }
25678         
25679         if (node.hasAttribute("style")) {
25680             
25681             var styles = node.getAttribute("style").split(";");
25682             var nstyle = [];
25683             Roo.each(styles, function(s) {
25684                 if (!s.match(/:/)) {
25685                     return;
25686                 }
25687                 var kv = s.split(":");
25688                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25689                     return;
25690                 }
25691                 // what ever is left... we allow.
25692                 nstyle.push(s);
25693             });
25694             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25695             if (!nstyle.length) {
25696                 node.removeAttribute('style');
25697             }
25698         }
25699         this.iterateChildren(node, this.cleanWord);
25700         
25701         
25702         
25703     },
25704     /**
25705      * iterateChildren of a Node, calling fn each time, using this as the scole..
25706      * @param {DomNode} node node to iterate children of.
25707      * @param {Function} fn method of this class to call on each item.
25708      */
25709     iterateChildren : function(node, fn)
25710     {
25711         if (!node.childNodes.length) {
25712                 return;
25713         }
25714         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25715            fn.call(this, node.childNodes[i])
25716         }
25717     },
25718     
25719     
25720     /**
25721      * cleanTableWidths.
25722      *
25723      * Quite often pasting from word etc.. results in tables with column and widths.
25724      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25725      *
25726      */
25727     cleanTableWidths : function(node)
25728     {
25729          
25730          
25731         if (!node) {
25732             this.cleanTableWidths(this.doc.body);
25733             return;
25734         }
25735         
25736         // ignore list...
25737         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25738             return; 
25739         }
25740         Roo.log(node.tagName);
25741         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25742             this.iterateChildren(node, this.cleanTableWidths);
25743             return;
25744         }
25745         if (node.hasAttribute('width')) {
25746             node.removeAttribute('width');
25747         }
25748         
25749          
25750         if (node.hasAttribute("style")) {
25751             // pretty basic...
25752             
25753             var styles = node.getAttribute("style").split(";");
25754             var nstyle = [];
25755             Roo.each(styles, function(s) {
25756                 if (!s.match(/:/)) {
25757                     return;
25758                 }
25759                 var kv = s.split(":");
25760                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25761                     return;
25762                 }
25763                 // what ever is left... we allow.
25764                 nstyle.push(s);
25765             });
25766             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25767             if (!nstyle.length) {
25768                 node.removeAttribute('style');
25769             }
25770         }
25771         
25772         this.iterateChildren(node, this.cleanTableWidths);
25773         
25774         
25775     },
25776     
25777     
25778     
25779     
25780     domToHTML : function(currentElement, depth, nopadtext) {
25781         
25782         depth = depth || 0;
25783         nopadtext = nopadtext || false;
25784     
25785         if (!currentElement) {
25786             return this.domToHTML(this.doc.body);
25787         }
25788         
25789         //Roo.log(currentElement);
25790         var j;
25791         var allText = false;
25792         var nodeName = currentElement.nodeName;
25793         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25794         
25795         if  (nodeName == '#text') {
25796             
25797             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25798         }
25799         
25800         
25801         var ret = '';
25802         if (nodeName != 'BODY') {
25803              
25804             var i = 0;
25805             // Prints the node tagName, such as <A>, <IMG>, etc
25806             if (tagName) {
25807                 var attr = [];
25808                 for(i = 0; i < currentElement.attributes.length;i++) {
25809                     // quoting?
25810                     var aname = currentElement.attributes.item(i).name;
25811                     if (!currentElement.attributes.item(i).value.length) {
25812                         continue;
25813                     }
25814                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25815                 }
25816                 
25817                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25818             } 
25819             else {
25820                 
25821                 // eack
25822             }
25823         } else {
25824             tagName = false;
25825         }
25826         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25827             return ret;
25828         }
25829         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25830             nopadtext = true;
25831         }
25832         
25833         
25834         // Traverse the tree
25835         i = 0;
25836         var currentElementChild = currentElement.childNodes.item(i);
25837         var allText = true;
25838         var innerHTML  = '';
25839         lastnode = '';
25840         while (currentElementChild) {
25841             // Formatting code (indent the tree so it looks nice on the screen)
25842             var nopad = nopadtext;
25843             if (lastnode == 'SPAN') {
25844                 nopad  = true;
25845             }
25846             // text
25847             if  (currentElementChild.nodeName == '#text') {
25848                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25849                 toadd = nopadtext ? toadd : toadd.trim();
25850                 if (!nopad && toadd.length > 80) {
25851                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25852                 }
25853                 innerHTML  += toadd;
25854                 
25855                 i++;
25856                 currentElementChild = currentElement.childNodes.item(i);
25857                 lastNode = '';
25858                 continue;
25859             }
25860             allText = false;
25861             
25862             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25863                 
25864             // Recursively traverse the tree structure of the child node
25865             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25866             lastnode = currentElementChild.nodeName;
25867             i++;
25868             currentElementChild=currentElement.childNodes.item(i);
25869         }
25870         
25871         ret += innerHTML;
25872         
25873         if (!allText) {
25874                 // The remaining code is mostly for formatting the tree
25875             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25876         }
25877         
25878         
25879         if (tagName) {
25880             ret+= "</"+tagName+">";
25881         }
25882         return ret;
25883         
25884     },
25885         
25886     applyBlacklists : function()
25887     {
25888         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25889         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25890         
25891         this.white = [];
25892         this.black = [];
25893         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25894             if (b.indexOf(tag) > -1) {
25895                 return;
25896             }
25897             this.white.push(tag);
25898             
25899         }, this);
25900         
25901         Roo.each(w, function(tag) {
25902             if (b.indexOf(tag) > -1) {
25903                 return;
25904             }
25905             if (this.white.indexOf(tag) > -1) {
25906                 return;
25907             }
25908             this.white.push(tag);
25909             
25910         }, this);
25911         
25912         
25913         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25914             if (w.indexOf(tag) > -1) {
25915                 return;
25916             }
25917             this.black.push(tag);
25918             
25919         }, this);
25920         
25921         Roo.each(b, function(tag) {
25922             if (w.indexOf(tag) > -1) {
25923                 return;
25924             }
25925             if (this.black.indexOf(tag) > -1) {
25926                 return;
25927             }
25928             this.black.push(tag);
25929             
25930         }, this);
25931         
25932         
25933         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25934         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25935         
25936         this.cwhite = [];
25937         this.cblack = [];
25938         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25939             if (b.indexOf(tag) > -1) {
25940                 return;
25941             }
25942             this.cwhite.push(tag);
25943             
25944         }, this);
25945         
25946         Roo.each(w, function(tag) {
25947             if (b.indexOf(tag) > -1) {
25948                 return;
25949             }
25950             if (this.cwhite.indexOf(tag) > -1) {
25951                 return;
25952             }
25953             this.cwhite.push(tag);
25954             
25955         }, this);
25956         
25957         
25958         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25959             if (w.indexOf(tag) > -1) {
25960                 return;
25961             }
25962             this.cblack.push(tag);
25963             
25964         }, this);
25965         
25966         Roo.each(b, function(tag) {
25967             if (w.indexOf(tag) > -1) {
25968                 return;
25969             }
25970             if (this.cblack.indexOf(tag) > -1) {
25971                 return;
25972             }
25973             this.cblack.push(tag);
25974             
25975         }, this);
25976     },
25977     
25978     setStylesheets : function(stylesheets)
25979     {
25980         if(typeof(stylesheets) == 'string'){
25981             Roo.get(this.iframe.contentDocument.head).createChild({
25982                 tag : 'link',
25983                 rel : 'stylesheet',
25984                 type : 'text/css',
25985                 href : stylesheets
25986             });
25987             
25988             return;
25989         }
25990         var _this = this;
25991      
25992         Roo.each(stylesheets, function(s) {
25993             if(!s.length){
25994                 return;
25995             }
25996             
25997             Roo.get(_this.iframe.contentDocument.head).createChild({
25998                 tag : 'link',
25999                 rel : 'stylesheet',
26000                 type : 'text/css',
26001                 href : s
26002             });
26003         });
26004
26005         
26006     },
26007     
26008     removeStylesheets : function()
26009     {
26010         var _this = this;
26011         
26012         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26013             s.remove();
26014         });
26015     },
26016     
26017     setStyle : function(style)
26018     {
26019         Roo.get(this.iframe.contentDocument.head).createChild({
26020             tag : 'style',
26021             type : 'text/css',
26022             html : style
26023         });
26024
26025         return;
26026     }
26027     
26028     // hide stuff that is not compatible
26029     /**
26030      * @event blur
26031      * @hide
26032      */
26033     /**
26034      * @event change
26035      * @hide
26036      */
26037     /**
26038      * @event focus
26039      * @hide
26040      */
26041     /**
26042      * @event specialkey
26043      * @hide
26044      */
26045     /**
26046      * @cfg {String} fieldClass @hide
26047      */
26048     /**
26049      * @cfg {String} focusClass @hide
26050      */
26051     /**
26052      * @cfg {String} autoCreate @hide
26053      */
26054     /**
26055      * @cfg {String} inputType @hide
26056      */
26057     /**
26058      * @cfg {String} invalidClass @hide
26059      */
26060     /**
26061      * @cfg {String} invalidText @hide
26062      */
26063     /**
26064      * @cfg {String} msgFx @hide
26065      */
26066     /**
26067      * @cfg {String} validateOnBlur @hide
26068      */
26069 });
26070
26071 Roo.HtmlEditorCore.white = [
26072         'area', 'br', 'img', 'input', 'hr', 'wbr',
26073         
26074        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26075        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26076        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26077        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26078        'table',   'ul',         'xmp', 
26079        
26080        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26081       'thead',   'tr', 
26082      
26083       'dir', 'menu', 'ol', 'ul', 'dl',
26084        
26085       'embed',  'object'
26086 ];
26087
26088
26089 Roo.HtmlEditorCore.black = [
26090     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26091         'applet', // 
26092         'base',   'basefont', 'bgsound', 'blink',  'body', 
26093         'frame',  'frameset', 'head',    'html',   'ilayer', 
26094         'iframe', 'layer',  'link',     'meta',    'object',   
26095         'script', 'style' ,'title',  'xml' // clean later..
26096 ];
26097 Roo.HtmlEditorCore.clean = [
26098     'script', 'style', 'title', 'xml'
26099 ];
26100 Roo.HtmlEditorCore.remove = [
26101     'font'
26102 ];
26103 // attributes..
26104
26105 Roo.HtmlEditorCore.ablack = [
26106     'on'
26107 ];
26108     
26109 Roo.HtmlEditorCore.aclean = [ 
26110     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26111 ];
26112
26113 // protocols..
26114 Roo.HtmlEditorCore.pwhite= [
26115         'http',  'https',  'mailto'
26116 ];
26117
26118 // white listed style attributes.
26119 Roo.HtmlEditorCore.cwhite= [
26120       //  'text-align', /// default is to allow most things..
26121       
26122          
26123 //        'font-size'//??
26124 ];
26125
26126 // black listed style attributes.
26127 Roo.HtmlEditorCore.cblack= [
26128       //  'font-size' -- this can be set by the project 
26129 ];
26130
26131
26132 Roo.HtmlEditorCore.swapCodes   =[ 
26133     [    8211, "&#8211;" ], 
26134     [    8212, "&#8212;" ], 
26135     [    8216,  "'" ],  
26136     [    8217, "'" ],  
26137     [    8220, '"' ],  
26138     [    8221, '"' ],  
26139     [    8226, "*" ],  
26140     [    8230, "..." ]
26141 ]; 
26142
26143     /*
26144  * - LGPL
26145  *
26146  * HtmlEditor
26147  * 
26148  */
26149
26150 /**
26151  * @class Roo.bootstrap.HtmlEditor
26152  * @extends Roo.bootstrap.TextArea
26153  * Bootstrap HtmlEditor class
26154
26155  * @constructor
26156  * Create a new HtmlEditor
26157  * @param {Object} config The config object
26158  */
26159
26160 Roo.bootstrap.HtmlEditor = function(config){
26161     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26162     if (!this.toolbars) {
26163         this.toolbars = [];
26164     }
26165     
26166     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26167     this.addEvents({
26168             /**
26169              * @event initialize
26170              * Fires when the editor is fully initialized (including the iframe)
26171              * @param {HtmlEditor} this
26172              */
26173             initialize: true,
26174             /**
26175              * @event activate
26176              * Fires when the editor is first receives the focus. Any insertion must wait
26177              * until after this event.
26178              * @param {HtmlEditor} this
26179              */
26180             activate: true,
26181              /**
26182              * @event beforesync
26183              * Fires before the textarea is updated with content from the editor iframe. Return false
26184              * to cancel the sync.
26185              * @param {HtmlEditor} this
26186              * @param {String} html
26187              */
26188             beforesync: true,
26189              /**
26190              * @event beforepush
26191              * Fires before the iframe editor is updated with content from the textarea. Return false
26192              * to cancel the push.
26193              * @param {HtmlEditor} this
26194              * @param {String} html
26195              */
26196             beforepush: true,
26197              /**
26198              * @event sync
26199              * Fires when the textarea is updated with content from the editor iframe.
26200              * @param {HtmlEditor} this
26201              * @param {String} html
26202              */
26203             sync: true,
26204              /**
26205              * @event push
26206              * Fires when the iframe editor is updated with content from the textarea.
26207              * @param {HtmlEditor} this
26208              * @param {String} html
26209              */
26210             push: true,
26211              /**
26212              * @event editmodechange
26213              * Fires when the editor switches edit modes
26214              * @param {HtmlEditor} this
26215              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26216              */
26217             editmodechange: true,
26218             /**
26219              * @event editorevent
26220              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26221              * @param {HtmlEditor} this
26222              */
26223             editorevent: true,
26224             /**
26225              * @event firstfocus
26226              * Fires when on first focus - needed by toolbars..
26227              * @param {HtmlEditor} this
26228              */
26229             firstfocus: true,
26230             /**
26231              * @event autosave
26232              * Auto save the htmlEditor value as a file into Events
26233              * @param {HtmlEditor} this
26234              */
26235             autosave: true,
26236             /**
26237              * @event savedpreview
26238              * preview the saved version of htmlEditor
26239              * @param {HtmlEditor} this
26240              */
26241             savedpreview: true
26242         });
26243 };
26244
26245
26246 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26247     
26248     
26249       /**
26250      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26251      */
26252     toolbars : false,
26253     
26254      /**
26255     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26256     */
26257     btns : [],
26258    
26259      /**
26260      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26261      *                        Roo.resizable.
26262      */
26263     resizable : false,
26264      /**
26265      * @cfg {Number} height (in pixels)
26266      */   
26267     height: 300,
26268    /**
26269      * @cfg {Number} width (in pixels)
26270      */   
26271     width: false,
26272     
26273     /**
26274      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26275      * 
26276      */
26277     stylesheets: false,
26278     
26279     // id of frame..
26280     frameId: false,
26281     
26282     // private properties
26283     validationEvent : false,
26284     deferHeight: true,
26285     initialized : false,
26286     activated : false,
26287     
26288     onFocus : Roo.emptyFn,
26289     iframePad:3,
26290     hideMode:'offsets',
26291     
26292     tbContainer : false,
26293     
26294     bodyCls : '',
26295     
26296     toolbarContainer :function() {
26297         return this.wrap.select('.x-html-editor-tb',true).first();
26298     },
26299
26300     /**
26301      * Protected method that will not generally be called directly. It
26302      * is called when the editor creates its toolbar. Override this method if you need to
26303      * add custom toolbar buttons.
26304      * @param {HtmlEditor} editor
26305      */
26306     createToolbar : function(){
26307         Roo.log('renewing');
26308         Roo.log("create toolbars");
26309         
26310         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26311         this.toolbars[0].render(this.toolbarContainer());
26312         
26313         return;
26314         
26315 //        if (!editor.toolbars || !editor.toolbars.length) {
26316 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26317 //        }
26318 //        
26319 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26320 //            editor.toolbars[i] = Roo.factory(
26321 //                    typeof(editor.toolbars[i]) == 'string' ?
26322 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26323 //                Roo.bootstrap.HtmlEditor);
26324 //            editor.toolbars[i].init(editor);
26325 //        }
26326     },
26327
26328      
26329     // private
26330     onRender : function(ct, position)
26331     {
26332        // Roo.log("Call onRender: " + this.xtype);
26333         var _t = this;
26334         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26335       
26336         this.wrap = this.inputEl().wrap({
26337             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26338         });
26339         
26340         this.editorcore.onRender(ct, position);
26341          
26342         if (this.resizable) {
26343             this.resizeEl = new Roo.Resizable(this.wrap, {
26344                 pinned : true,
26345                 wrap: true,
26346                 dynamic : true,
26347                 minHeight : this.height,
26348                 height: this.height,
26349                 handles : this.resizable,
26350                 width: this.width,
26351                 listeners : {
26352                     resize : function(r, w, h) {
26353                         _t.onResize(w,h); // -something
26354                     }
26355                 }
26356             });
26357             
26358         }
26359         this.createToolbar(this);
26360        
26361         
26362         if(!this.width && this.resizable){
26363             this.setSize(this.wrap.getSize());
26364         }
26365         if (this.resizeEl) {
26366             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26367             // should trigger onReize..
26368         }
26369         
26370     },
26371
26372     // private
26373     onResize : function(w, h)
26374     {
26375         Roo.log('resize: ' +w + ',' + h );
26376         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26377         var ew = false;
26378         var eh = false;
26379         
26380         if(this.inputEl() ){
26381             if(typeof w == 'number'){
26382                 var aw = w - this.wrap.getFrameWidth('lr');
26383                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26384                 ew = aw;
26385             }
26386             if(typeof h == 'number'){
26387                  var tbh = -11;  // fixme it needs to tool bar size!
26388                 for (var i =0; i < this.toolbars.length;i++) {
26389                     // fixme - ask toolbars for heights?
26390                     tbh += this.toolbars[i].el.getHeight();
26391                     //if (this.toolbars[i].footer) {
26392                     //    tbh += this.toolbars[i].footer.el.getHeight();
26393                     //}
26394                 }
26395               
26396                 
26397                 
26398                 
26399                 
26400                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26401                 ah -= 5; // knock a few pixes off for look..
26402                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26403                 var eh = ah;
26404             }
26405         }
26406         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26407         this.editorcore.onResize(ew,eh);
26408         
26409     },
26410
26411     /**
26412      * Toggles the editor between standard and source edit mode.
26413      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26414      */
26415     toggleSourceEdit : function(sourceEditMode)
26416     {
26417         this.editorcore.toggleSourceEdit(sourceEditMode);
26418         
26419         if(this.editorcore.sourceEditMode){
26420             Roo.log('editor - showing textarea');
26421             
26422 //            Roo.log('in');
26423 //            Roo.log(this.syncValue());
26424             this.syncValue();
26425             this.inputEl().removeClass(['hide', 'x-hidden']);
26426             this.inputEl().dom.removeAttribute('tabIndex');
26427             this.inputEl().focus();
26428         }else{
26429             Roo.log('editor - hiding textarea');
26430 //            Roo.log('out')
26431 //            Roo.log(this.pushValue()); 
26432             this.pushValue();
26433             
26434             this.inputEl().addClass(['hide', 'x-hidden']);
26435             this.inputEl().dom.setAttribute('tabIndex', -1);
26436             //this.deferFocus();
26437         }
26438          
26439         if(this.resizable){
26440             this.setSize(this.wrap.getSize());
26441         }
26442         
26443         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26444     },
26445  
26446     // private (for BoxComponent)
26447     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26448
26449     // private (for BoxComponent)
26450     getResizeEl : function(){
26451         return this.wrap;
26452     },
26453
26454     // private (for BoxComponent)
26455     getPositionEl : function(){
26456         return this.wrap;
26457     },
26458
26459     // private
26460     initEvents : function(){
26461         this.originalValue = this.getValue();
26462     },
26463
26464 //    /**
26465 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26466 //     * @method
26467 //     */
26468 //    markInvalid : Roo.emptyFn,
26469 //    /**
26470 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26471 //     * @method
26472 //     */
26473 //    clearInvalid : Roo.emptyFn,
26474
26475     setValue : function(v){
26476         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26477         this.editorcore.pushValue();
26478     },
26479
26480      
26481     // private
26482     deferFocus : function(){
26483         this.focus.defer(10, this);
26484     },
26485
26486     // doc'ed in Field
26487     focus : function(){
26488         this.editorcore.focus();
26489         
26490     },
26491       
26492
26493     // private
26494     onDestroy : function(){
26495         
26496         
26497         
26498         if(this.rendered){
26499             
26500             for (var i =0; i < this.toolbars.length;i++) {
26501                 // fixme - ask toolbars for heights?
26502                 this.toolbars[i].onDestroy();
26503             }
26504             
26505             this.wrap.dom.innerHTML = '';
26506             this.wrap.remove();
26507         }
26508     },
26509
26510     // private
26511     onFirstFocus : function(){
26512         //Roo.log("onFirstFocus");
26513         this.editorcore.onFirstFocus();
26514          for (var i =0; i < this.toolbars.length;i++) {
26515             this.toolbars[i].onFirstFocus();
26516         }
26517         
26518     },
26519     
26520     // private
26521     syncValue : function()
26522     {   
26523         this.editorcore.syncValue();
26524     },
26525     
26526     pushValue : function()
26527     {   
26528         this.editorcore.pushValue();
26529     }
26530      
26531     
26532     // hide stuff that is not compatible
26533     /**
26534      * @event blur
26535      * @hide
26536      */
26537     /**
26538      * @event change
26539      * @hide
26540      */
26541     /**
26542      * @event focus
26543      * @hide
26544      */
26545     /**
26546      * @event specialkey
26547      * @hide
26548      */
26549     /**
26550      * @cfg {String} fieldClass @hide
26551      */
26552     /**
26553      * @cfg {String} focusClass @hide
26554      */
26555     /**
26556      * @cfg {String} autoCreate @hide
26557      */
26558     /**
26559      * @cfg {String} inputType @hide
26560      */
26561      
26562     /**
26563      * @cfg {String} invalidText @hide
26564      */
26565     /**
26566      * @cfg {String} msgFx @hide
26567      */
26568     /**
26569      * @cfg {String} validateOnBlur @hide
26570      */
26571 });
26572  
26573     
26574    
26575    
26576    
26577       
26578 Roo.namespace('Roo.bootstrap.htmleditor');
26579 /**
26580  * @class Roo.bootstrap.HtmlEditorToolbar1
26581  * Basic Toolbar
26582  * 
26583  * @example
26584  * Usage:
26585  *
26586  new Roo.bootstrap.HtmlEditor({
26587     ....
26588     toolbars : [
26589         new Roo.bootstrap.HtmlEditorToolbar1({
26590             disable : { fonts: 1 , format: 1, ..., ... , ...],
26591             btns : [ .... ]
26592         })
26593     }
26594      
26595  * 
26596  * @cfg {Object} disable List of elements to disable..
26597  * @cfg {Array} btns List of additional buttons.
26598  * 
26599  * 
26600  * NEEDS Extra CSS? 
26601  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26602  */
26603  
26604 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26605 {
26606     
26607     Roo.apply(this, config);
26608     
26609     // default disabled, based on 'good practice'..
26610     this.disable = this.disable || {};
26611     Roo.applyIf(this.disable, {
26612         fontSize : true,
26613         colors : true,
26614         specialElements : true
26615     });
26616     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26617     
26618     this.editor = config.editor;
26619     this.editorcore = config.editor.editorcore;
26620     
26621     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26622     
26623     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26624     // dont call parent... till later.
26625 }
26626 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26627      
26628     bar : true,
26629     
26630     editor : false,
26631     editorcore : false,
26632     
26633     
26634     formats : [
26635         "p" ,  
26636         "h1","h2","h3","h4","h5","h6", 
26637         "pre", "code", 
26638         "abbr", "acronym", "address", "cite", "samp", "var",
26639         'div','span'
26640     ],
26641     
26642     onRender : function(ct, position)
26643     {
26644        // Roo.log("Call onRender: " + this.xtype);
26645         
26646        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26647        Roo.log(this.el);
26648        this.el.dom.style.marginBottom = '0';
26649        var _this = this;
26650        var editorcore = this.editorcore;
26651        var editor= this.editor;
26652        
26653        var children = [];
26654        var btn = function(id,cmd , toggle, handler, html){
26655        
26656             var  event = toggle ? 'toggle' : 'click';
26657        
26658             var a = {
26659                 size : 'sm',
26660                 xtype: 'Button',
26661                 xns: Roo.bootstrap,
26662                 //glyphicon : id,
26663                 fa: id,
26664                 cmd : id || cmd,
26665                 enableToggle:toggle !== false,
26666                 html : html || '',
26667                 pressed : toggle ? false : null,
26668                 listeners : {}
26669             };
26670             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26671                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26672             };
26673             children.push(a);
26674             return a;
26675        }
26676        
26677     //    var cb_box = function...
26678         
26679         var style = {
26680                 xtype: 'Button',
26681                 size : 'sm',
26682                 xns: Roo.bootstrap,
26683                 fa : 'font',
26684                 //html : 'submit'
26685                 menu : {
26686                     xtype: 'Menu',
26687                     xns: Roo.bootstrap,
26688                     items:  []
26689                 }
26690         };
26691         Roo.each(this.formats, function(f) {
26692             style.menu.items.push({
26693                 xtype :'MenuItem',
26694                 xns: Roo.bootstrap,
26695                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26696                 tagname : f,
26697                 listeners : {
26698                     click : function()
26699                     {
26700                         editorcore.insertTag(this.tagname);
26701                         editor.focus();
26702                     }
26703                 }
26704                 
26705             });
26706         });
26707         children.push(style);   
26708         
26709         btn('bold',false,true);
26710         btn('italic',false,true);
26711         btn('align-left', 'justifyleft',true);
26712         btn('align-center', 'justifycenter',true);
26713         btn('align-right' , 'justifyright',true);
26714         btn('link', false, false, function(btn) {
26715             //Roo.log("create link?");
26716             var url = prompt(this.createLinkText, this.defaultLinkValue);
26717             if(url && url != 'http:/'+'/'){
26718                 this.editorcore.relayCmd('createlink', url);
26719             }
26720         }),
26721         btn('list','insertunorderedlist',true);
26722         btn('pencil', false,true, function(btn){
26723                 Roo.log(this);
26724                 this.toggleSourceEdit(btn.pressed);
26725         });
26726         
26727         if (this.editor.btns.length > 0) {
26728             for (var i = 0; i<this.editor.btns.length; i++) {
26729                 children.push(this.editor.btns[i]);
26730             }
26731         }
26732         
26733         /*
26734         var cog = {
26735                 xtype: 'Button',
26736                 size : 'sm',
26737                 xns: Roo.bootstrap,
26738                 glyphicon : 'cog',
26739                 //html : 'submit'
26740                 menu : {
26741                     xtype: 'Menu',
26742                     xns: Roo.bootstrap,
26743                     items:  []
26744                 }
26745         };
26746         
26747         cog.menu.items.push({
26748             xtype :'MenuItem',
26749             xns: Roo.bootstrap,
26750             html : Clean styles,
26751             tagname : f,
26752             listeners : {
26753                 click : function()
26754                 {
26755                     editorcore.insertTag(this.tagname);
26756                     editor.focus();
26757                 }
26758             }
26759             
26760         });
26761        */
26762         
26763          
26764        this.xtype = 'NavSimplebar';
26765         
26766         for(var i=0;i< children.length;i++) {
26767             
26768             this.buttons.add(this.addxtypeChild(children[i]));
26769             
26770         }
26771         
26772         editor.on('editorevent', this.updateToolbar, this);
26773     },
26774     onBtnClick : function(id)
26775     {
26776        this.editorcore.relayCmd(id);
26777        this.editorcore.focus();
26778     },
26779     
26780     /**
26781      * Protected method that will not generally be called directly. It triggers
26782      * a toolbar update by reading the markup state of the current selection in the editor.
26783      */
26784     updateToolbar: function(){
26785
26786         if(!this.editorcore.activated){
26787             this.editor.onFirstFocus(); // is this neeed?
26788             return;
26789         }
26790
26791         var btns = this.buttons; 
26792         var doc = this.editorcore.doc;
26793         btns.get('bold').setActive(doc.queryCommandState('bold'));
26794         btns.get('italic').setActive(doc.queryCommandState('italic'));
26795         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26796         
26797         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26798         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26799         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26800         
26801         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26802         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26803          /*
26804         
26805         var ans = this.editorcore.getAllAncestors();
26806         if (this.formatCombo) {
26807             
26808             
26809             var store = this.formatCombo.store;
26810             this.formatCombo.setValue("");
26811             for (var i =0; i < ans.length;i++) {
26812                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26813                     // select it..
26814                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26815                     break;
26816                 }
26817             }
26818         }
26819         
26820         
26821         
26822         // hides menus... - so this cant be on a menu...
26823         Roo.bootstrap.MenuMgr.hideAll();
26824         */
26825         Roo.bootstrap.MenuMgr.hideAll();
26826         //this.editorsyncValue();
26827     },
26828     onFirstFocus: function() {
26829         this.buttons.each(function(item){
26830            item.enable();
26831         });
26832     },
26833     toggleSourceEdit : function(sourceEditMode){
26834         
26835           
26836         if(sourceEditMode){
26837             Roo.log("disabling buttons");
26838            this.buttons.each( function(item){
26839                 if(item.cmd != 'pencil'){
26840                     item.disable();
26841                 }
26842             });
26843           
26844         }else{
26845             Roo.log("enabling buttons");
26846             if(this.editorcore.initialized){
26847                 this.buttons.each( function(item){
26848                     item.enable();
26849                 });
26850             }
26851             
26852         }
26853         Roo.log("calling toggole on editor");
26854         // tell the editor that it's been pressed..
26855         this.editor.toggleSourceEdit(sourceEditMode);
26856        
26857     }
26858 });
26859
26860
26861
26862
26863  
26864 /*
26865  * - LGPL
26866  */
26867
26868 /**
26869  * @class Roo.bootstrap.Markdown
26870  * @extends Roo.bootstrap.TextArea
26871  * Bootstrap Showdown editable area
26872  * @cfg {string} content
26873  * 
26874  * @constructor
26875  * Create a new Showdown
26876  */
26877
26878 Roo.bootstrap.Markdown = function(config){
26879     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26880    
26881 };
26882
26883 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26884     
26885     editing :false,
26886     
26887     initEvents : function()
26888     {
26889         
26890         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26891         this.markdownEl = this.el.createChild({
26892             cls : 'roo-markdown-area'
26893         });
26894         this.inputEl().addClass('d-none');
26895         if (this.getValue() == '') {
26896             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26897             
26898         } else {
26899             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26900         }
26901         this.markdownEl.on('click', this.toggleTextEdit, this);
26902         this.on('blur', this.toggleTextEdit, this);
26903         this.on('specialkey', this.resizeTextArea, this);
26904     },
26905     
26906     toggleTextEdit : function()
26907     {
26908         var sh = this.markdownEl.getHeight();
26909         this.inputEl().addClass('d-none');
26910         this.markdownEl.addClass('d-none');
26911         if (!this.editing) {
26912             // show editor?
26913             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26914             this.inputEl().removeClass('d-none');
26915             this.inputEl().focus();
26916             this.editing = true;
26917             return;
26918         }
26919         // show showdown...
26920         this.updateMarkdown();
26921         this.markdownEl.removeClass('d-none');
26922         this.editing = false;
26923         return;
26924     },
26925     updateMarkdown : function()
26926     {
26927         if (this.getValue() == '') {
26928             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26929             return;
26930         }
26931  
26932         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26933     },
26934     
26935     resizeTextArea: function () {
26936         
26937         var sh = 100;
26938         Roo.log([sh, this.getValue().split("\n").length * 30]);
26939         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26940     },
26941     setValue : function(val)
26942     {
26943         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26944         if (!this.editing) {
26945             this.updateMarkdown();
26946         }
26947         
26948     },
26949     focus : function()
26950     {
26951         if (!this.editing) {
26952             this.toggleTextEdit();
26953         }
26954         
26955     }
26956
26957
26958 });
26959 /**
26960  * @class Roo.bootstrap.Table.AbstractSelectionModel
26961  * @extends Roo.util.Observable
26962  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26963  * implemented by descendant classes.  This class should not be directly instantiated.
26964  * @constructor
26965  */
26966 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26967     this.locked = false;
26968     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26969 };
26970
26971
26972 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26973     /** @ignore Called by the grid automatically. Do not call directly. */
26974     init : function(grid){
26975         this.grid = grid;
26976         this.initEvents();
26977     },
26978
26979     /**
26980      * Locks the selections.
26981      */
26982     lock : function(){
26983         this.locked = true;
26984     },
26985
26986     /**
26987      * Unlocks the selections.
26988      */
26989     unlock : function(){
26990         this.locked = false;
26991     },
26992
26993     /**
26994      * Returns true if the selections are locked.
26995      * @return {Boolean}
26996      */
26997     isLocked : function(){
26998         return this.locked;
26999     },
27000     
27001     
27002     initEvents : function ()
27003     {
27004         
27005     }
27006 });
27007 /**
27008  * @extends Roo.bootstrap.Table.AbstractSelectionModel
27009  * @class Roo.bootstrap.Table.RowSelectionModel
27010  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27011  * It supports multiple selections and keyboard selection/navigation. 
27012  * @constructor
27013  * @param {Object} config
27014  */
27015
27016 Roo.bootstrap.Table.RowSelectionModel = function(config){
27017     Roo.apply(this, config);
27018     this.selections = new Roo.util.MixedCollection(false, function(o){
27019         return o.id;
27020     });
27021
27022     this.last = false;
27023     this.lastActive = false;
27024
27025     this.addEvents({
27026         /**
27027              * @event selectionchange
27028              * Fires when the selection changes
27029              * @param {SelectionModel} this
27030              */
27031             "selectionchange" : true,
27032         /**
27033              * @event afterselectionchange
27034              * Fires after the selection changes (eg. by key press or clicking)
27035              * @param {SelectionModel} this
27036              */
27037             "afterselectionchange" : true,
27038         /**
27039              * @event beforerowselect
27040              * Fires when a row is selected being selected, return false to cancel.
27041              * @param {SelectionModel} this
27042              * @param {Number} rowIndex The selected index
27043              * @param {Boolean} keepExisting False if other selections will be cleared
27044              */
27045             "beforerowselect" : true,
27046         /**
27047              * @event rowselect
27048              * Fires when a row is selected.
27049              * @param {SelectionModel} this
27050              * @param {Number} rowIndex The selected index
27051              * @param {Roo.data.Record} r The record
27052              */
27053             "rowselect" : true,
27054         /**
27055              * @event rowdeselect
27056              * Fires when a row is deselected.
27057              * @param {SelectionModel} this
27058              * @param {Number} rowIndex The selected index
27059              */
27060         "rowdeselect" : true
27061     });
27062     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27063     this.locked = false;
27064  };
27065
27066 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27067     /**
27068      * @cfg {Boolean} singleSelect
27069      * True to allow selection of only one row at a time (defaults to false)
27070      */
27071     singleSelect : false,
27072
27073     // private
27074     initEvents : function()
27075     {
27076
27077         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27078         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27079         //}else{ // allow click to work like normal
27080          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27081         //}
27082         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27083         this.grid.on("rowclick", this.handleMouseDown, this);
27084         
27085         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27086             "up" : function(e){
27087                 if(!e.shiftKey){
27088                     this.selectPrevious(e.shiftKey);
27089                 }else if(this.last !== false && this.lastActive !== false){
27090                     var last = this.last;
27091                     this.selectRange(this.last,  this.lastActive-1);
27092                     this.grid.getView().focusRow(this.lastActive);
27093                     if(last !== false){
27094                         this.last = last;
27095                     }
27096                 }else{
27097                     this.selectFirstRow();
27098                 }
27099                 this.fireEvent("afterselectionchange", this);
27100             },
27101             "down" : function(e){
27102                 if(!e.shiftKey){
27103                     this.selectNext(e.shiftKey);
27104                 }else if(this.last !== false && this.lastActive !== false){
27105                     var last = this.last;
27106                     this.selectRange(this.last,  this.lastActive+1);
27107                     this.grid.getView().focusRow(this.lastActive);
27108                     if(last !== false){
27109                         this.last = last;
27110                     }
27111                 }else{
27112                     this.selectFirstRow();
27113                 }
27114                 this.fireEvent("afterselectionchange", this);
27115             },
27116             scope: this
27117         });
27118         this.grid.store.on('load', function(){
27119             this.selections.clear();
27120         },this);
27121         /*
27122         var view = this.grid.view;
27123         view.on("refresh", this.onRefresh, this);
27124         view.on("rowupdated", this.onRowUpdated, this);
27125         view.on("rowremoved", this.onRemove, this);
27126         */
27127     },
27128
27129     // private
27130     onRefresh : function()
27131     {
27132         var ds = this.grid.store, i, v = this.grid.view;
27133         var s = this.selections;
27134         s.each(function(r){
27135             if((i = ds.indexOfId(r.id)) != -1){
27136                 v.onRowSelect(i);
27137             }else{
27138                 s.remove(r);
27139             }
27140         });
27141     },
27142
27143     // private
27144     onRemove : function(v, index, r){
27145         this.selections.remove(r);
27146     },
27147
27148     // private
27149     onRowUpdated : function(v, index, r){
27150         if(this.isSelected(r)){
27151             v.onRowSelect(index);
27152         }
27153     },
27154
27155     /**
27156      * Select records.
27157      * @param {Array} records The records to select
27158      * @param {Boolean} keepExisting (optional) True to keep existing selections
27159      */
27160     selectRecords : function(records, keepExisting)
27161     {
27162         if(!keepExisting){
27163             this.clearSelections();
27164         }
27165             var ds = this.grid.store;
27166         for(var i = 0, len = records.length; i < len; i++){
27167             this.selectRow(ds.indexOf(records[i]), true);
27168         }
27169     },
27170
27171     /**
27172      * Gets the number of selected rows.
27173      * @return {Number}
27174      */
27175     getCount : function(){
27176         return this.selections.length;
27177     },
27178
27179     /**
27180      * Selects the first row in the grid.
27181      */
27182     selectFirstRow : function(){
27183         this.selectRow(0);
27184     },
27185
27186     /**
27187      * Select the last row.
27188      * @param {Boolean} keepExisting (optional) True to keep existing selections
27189      */
27190     selectLastRow : function(keepExisting){
27191         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27192         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27193     },
27194
27195     /**
27196      * Selects the row immediately following the last selected row.
27197      * @param {Boolean} keepExisting (optional) True to keep existing selections
27198      */
27199     selectNext : function(keepExisting)
27200     {
27201             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27202             this.selectRow(this.last+1, keepExisting);
27203             this.grid.getView().focusRow(this.last);
27204         }
27205     },
27206
27207     /**
27208      * Selects the row that precedes the last selected row.
27209      * @param {Boolean} keepExisting (optional) True to keep existing selections
27210      */
27211     selectPrevious : function(keepExisting){
27212         if(this.last){
27213             this.selectRow(this.last-1, keepExisting);
27214             this.grid.getView().focusRow(this.last);
27215         }
27216     },
27217
27218     /**
27219      * Returns the selected records
27220      * @return {Array} Array of selected records
27221      */
27222     getSelections : function(){
27223         return [].concat(this.selections.items);
27224     },
27225
27226     /**
27227      * Returns the first selected record.
27228      * @return {Record}
27229      */
27230     getSelected : function(){
27231         return this.selections.itemAt(0);
27232     },
27233
27234
27235     /**
27236      * Clears all selections.
27237      */
27238     clearSelections : function(fast)
27239     {
27240         if(this.locked) {
27241             return;
27242         }
27243         if(fast !== true){
27244                 var ds = this.grid.store;
27245             var s = this.selections;
27246             s.each(function(r){
27247                 this.deselectRow(ds.indexOfId(r.id));
27248             }, this);
27249             s.clear();
27250         }else{
27251             this.selections.clear();
27252         }
27253         this.last = false;
27254     },
27255
27256
27257     /**
27258      * Selects all rows.
27259      */
27260     selectAll : function(){
27261         if(this.locked) {
27262             return;
27263         }
27264         this.selections.clear();
27265         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27266             this.selectRow(i, true);
27267         }
27268     },
27269
27270     /**
27271      * Returns True if there is a selection.
27272      * @return {Boolean}
27273      */
27274     hasSelection : function(){
27275         return this.selections.length > 0;
27276     },
27277
27278     /**
27279      * Returns True if the specified row is selected.
27280      * @param {Number/Record} record The record or index of the record to check
27281      * @return {Boolean}
27282      */
27283     isSelected : function(index){
27284             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27285         return (r && this.selections.key(r.id) ? true : false);
27286     },
27287
27288     /**
27289      * Returns True if the specified record id is selected.
27290      * @param {String} id The id of record to check
27291      * @return {Boolean}
27292      */
27293     isIdSelected : function(id){
27294         return (this.selections.key(id) ? true : false);
27295     },
27296
27297
27298     // private
27299     handleMouseDBClick : function(e, t){
27300         
27301     },
27302     // private
27303     handleMouseDown : function(e, t)
27304     {
27305             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27306         if(this.isLocked() || rowIndex < 0 ){
27307             return;
27308         };
27309         if(e.shiftKey && this.last !== false){
27310             var last = this.last;
27311             this.selectRange(last, rowIndex, e.ctrlKey);
27312             this.last = last; // reset the last
27313             t.focus();
27314     
27315         }else{
27316             var isSelected = this.isSelected(rowIndex);
27317             //Roo.log("select row:" + rowIndex);
27318             if(isSelected){
27319                 this.deselectRow(rowIndex);
27320             } else {
27321                         this.selectRow(rowIndex, true);
27322             }
27323     
27324             /*
27325                 if(e.button !== 0 && isSelected){
27326                 alert('rowIndex 2: ' + rowIndex);
27327                     view.focusRow(rowIndex);
27328                 }else if(e.ctrlKey && isSelected){
27329                     this.deselectRow(rowIndex);
27330                 }else if(!isSelected){
27331                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27332                     view.focusRow(rowIndex);
27333                 }
27334             */
27335         }
27336         this.fireEvent("afterselectionchange", this);
27337     },
27338     // private
27339     handleDragableRowClick :  function(grid, rowIndex, e) 
27340     {
27341         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27342             this.selectRow(rowIndex, false);
27343             grid.view.focusRow(rowIndex);
27344              this.fireEvent("afterselectionchange", this);
27345         }
27346     },
27347     
27348     /**
27349      * Selects multiple rows.
27350      * @param {Array} rows Array of the indexes of the row to select
27351      * @param {Boolean} keepExisting (optional) True to keep existing selections
27352      */
27353     selectRows : function(rows, keepExisting){
27354         if(!keepExisting){
27355             this.clearSelections();
27356         }
27357         for(var i = 0, len = rows.length; i < len; i++){
27358             this.selectRow(rows[i], true);
27359         }
27360     },
27361
27362     /**
27363      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27364      * @param {Number} startRow The index of the first row in the range
27365      * @param {Number} endRow The index of the last row in the range
27366      * @param {Boolean} keepExisting (optional) True to retain existing selections
27367      */
27368     selectRange : function(startRow, endRow, keepExisting){
27369         if(this.locked) {
27370             return;
27371         }
27372         if(!keepExisting){
27373             this.clearSelections();
27374         }
27375         if(startRow <= endRow){
27376             for(var i = startRow; i <= endRow; i++){
27377                 this.selectRow(i, true);
27378             }
27379         }else{
27380             for(var i = startRow; i >= endRow; i--){
27381                 this.selectRow(i, true);
27382             }
27383         }
27384     },
27385
27386     /**
27387      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27388      * @param {Number} startRow The index of the first row in the range
27389      * @param {Number} endRow The index of the last row in the range
27390      */
27391     deselectRange : function(startRow, endRow, preventViewNotify){
27392         if(this.locked) {
27393             return;
27394         }
27395         for(var i = startRow; i <= endRow; i++){
27396             this.deselectRow(i, preventViewNotify);
27397         }
27398     },
27399
27400     /**
27401      * Selects a row.
27402      * @param {Number} row The index of the row to select
27403      * @param {Boolean} keepExisting (optional) True to keep existing selections
27404      */
27405     selectRow : function(index, keepExisting, preventViewNotify)
27406     {
27407             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27408             return;
27409         }
27410         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27411             if(!keepExisting || this.singleSelect){
27412                 this.clearSelections();
27413             }
27414             
27415             var r = this.grid.store.getAt(index);
27416             //console.log('selectRow - record id :' + r.id);
27417             
27418             this.selections.add(r);
27419             this.last = this.lastActive = index;
27420             if(!preventViewNotify){
27421                 var proxy = new Roo.Element(
27422                                 this.grid.getRowDom(index)
27423                 );
27424                 proxy.addClass('bg-info info');
27425             }
27426             this.fireEvent("rowselect", this, index, r);
27427             this.fireEvent("selectionchange", this);
27428         }
27429     },
27430
27431     /**
27432      * Deselects a row.
27433      * @param {Number} row The index of the row to deselect
27434      */
27435     deselectRow : function(index, preventViewNotify)
27436     {
27437         if(this.locked) {
27438             return;
27439         }
27440         if(this.last == index){
27441             this.last = false;
27442         }
27443         if(this.lastActive == index){
27444             this.lastActive = false;
27445         }
27446         
27447         var r = this.grid.store.getAt(index);
27448         if (!r) {
27449             return;
27450         }
27451         
27452         this.selections.remove(r);
27453         //.console.log('deselectRow - record id :' + r.id);
27454         if(!preventViewNotify){
27455         
27456             var proxy = new Roo.Element(
27457                 this.grid.getRowDom(index)
27458             );
27459             proxy.removeClass('bg-info info');
27460         }
27461         this.fireEvent("rowdeselect", this, index);
27462         this.fireEvent("selectionchange", this);
27463     },
27464
27465     // private
27466     restoreLast : function(){
27467         if(this._last){
27468             this.last = this._last;
27469         }
27470     },
27471
27472     // private
27473     acceptsNav : function(row, col, cm){
27474         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27475     },
27476
27477     // private
27478     onEditorKey : function(field, e){
27479         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27480         if(k == e.TAB){
27481             e.stopEvent();
27482             ed.completeEdit();
27483             if(e.shiftKey){
27484                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27485             }else{
27486                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27487             }
27488         }else if(k == e.ENTER && !e.ctrlKey){
27489             e.stopEvent();
27490             ed.completeEdit();
27491             if(e.shiftKey){
27492                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27493             }else{
27494                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27495             }
27496         }else if(k == e.ESC){
27497             ed.cancelEdit();
27498         }
27499         if(newCell){
27500             g.startEditing(newCell[0], newCell[1]);
27501         }
27502     }
27503 });
27504 /*
27505  * Based on:
27506  * Ext JS Library 1.1.1
27507  * Copyright(c) 2006-2007, Ext JS, LLC.
27508  *
27509  * Originally Released Under LGPL - original licence link has changed is not relivant.
27510  *
27511  * Fork - LGPL
27512  * <script type="text/javascript">
27513  */
27514  
27515 /**
27516  * @class Roo.bootstrap.PagingToolbar
27517  * @extends Roo.bootstrap.NavSimplebar
27518  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27519  * @constructor
27520  * Create a new PagingToolbar
27521  * @param {Object} config The config object
27522  * @param {Roo.data.Store} store
27523  */
27524 Roo.bootstrap.PagingToolbar = function(config)
27525 {
27526     // old args format still supported... - xtype is prefered..
27527         // created from xtype...
27528     
27529     this.ds = config.dataSource;
27530     
27531     if (config.store && !this.ds) {
27532         this.store= Roo.factory(config.store, Roo.data);
27533         this.ds = this.store;
27534         this.ds.xmodule = this.xmodule || false;
27535     }
27536     
27537     this.toolbarItems = [];
27538     if (config.items) {
27539         this.toolbarItems = config.items;
27540     }
27541     
27542     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27543     
27544     this.cursor = 0;
27545     
27546     if (this.ds) { 
27547         this.bind(this.ds);
27548     }
27549     
27550     if (Roo.bootstrap.version == 4) {
27551         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27552     } else {
27553         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27554     }
27555     
27556 };
27557
27558 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27559     /**
27560      * @cfg {Roo.data.Store} dataSource
27561      * The underlying data store providing the paged data
27562      */
27563     /**
27564      * @cfg {String/HTMLElement/Element} container
27565      * container The id or element that will contain the toolbar
27566      */
27567     /**
27568      * @cfg {Boolean} displayInfo
27569      * True to display the displayMsg (defaults to false)
27570      */
27571     /**
27572      * @cfg {Number} pageSize
27573      * The number of records to display per page (defaults to 20)
27574      */
27575     pageSize: 20,
27576     /**
27577      * @cfg {String} displayMsg
27578      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27579      */
27580     displayMsg : 'Displaying {0} - {1} of {2}',
27581     /**
27582      * @cfg {String} emptyMsg
27583      * The message to display when no records are found (defaults to "No data to display")
27584      */
27585     emptyMsg : 'No data to display',
27586     /**
27587      * Customizable piece of the default paging text (defaults to "Page")
27588      * @type String
27589      */
27590     beforePageText : "Page",
27591     /**
27592      * Customizable piece of the default paging text (defaults to "of %0")
27593      * @type String
27594      */
27595     afterPageText : "of {0}",
27596     /**
27597      * Customizable piece of the default paging text (defaults to "First Page")
27598      * @type String
27599      */
27600     firstText : "First Page",
27601     /**
27602      * Customizable piece of the default paging text (defaults to "Previous Page")
27603      * @type String
27604      */
27605     prevText : "Previous Page",
27606     /**
27607      * Customizable piece of the default paging text (defaults to "Next Page")
27608      * @type String
27609      */
27610     nextText : "Next Page",
27611     /**
27612      * Customizable piece of the default paging text (defaults to "Last Page")
27613      * @type String
27614      */
27615     lastText : "Last Page",
27616     /**
27617      * Customizable piece of the default paging text (defaults to "Refresh")
27618      * @type String
27619      */
27620     refreshText : "Refresh",
27621
27622     buttons : false,
27623     // private
27624     onRender : function(ct, position) 
27625     {
27626         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27627         this.navgroup.parentId = this.id;
27628         this.navgroup.onRender(this.el, null);
27629         // add the buttons to the navgroup
27630         
27631         if(this.displayInfo){
27632             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27633             this.displayEl = this.el.select('.x-paging-info', true).first();
27634 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27635 //            this.displayEl = navel.el.select('span',true).first();
27636         }
27637         
27638         var _this = this;
27639         
27640         if(this.buttons){
27641             Roo.each(_this.buttons, function(e){ // this might need to use render????
27642                Roo.factory(e).render(_this.el);
27643             });
27644         }
27645             
27646         Roo.each(_this.toolbarItems, function(e) {
27647             _this.navgroup.addItem(e);
27648         });
27649         
27650         
27651         this.first = this.navgroup.addItem({
27652             tooltip: this.firstText,
27653             cls: "prev btn-outline-secondary",
27654             html : ' <i class="fa fa-step-backward"></i>',
27655             disabled: true,
27656             preventDefault: true,
27657             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27658         });
27659         
27660         this.prev =  this.navgroup.addItem({
27661             tooltip: this.prevText,
27662             cls: "prev btn-outline-secondary",
27663             html : ' <i class="fa fa-backward"></i>',
27664             disabled: true,
27665             preventDefault: true,
27666             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27667         });
27668     //this.addSeparator();
27669         
27670         
27671         var field = this.navgroup.addItem( {
27672             tagtype : 'span',
27673             cls : 'x-paging-position  btn-outline-secondary',
27674              disabled: true,
27675             html : this.beforePageText  +
27676                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27677                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27678          } ); //?? escaped?
27679         
27680         this.field = field.el.select('input', true).first();
27681         this.field.on("keydown", this.onPagingKeydown, this);
27682         this.field.on("focus", function(){this.dom.select();});
27683     
27684     
27685         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27686         //this.field.setHeight(18);
27687         //this.addSeparator();
27688         this.next = this.navgroup.addItem({
27689             tooltip: this.nextText,
27690             cls: "next btn-outline-secondary",
27691             html : ' <i class="fa fa-forward"></i>',
27692             disabled: true,
27693             preventDefault: true,
27694             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27695         });
27696         this.last = this.navgroup.addItem({
27697             tooltip: this.lastText,
27698             html : ' <i class="fa fa-step-forward"></i>',
27699             cls: "next btn-outline-secondary",
27700             disabled: true,
27701             preventDefault: true,
27702             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27703         });
27704     //this.addSeparator();
27705         this.loading = this.navgroup.addItem({
27706             tooltip: this.refreshText,
27707             cls: "btn-outline-secondary",
27708             html : ' <i class="fa fa-refresh"></i>',
27709             preventDefault: true,
27710             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27711         });
27712         
27713     },
27714
27715     // private
27716     updateInfo : function(){
27717         if(this.displayEl){
27718             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27719             var msg = count == 0 ?
27720                 this.emptyMsg :
27721                 String.format(
27722                     this.displayMsg,
27723                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27724                 );
27725             this.displayEl.update(msg);
27726         }
27727     },
27728
27729     // private
27730     onLoad : function(ds, r, o)
27731     {
27732         this.cursor = o.params && o.params.start ? o.params.start : 0;
27733         
27734         var d = this.getPageData(),
27735             ap = d.activePage,
27736             ps = d.pages;
27737         
27738         
27739         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27740         this.field.dom.value = ap;
27741         this.first.setDisabled(ap == 1);
27742         this.prev.setDisabled(ap == 1);
27743         this.next.setDisabled(ap == ps);
27744         this.last.setDisabled(ap == ps);
27745         this.loading.enable();
27746         this.updateInfo();
27747     },
27748
27749     // private
27750     getPageData : function(){
27751         var total = this.ds.getTotalCount();
27752         return {
27753             total : total,
27754             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27755             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27756         };
27757     },
27758
27759     // private
27760     onLoadError : function(){
27761         this.loading.enable();
27762     },
27763
27764     // private
27765     onPagingKeydown : function(e){
27766         var k = e.getKey();
27767         var d = this.getPageData();
27768         if(k == e.RETURN){
27769             var v = this.field.dom.value, pageNum;
27770             if(!v || isNaN(pageNum = parseInt(v, 10))){
27771                 this.field.dom.value = d.activePage;
27772                 return;
27773             }
27774             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27775             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27776             e.stopEvent();
27777         }
27778         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))
27779         {
27780           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27781           this.field.dom.value = pageNum;
27782           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27783           e.stopEvent();
27784         }
27785         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27786         {
27787           var v = this.field.dom.value, pageNum; 
27788           var increment = (e.shiftKey) ? 10 : 1;
27789           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27790                 increment *= -1;
27791           }
27792           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27793             this.field.dom.value = d.activePage;
27794             return;
27795           }
27796           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27797           {
27798             this.field.dom.value = parseInt(v, 10) + increment;
27799             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27800             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27801           }
27802           e.stopEvent();
27803         }
27804     },
27805
27806     // private
27807     beforeLoad : function(){
27808         if(this.loading){
27809             this.loading.disable();
27810         }
27811     },
27812
27813     // private
27814     onClick : function(which){
27815         
27816         var ds = this.ds;
27817         if (!ds) {
27818             return;
27819         }
27820         
27821         switch(which){
27822             case "first":
27823                 ds.load({params:{start: 0, limit: this.pageSize}});
27824             break;
27825             case "prev":
27826                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27827             break;
27828             case "next":
27829                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27830             break;
27831             case "last":
27832                 var total = ds.getTotalCount();
27833                 var extra = total % this.pageSize;
27834                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27835                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27836             break;
27837             case "refresh":
27838                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27839             break;
27840         }
27841     },
27842
27843     /**
27844      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27845      * @param {Roo.data.Store} store The data store to unbind
27846      */
27847     unbind : function(ds){
27848         ds.un("beforeload", this.beforeLoad, this);
27849         ds.un("load", this.onLoad, this);
27850         ds.un("loadexception", this.onLoadError, this);
27851         ds.un("remove", this.updateInfo, this);
27852         ds.un("add", this.updateInfo, this);
27853         this.ds = undefined;
27854     },
27855
27856     /**
27857      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27858      * @param {Roo.data.Store} store The data store to bind
27859      */
27860     bind : function(ds){
27861         ds.on("beforeload", this.beforeLoad, this);
27862         ds.on("load", this.onLoad, this);
27863         ds.on("loadexception", this.onLoadError, this);
27864         ds.on("remove", this.updateInfo, this);
27865         ds.on("add", this.updateInfo, this);
27866         this.ds = ds;
27867     }
27868 });/*
27869  * - LGPL
27870  *
27871  * element
27872  * 
27873  */
27874
27875 /**
27876  * @class Roo.bootstrap.MessageBar
27877  * @extends Roo.bootstrap.Component
27878  * Bootstrap MessageBar class
27879  * @cfg {String} html contents of the MessageBar
27880  * @cfg {String} weight (info | success | warning | danger) default info
27881  * @cfg {String} beforeClass insert the bar before the given class
27882  * @cfg {Boolean} closable (true | false) default false
27883  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27884  * 
27885  * @constructor
27886  * Create a new Element
27887  * @param {Object} config The config object
27888  */
27889
27890 Roo.bootstrap.MessageBar = function(config){
27891     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27892 };
27893
27894 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27895     
27896     html: '',
27897     weight: 'info',
27898     closable: false,
27899     fixed: false,
27900     beforeClass: 'bootstrap-sticky-wrap',
27901     
27902     getAutoCreate : function(){
27903         
27904         var cfg = {
27905             tag: 'div',
27906             cls: 'alert alert-dismissable alert-' + this.weight,
27907             cn: [
27908                 {
27909                     tag: 'span',
27910                     cls: 'message',
27911                     html: this.html || ''
27912                 }
27913             ]
27914         };
27915         
27916         if(this.fixed){
27917             cfg.cls += ' alert-messages-fixed';
27918         }
27919         
27920         if(this.closable){
27921             cfg.cn.push({
27922                 tag: 'button',
27923                 cls: 'close',
27924                 html: 'x'
27925             });
27926         }
27927         
27928         return cfg;
27929     },
27930     
27931     onRender : function(ct, position)
27932     {
27933         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27934         
27935         if(!this.el){
27936             var cfg = Roo.apply({},  this.getAutoCreate());
27937             cfg.id = Roo.id();
27938             
27939             if (this.cls) {
27940                 cfg.cls += ' ' + this.cls;
27941             }
27942             if (this.style) {
27943                 cfg.style = this.style;
27944             }
27945             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27946             
27947             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27948         }
27949         
27950         this.el.select('>button.close').on('click', this.hide, this);
27951         
27952     },
27953     
27954     show : function()
27955     {
27956         if (!this.rendered) {
27957             this.render();
27958         }
27959         
27960         this.el.show();
27961         
27962         this.fireEvent('show', this);
27963         
27964     },
27965     
27966     hide : function()
27967     {
27968         if (!this.rendered) {
27969             this.render();
27970         }
27971         
27972         this.el.hide();
27973         
27974         this.fireEvent('hide', this);
27975     },
27976     
27977     update : function()
27978     {
27979 //        var e = this.el.dom.firstChild;
27980 //        
27981 //        if(this.closable){
27982 //            e = e.nextSibling;
27983 //        }
27984 //        
27985 //        e.data = this.html || '';
27986
27987         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27988     }
27989    
27990 });
27991
27992  
27993
27994      /*
27995  * - LGPL
27996  *
27997  * Graph
27998  * 
27999  */
28000
28001
28002 /**
28003  * @class Roo.bootstrap.Graph
28004  * @extends Roo.bootstrap.Component
28005  * Bootstrap Graph class
28006 > Prameters
28007  -sm {number} sm 4
28008  -md {number} md 5
28009  @cfg {String} graphtype  bar | vbar | pie
28010  @cfg {number} g_x coodinator | centre x (pie)
28011  @cfg {number} g_y coodinator | centre y (pie)
28012  @cfg {number} g_r radius (pie)
28013  @cfg {number} g_height height of the chart (respected by all elements in the set)
28014  @cfg {number} g_width width of the chart (respected by all elements in the set)
28015  @cfg {Object} title The title of the chart
28016     
28017  -{Array}  values
28018  -opts (object) options for the chart 
28019      o {
28020      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28021      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28022      o vgutter (number)
28023      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.
28024      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28025      o to
28026      o stretch (boolean)
28027      o }
28028  -opts (object) options for the pie
28029      o{
28030      o cut
28031      o startAngle (number)
28032      o endAngle (number)
28033      } 
28034  *
28035  * @constructor
28036  * Create a new Input
28037  * @param {Object} config The config object
28038  */
28039
28040 Roo.bootstrap.Graph = function(config){
28041     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28042     
28043     this.addEvents({
28044         // img events
28045         /**
28046          * @event click
28047          * The img click event for the img.
28048          * @param {Roo.EventObject} e
28049          */
28050         "click" : true
28051     });
28052 };
28053
28054 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28055     
28056     sm: 4,
28057     md: 5,
28058     graphtype: 'bar',
28059     g_height: 250,
28060     g_width: 400,
28061     g_x: 50,
28062     g_y: 50,
28063     g_r: 30,
28064     opts:{
28065         //g_colors: this.colors,
28066         g_type: 'soft',
28067         g_gutter: '20%'
28068
28069     },
28070     title : false,
28071
28072     getAutoCreate : function(){
28073         
28074         var cfg = {
28075             tag: 'div',
28076             html : null
28077         };
28078         
28079         
28080         return  cfg;
28081     },
28082
28083     onRender : function(ct,position){
28084         
28085         
28086         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28087         
28088         if (typeof(Raphael) == 'undefined') {
28089             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28090             return;
28091         }
28092         
28093         this.raphael = Raphael(this.el.dom);
28094         
28095                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28096                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28097                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28098                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28099                 /*
28100                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28101                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28102                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28103                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28104                 
28105                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28106                 r.barchart(330, 10, 300, 220, data1);
28107                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28108                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28109                 */
28110                 
28111                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28112                 // r.barchart(30, 30, 560, 250,  xdata, {
28113                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28114                 //     axis : "0 0 1 1",
28115                 //     axisxlabels :  xdata
28116                 //     //yvalues : cols,
28117                    
28118                 // });
28119 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28120 //        
28121 //        this.load(null,xdata,{
28122 //                axis : "0 0 1 1",
28123 //                axisxlabels :  xdata
28124 //                });
28125
28126     },
28127
28128     load : function(graphtype,xdata,opts)
28129     {
28130         this.raphael.clear();
28131         if(!graphtype) {
28132             graphtype = this.graphtype;
28133         }
28134         if(!opts){
28135             opts = this.opts;
28136         }
28137         var r = this.raphael,
28138             fin = function () {
28139                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28140             },
28141             fout = function () {
28142                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28143             },
28144             pfin = function() {
28145                 this.sector.stop();
28146                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28147
28148                 if (this.label) {
28149                     this.label[0].stop();
28150                     this.label[0].attr({ r: 7.5 });
28151                     this.label[1].attr({ "font-weight": 800 });
28152                 }
28153             },
28154             pfout = function() {
28155                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28156
28157                 if (this.label) {
28158                     this.label[0].animate({ r: 5 }, 500, "bounce");
28159                     this.label[1].attr({ "font-weight": 400 });
28160                 }
28161             };
28162
28163         switch(graphtype){
28164             case 'bar':
28165                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28166                 break;
28167             case 'hbar':
28168                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28169                 break;
28170             case 'pie':
28171 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28172 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28173 //            
28174                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28175                 
28176                 break;
28177
28178         }
28179         
28180         if(this.title){
28181             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28182         }
28183         
28184     },
28185     
28186     setTitle: function(o)
28187     {
28188         this.title = o;
28189     },
28190     
28191     initEvents: function() {
28192         
28193         if(!this.href){
28194             this.el.on('click', this.onClick, this);
28195         }
28196     },
28197     
28198     onClick : function(e)
28199     {
28200         Roo.log('img onclick');
28201         this.fireEvent('click', this, e);
28202     }
28203    
28204 });
28205
28206  
28207 /*
28208  * - LGPL
28209  *
28210  * numberBox
28211  * 
28212  */
28213 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28214
28215 /**
28216  * @class Roo.bootstrap.dash.NumberBox
28217  * @extends Roo.bootstrap.Component
28218  * Bootstrap NumberBox class
28219  * @cfg {String} headline Box headline
28220  * @cfg {String} content Box content
28221  * @cfg {String} icon Box icon
28222  * @cfg {String} footer Footer text
28223  * @cfg {String} fhref Footer href
28224  * 
28225  * @constructor
28226  * Create a new NumberBox
28227  * @param {Object} config The config object
28228  */
28229
28230
28231 Roo.bootstrap.dash.NumberBox = function(config){
28232     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28233     
28234 };
28235
28236 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28237     
28238     headline : '',
28239     content : '',
28240     icon : '',
28241     footer : '',
28242     fhref : '',
28243     ficon : '',
28244     
28245     getAutoCreate : function(){
28246         
28247         var cfg = {
28248             tag : 'div',
28249             cls : 'small-box ',
28250             cn : [
28251                 {
28252                     tag : 'div',
28253                     cls : 'inner',
28254                     cn :[
28255                         {
28256                             tag : 'h3',
28257                             cls : 'roo-headline',
28258                             html : this.headline
28259                         },
28260                         {
28261                             tag : 'p',
28262                             cls : 'roo-content',
28263                             html : this.content
28264                         }
28265                     ]
28266                 }
28267             ]
28268         };
28269         
28270         if(this.icon){
28271             cfg.cn.push({
28272                 tag : 'div',
28273                 cls : 'icon',
28274                 cn :[
28275                     {
28276                         tag : 'i',
28277                         cls : 'ion ' + this.icon
28278                     }
28279                 ]
28280             });
28281         }
28282         
28283         if(this.footer){
28284             var footer = {
28285                 tag : 'a',
28286                 cls : 'small-box-footer',
28287                 href : this.fhref || '#',
28288                 html : this.footer
28289             };
28290             
28291             cfg.cn.push(footer);
28292             
28293         }
28294         
28295         return  cfg;
28296     },
28297
28298     onRender : function(ct,position){
28299         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28300
28301
28302        
28303                 
28304     },
28305
28306     setHeadline: function (value)
28307     {
28308         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28309     },
28310     
28311     setFooter: function (value, href)
28312     {
28313         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28314         
28315         if(href){
28316             this.el.select('a.small-box-footer',true).first().attr('href', href);
28317         }
28318         
28319     },
28320
28321     setContent: function (value)
28322     {
28323         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28324     },
28325
28326     initEvents: function() 
28327     {   
28328         
28329     }
28330     
28331 });
28332
28333  
28334 /*
28335  * - LGPL
28336  *
28337  * TabBox
28338  * 
28339  */
28340 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28341
28342 /**
28343  * @class Roo.bootstrap.dash.TabBox
28344  * @extends Roo.bootstrap.Component
28345  * Bootstrap TabBox class
28346  * @cfg {String} title Title of the TabBox
28347  * @cfg {String} icon Icon of the TabBox
28348  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28349  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28350  * 
28351  * @constructor
28352  * Create a new TabBox
28353  * @param {Object} config The config object
28354  */
28355
28356
28357 Roo.bootstrap.dash.TabBox = function(config){
28358     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28359     this.addEvents({
28360         // raw events
28361         /**
28362          * @event addpane
28363          * When a pane is added
28364          * @param {Roo.bootstrap.dash.TabPane} pane
28365          */
28366         "addpane" : true,
28367         /**
28368          * @event activatepane
28369          * When a pane is activated
28370          * @param {Roo.bootstrap.dash.TabPane} pane
28371          */
28372         "activatepane" : true
28373         
28374          
28375     });
28376     
28377     this.panes = [];
28378 };
28379
28380 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28381
28382     title : '',
28383     icon : false,
28384     showtabs : true,
28385     tabScrollable : false,
28386     
28387     getChildContainer : function()
28388     {
28389         return this.el.select('.tab-content', true).first();
28390     },
28391     
28392     getAutoCreate : function(){
28393         
28394         var header = {
28395             tag: 'li',
28396             cls: 'pull-left header',
28397             html: this.title,
28398             cn : []
28399         };
28400         
28401         if(this.icon){
28402             header.cn.push({
28403                 tag: 'i',
28404                 cls: 'fa ' + this.icon
28405             });
28406         }
28407         
28408         var h = {
28409             tag: 'ul',
28410             cls: 'nav nav-tabs pull-right',
28411             cn: [
28412                 header
28413             ]
28414         };
28415         
28416         if(this.tabScrollable){
28417             h = {
28418                 tag: 'div',
28419                 cls: 'tab-header',
28420                 cn: [
28421                     {
28422                         tag: 'ul',
28423                         cls: 'nav nav-tabs pull-right',
28424                         cn: [
28425                             header
28426                         ]
28427                     }
28428                 ]
28429             };
28430         }
28431         
28432         var cfg = {
28433             tag: 'div',
28434             cls: 'nav-tabs-custom',
28435             cn: [
28436                 h,
28437                 {
28438                     tag: 'div',
28439                     cls: 'tab-content no-padding',
28440                     cn: []
28441                 }
28442             ]
28443         };
28444
28445         return  cfg;
28446     },
28447     initEvents : function()
28448     {
28449         //Roo.log('add add pane handler');
28450         this.on('addpane', this.onAddPane, this);
28451     },
28452      /**
28453      * Updates the box title
28454      * @param {String} html to set the title to.
28455      */
28456     setTitle : function(value)
28457     {
28458         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28459     },
28460     onAddPane : function(pane)
28461     {
28462         this.panes.push(pane);
28463         //Roo.log('addpane');
28464         //Roo.log(pane);
28465         // tabs are rendere left to right..
28466         if(!this.showtabs){
28467             return;
28468         }
28469         
28470         var ctr = this.el.select('.nav-tabs', true).first();
28471          
28472          
28473         var existing = ctr.select('.nav-tab',true);
28474         var qty = existing.getCount();;
28475         
28476         
28477         var tab = ctr.createChild({
28478             tag : 'li',
28479             cls : 'nav-tab' + (qty ? '' : ' active'),
28480             cn : [
28481                 {
28482                     tag : 'a',
28483                     href:'#',
28484                     html : pane.title
28485                 }
28486             ]
28487         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28488         pane.tab = tab;
28489         
28490         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28491         if (!qty) {
28492             pane.el.addClass('active');
28493         }
28494         
28495                 
28496     },
28497     onTabClick : function(ev,un,ob,pane)
28498     {
28499         //Roo.log('tab - prev default');
28500         ev.preventDefault();
28501         
28502         
28503         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28504         pane.tab.addClass('active');
28505         //Roo.log(pane.title);
28506         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28507         // technically we should have a deactivate event.. but maybe add later.
28508         // and it should not de-activate the selected tab...
28509         this.fireEvent('activatepane', pane);
28510         pane.el.addClass('active');
28511         pane.fireEvent('activate');
28512         
28513         
28514     },
28515     
28516     getActivePane : function()
28517     {
28518         var r = false;
28519         Roo.each(this.panes, function(p) {
28520             if(p.el.hasClass('active')){
28521                 r = p;
28522                 return false;
28523             }
28524             
28525             return;
28526         });
28527         
28528         return r;
28529     }
28530     
28531     
28532 });
28533
28534  
28535 /*
28536  * - LGPL
28537  *
28538  * Tab pane
28539  * 
28540  */
28541 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28542 /**
28543  * @class Roo.bootstrap.TabPane
28544  * @extends Roo.bootstrap.Component
28545  * Bootstrap TabPane class
28546  * @cfg {Boolean} active (false | true) Default false
28547  * @cfg {String} title title of panel
28548
28549  * 
28550  * @constructor
28551  * Create a new TabPane
28552  * @param {Object} config The config object
28553  */
28554
28555 Roo.bootstrap.dash.TabPane = function(config){
28556     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28557     
28558     this.addEvents({
28559         // raw events
28560         /**
28561          * @event activate
28562          * When a pane is activated
28563          * @param {Roo.bootstrap.dash.TabPane} pane
28564          */
28565         "activate" : true
28566          
28567     });
28568 };
28569
28570 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28571     
28572     active : false,
28573     title : '',
28574     
28575     // the tabBox that this is attached to.
28576     tab : false,
28577      
28578     getAutoCreate : function() 
28579     {
28580         var cfg = {
28581             tag: 'div',
28582             cls: 'tab-pane'
28583         };
28584         
28585         if(this.active){
28586             cfg.cls += ' active';
28587         }
28588         
28589         return cfg;
28590     },
28591     initEvents  : function()
28592     {
28593         //Roo.log('trigger add pane handler');
28594         this.parent().fireEvent('addpane', this)
28595     },
28596     
28597      /**
28598      * Updates the tab title 
28599      * @param {String} html to set the title to.
28600      */
28601     setTitle: function(str)
28602     {
28603         if (!this.tab) {
28604             return;
28605         }
28606         this.title = str;
28607         this.tab.select('a', true).first().dom.innerHTML = str;
28608         
28609     }
28610     
28611     
28612     
28613 });
28614
28615  
28616
28617
28618  /*
28619  * - LGPL
28620  *
28621  * menu
28622  * 
28623  */
28624 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28625
28626 /**
28627  * @class Roo.bootstrap.menu.Menu
28628  * @extends Roo.bootstrap.Component
28629  * Bootstrap Menu class - container for Menu
28630  * @cfg {String} html Text of the menu
28631  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28632  * @cfg {String} icon Font awesome icon
28633  * @cfg {String} pos Menu align to (top | bottom) default bottom
28634  * 
28635  * 
28636  * @constructor
28637  * Create a new Menu
28638  * @param {Object} config The config object
28639  */
28640
28641
28642 Roo.bootstrap.menu.Menu = function(config){
28643     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28644     
28645     this.addEvents({
28646         /**
28647          * @event beforeshow
28648          * Fires before this menu is displayed
28649          * @param {Roo.bootstrap.menu.Menu} this
28650          */
28651         beforeshow : true,
28652         /**
28653          * @event beforehide
28654          * Fires before this menu is hidden
28655          * @param {Roo.bootstrap.menu.Menu} this
28656          */
28657         beforehide : true,
28658         /**
28659          * @event show
28660          * Fires after this menu is displayed
28661          * @param {Roo.bootstrap.menu.Menu} this
28662          */
28663         show : true,
28664         /**
28665          * @event hide
28666          * Fires after this menu is hidden
28667          * @param {Roo.bootstrap.menu.Menu} this
28668          */
28669         hide : true,
28670         /**
28671          * @event click
28672          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28673          * @param {Roo.bootstrap.menu.Menu} this
28674          * @param {Roo.EventObject} e
28675          */
28676         click : true
28677     });
28678     
28679 };
28680
28681 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28682     
28683     submenu : false,
28684     html : '',
28685     weight : 'default',
28686     icon : false,
28687     pos : 'bottom',
28688     
28689     
28690     getChildContainer : function() {
28691         if(this.isSubMenu){
28692             return this.el;
28693         }
28694         
28695         return this.el.select('ul.dropdown-menu', true).first();  
28696     },
28697     
28698     getAutoCreate : function()
28699     {
28700         var text = [
28701             {
28702                 tag : 'span',
28703                 cls : 'roo-menu-text',
28704                 html : this.html
28705             }
28706         ];
28707         
28708         if(this.icon){
28709             text.unshift({
28710                 tag : 'i',
28711                 cls : 'fa ' + this.icon
28712             })
28713         }
28714         
28715         
28716         var cfg = {
28717             tag : 'div',
28718             cls : 'btn-group',
28719             cn : [
28720                 {
28721                     tag : 'button',
28722                     cls : 'dropdown-button btn btn-' + this.weight,
28723                     cn : text
28724                 },
28725                 {
28726                     tag : 'button',
28727                     cls : 'dropdown-toggle btn btn-' + this.weight,
28728                     cn : [
28729                         {
28730                             tag : 'span',
28731                             cls : 'caret'
28732                         }
28733                     ]
28734                 },
28735                 {
28736                     tag : 'ul',
28737                     cls : 'dropdown-menu'
28738                 }
28739             ]
28740             
28741         };
28742         
28743         if(this.pos == 'top'){
28744             cfg.cls += ' dropup';
28745         }
28746         
28747         if(this.isSubMenu){
28748             cfg = {
28749                 tag : 'ul',
28750                 cls : 'dropdown-menu'
28751             }
28752         }
28753         
28754         return cfg;
28755     },
28756     
28757     onRender : function(ct, position)
28758     {
28759         this.isSubMenu = ct.hasClass('dropdown-submenu');
28760         
28761         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28762     },
28763     
28764     initEvents : function() 
28765     {
28766         if(this.isSubMenu){
28767             return;
28768         }
28769         
28770         this.hidden = true;
28771         
28772         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28773         this.triggerEl.on('click', this.onTriggerPress, this);
28774         
28775         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28776         this.buttonEl.on('click', this.onClick, this);
28777         
28778     },
28779     
28780     list : function()
28781     {
28782         if(this.isSubMenu){
28783             return this.el;
28784         }
28785         
28786         return this.el.select('ul.dropdown-menu', true).first();
28787     },
28788     
28789     onClick : function(e)
28790     {
28791         this.fireEvent("click", this, e);
28792     },
28793     
28794     onTriggerPress  : function(e)
28795     {   
28796         if (this.isVisible()) {
28797             this.hide();
28798         } else {
28799             this.show();
28800         }
28801     },
28802     
28803     isVisible : function(){
28804         return !this.hidden;
28805     },
28806     
28807     show : function()
28808     {
28809         this.fireEvent("beforeshow", this);
28810         
28811         this.hidden = false;
28812         this.el.addClass('open');
28813         
28814         Roo.get(document).on("mouseup", this.onMouseUp, this);
28815         
28816         this.fireEvent("show", this);
28817         
28818         
28819     },
28820     
28821     hide : function()
28822     {
28823         this.fireEvent("beforehide", this);
28824         
28825         this.hidden = true;
28826         this.el.removeClass('open');
28827         
28828         Roo.get(document).un("mouseup", this.onMouseUp);
28829         
28830         this.fireEvent("hide", this);
28831     },
28832     
28833     onMouseUp : function()
28834     {
28835         this.hide();
28836     }
28837     
28838 });
28839
28840  
28841  /*
28842  * - LGPL
28843  *
28844  * menu item
28845  * 
28846  */
28847 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28848
28849 /**
28850  * @class Roo.bootstrap.menu.Item
28851  * @extends Roo.bootstrap.Component
28852  * Bootstrap MenuItem class
28853  * @cfg {Boolean} submenu (true | false) default false
28854  * @cfg {String} html text of the item
28855  * @cfg {String} href the link
28856  * @cfg {Boolean} disable (true | false) default false
28857  * @cfg {Boolean} preventDefault (true | false) default true
28858  * @cfg {String} icon Font awesome icon
28859  * @cfg {String} pos Submenu align to (left | right) default right 
28860  * 
28861  * 
28862  * @constructor
28863  * Create a new Item
28864  * @param {Object} config The config object
28865  */
28866
28867
28868 Roo.bootstrap.menu.Item = function(config){
28869     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28870     this.addEvents({
28871         /**
28872          * @event mouseover
28873          * Fires when the mouse is hovering over this menu
28874          * @param {Roo.bootstrap.menu.Item} this
28875          * @param {Roo.EventObject} e
28876          */
28877         mouseover : true,
28878         /**
28879          * @event mouseout
28880          * Fires when the mouse exits this menu
28881          * @param {Roo.bootstrap.menu.Item} this
28882          * @param {Roo.EventObject} e
28883          */
28884         mouseout : true,
28885         // raw events
28886         /**
28887          * @event click
28888          * The raw click event for the entire grid.
28889          * @param {Roo.EventObject} e
28890          */
28891         click : true
28892     });
28893 };
28894
28895 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28896     
28897     submenu : false,
28898     href : '',
28899     html : '',
28900     preventDefault: true,
28901     disable : false,
28902     icon : false,
28903     pos : 'right',
28904     
28905     getAutoCreate : function()
28906     {
28907         var text = [
28908             {
28909                 tag : 'span',
28910                 cls : 'roo-menu-item-text',
28911                 html : this.html
28912             }
28913         ];
28914         
28915         if(this.icon){
28916             text.unshift({
28917                 tag : 'i',
28918                 cls : 'fa ' + this.icon
28919             })
28920         }
28921         
28922         var cfg = {
28923             tag : 'li',
28924             cn : [
28925                 {
28926                     tag : 'a',
28927                     href : this.href || '#',
28928                     cn : text
28929                 }
28930             ]
28931         };
28932         
28933         if(this.disable){
28934             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28935         }
28936         
28937         if(this.submenu){
28938             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28939             
28940             if(this.pos == 'left'){
28941                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28942             }
28943         }
28944         
28945         return cfg;
28946     },
28947     
28948     initEvents : function() 
28949     {
28950         this.el.on('mouseover', this.onMouseOver, this);
28951         this.el.on('mouseout', this.onMouseOut, this);
28952         
28953         this.el.select('a', true).first().on('click', this.onClick, this);
28954         
28955     },
28956     
28957     onClick : function(e)
28958     {
28959         if(this.preventDefault){
28960             e.preventDefault();
28961         }
28962         
28963         this.fireEvent("click", this, e);
28964     },
28965     
28966     onMouseOver : function(e)
28967     {
28968         if(this.submenu && this.pos == 'left'){
28969             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28970         }
28971         
28972         this.fireEvent("mouseover", this, e);
28973     },
28974     
28975     onMouseOut : function(e)
28976     {
28977         this.fireEvent("mouseout", this, e);
28978     }
28979 });
28980
28981  
28982
28983  /*
28984  * - LGPL
28985  *
28986  * menu separator
28987  * 
28988  */
28989 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28990
28991 /**
28992  * @class Roo.bootstrap.menu.Separator
28993  * @extends Roo.bootstrap.Component
28994  * Bootstrap Separator class
28995  * 
28996  * @constructor
28997  * Create a new Separator
28998  * @param {Object} config The config object
28999  */
29000
29001
29002 Roo.bootstrap.menu.Separator = function(config){
29003     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29004 };
29005
29006 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29007     
29008     getAutoCreate : function(){
29009         var cfg = {
29010             tag : 'li',
29011             cls: 'dropdown-divider divider'
29012         };
29013         
29014         return cfg;
29015     }
29016    
29017 });
29018
29019  
29020
29021  /*
29022  * - LGPL
29023  *
29024  * Tooltip
29025  * 
29026  */
29027
29028 /**
29029  * @class Roo.bootstrap.Tooltip
29030  * Bootstrap Tooltip class
29031  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29032  * to determine which dom element triggers the tooltip.
29033  * 
29034  * It needs to add support for additional attributes like tooltip-position
29035  * 
29036  * @constructor
29037  * Create a new Toolti
29038  * @param {Object} config The config object
29039  */
29040
29041 Roo.bootstrap.Tooltip = function(config){
29042     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29043     
29044     this.alignment = Roo.bootstrap.Tooltip.alignment;
29045     
29046     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29047         this.alignment = config.alignment;
29048     }
29049     
29050 };
29051
29052 Roo.apply(Roo.bootstrap.Tooltip, {
29053     /**
29054      * @function init initialize tooltip monitoring.
29055      * @static
29056      */
29057     currentEl : false,
29058     currentTip : false,
29059     currentRegion : false,
29060     
29061     //  init : delay?
29062     
29063     init : function()
29064     {
29065         Roo.get(document).on('mouseover', this.enter ,this);
29066         Roo.get(document).on('mouseout', this.leave, this);
29067          
29068         
29069         this.currentTip = new Roo.bootstrap.Tooltip();
29070     },
29071     
29072     enter : function(ev)
29073     {
29074         var dom = ev.getTarget();
29075         
29076         //Roo.log(['enter',dom]);
29077         var el = Roo.fly(dom);
29078         if (this.currentEl) {
29079             //Roo.log(dom);
29080             //Roo.log(this.currentEl);
29081             //Roo.log(this.currentEl.contains(dom));
29082             if (this.currentEl == el) {
29083                 return;
29084             }
29085             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29086                 return;
29087             }
29088
29089         }
29090         
29091         if (this.currentTip.el) {
29092             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29093         }    
29094         //Roo.log(ev);
29095         
29096         if(!el || el.dom == document){
29097             return;
29098         }
29099         
29100         var bindEl = el; 
29101         var pel = false;
29102         if (!el.attr('tooltip')) {
29103             pel = el.findParent("[tooltip]");
29104             if (pel) {
29105                 bindEl = Roo.get(pel);
29106             }
29107         }
29108         
29109        
29110         
29111         // you can not look for children, as if el is the body.. then everythign is the child..
29112         if (!pel && !el.attr('tooltip')) { //
29113             if (!el.select("[tooltip]").elements.length) {
29114                 return;
29115             }
29116             // is the mouse over this child...?
29117             bindEl = el.select("[tooltip]").first();
29118             var xy = ev.getXY();
29119             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29120                 //Roo.log("not in region.");
29121                 return;
29122             }
29123             //Roo.log("child element over..");
29124             
29125         }
29126         this.currentEl = el;
29127         this.currentTip.bind(bindEl);
29128         this.currentRegion = Roo.lib.Region.getRegion(dom);
29129         this.currentTip.enter();
29130         
29131     },
29132     leave : function(ev)
29133     {
29134         var dom = ev.getTarget();
29135         //Roo.log(['leave',dom]);
29136         if (!this.currentEl) {
29137             return;
29138         }
29139         
29140         
29141         if (dom != this.currentEl.dom) {
29142             return;
29143         }
29144         var xy = ev.getXY();
29145         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29146             return;
29147         }
29148         // only activate leave if mouse cursor is outside... bounding box..
29149         
29150         
29151         
29152         
29153         if (this.currentTip) {
29154             this.currentTip.leave();
29155         }
29156         //Roo.log('clear currentEl');
29157         this.currentEl = false;
29158         
29159         
29160     },
29161     alignment : {
29162         'left' : ['r-l', [-2,0], 'right'],
29163         'right' : ['l-r', [2,0], 'left'],
29164         'bottom' : ['t-b', [0,2], 'top'],
29165         'top' : [ 'b-t', [0,-2], 'bottom']
29166     }
29167     
29168 });
29169
29170
29171 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29172     
29173     
29174     bindEl : false,
29175     
29176     delay : null, // can be { show : 300 , hide: 500}
29177     
29178     timeout : null,
29179     
29180     hoverState : null, //???
29181     
29182     placement : 'bottom', 
29183     
29184     alignment : false,
29185     
29186     getAutoCreate : function(){
29187     
29188         var cfg = {
29189            cls : 'tooltip',   
29190            role : 'tooltip',
29191            cn : [
29192                 {
29193                     cls : 'tooltip-arrow arrow'
29194                 },
29195                 {
29196                     cls : 'tooltip-inner'
29197                 }
29198            ]
29199         };
29200         
29201         return cfg;
29202     },
29203     bind : function(el)
29204     {
29205         this.bindEl = el;
29206     },
29207     
29208     initEvents : function()
29209     {
29210         this.arrowEl = this.el.select('.arrow', true).first();
29211         this.innerEl = this.el.select('.tooltip-inner', true).first();
29212     },
29213     
29214     enter : function () {
29215        
29216         if (this.timeout != null) {
29217             clearTimeout(this.timeout);
29218         }
29219         
29220         this.hoverState = 'in';
29221          //Roo.log("enter - show");
29222         if (!this.delay || !this.delay.show) {
29223             this.show();
29224             return;
29225         }
29226         var _t = this;
29227         this.timeout = setTimeout(function () {
29228             if (_t.hoverState == 'in') {
29229                 _t.show();
29230             }
29231         }, this.delay.show);
29232     },
29233     leave : function()
29234     {
29235         clearTimeout(this.timeout);
29236     
29237         this.hoverState = 'out';
29238          if (!this.delay || !this.delay.hide) {
29239             this.hide();
29240             return;
29241         }
29242        
29243         var _t = this;
29244         this.timeout = setTimeout(function () {
29245             //Roo.log("leave - timeout");
29246             
29247             if (_t.hoverState == 'out') {
29248                 _t.hide();
29249                 Roo.bootstrap.Tooltip.currentEl = false;
29250             }
29251         }, delay);
29252     },
29253     
29254     show : function (msg)
29255     {
29256         if (!this.el) {
29257             this.render(document.body);
29258         }
29259         // set content.
29260         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29261         
29262         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29263         
29264         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29265         
29266         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29267                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29268         
29269         var placement = typeof this.placement == 'function' ?
29270             this.placement.call(this, this.el, on_el) :
29271             this.placement;
29272             
29273         var autoToken = /\s?auto?\s?/i;
29274         var autoPlace = autoToken.test(placement);
29275         if (autoPlace) {
29276             placement = placement.replace(autoToken, '') || 'top';
29277         }
29278         
29279         //this.el.detach()
29280         //this.el.setXY([0,0]);
29281         this.el.show();
29282         //this.el.dom.style.display='block';
29283         
29284         //this.el.appendTo(on_el);
29285         
29286         var p = this.getPosition();
29287         var box = this.el.getBox();
29288         
29289         if (autoPlace) {
29290             // fixme..
29291         }
29292         
29293         var align = this.alignment[placement];
29294         
29295         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29296         
29297         if(placement == 'top' || placement == 'bottom'){
29298             if(xy[0] < 0){
29299                 placement = 'right';
29300             }
29301             
29302             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29303                 placement = 'left';
29304             }
29305             
29306             var scroll = Roo.select('body', true).first().getScroll();
29307             
29308             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29309                 placement = 'top';
29310             }
29311             
29312             align = this.alignment[placement];
29313             
29314             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29315             
29316         }
29317         
29318         var elems = document.getElementsByTagName('div');
29319         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29320         for (var i = 0; i < elems.length; i++) {
29321           var zindex = Number.parseInt(
29322                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29323                 10
29324           );
29325           if (zindex > highest) {
29326             highest = zindex;
29327           }
29328         }
29329         
29330         
29331         
29332         this.el.dom.style.zIndex = highest;
29333         
29334         this.el.alignTo(this.bindEl, align[0],align[1]);
29335         //var arrow = this.el.select('.arrow',true).first();
29336         //arrow.set(align[2], 
29337         
29338         this.el.addClass(placement);
29339         this.el.addClass("bs-tooltip-"+ placement);
29340         
29341         this.el.addClass('in fade show');
29342         
29343         this.hoverState = null;
29344         
29345         if (this.el.hasClass('fade')) {
29346             // fade it?
29347         }
29348         
29349         
29350         
29351         
29352         
29353     },
29354     hide : function()
29355     {
29356          
29357         if (!this.el) {
29358             return;
29359         }
29360         //this.el.setXY([0,0]);
29361         this.el.removeClass(['show', 'in']);
29362         //this.el.hide();
29363         
29364     }
29365     
29366 });
29367  
29368
29369  /*
29370  * - LGPL
29371  *
29372  * Location Picker
29373  * 
29374  */
29375
29376 /**
29377  * @class Roo.bootstrap.LocationPicker
29378  * @extends Roo.bootstrap.Component
29379  * Bootstrap LocationPicker class
29380  * @cfg {Number} latitude Position when init default 0
29381  * @cfg {Number} longitude Position when init default 0
29382  * @cfg {Number} zoom default 15
29383  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29384  * @cfg {Boolean} mapTypeControl default false
29385  * @cfg {Boolean} disableDoubleClickZoom default false
29386  * @cfg {Boolean} scrollwheel default true
29387  * @cfg {Boolean} streetViewControl default false
29388  * @cfg {Number} radius default 0
29389  * @cfg {String} locationName
29390  * @cfg {Boolean} draggable default true
29391  * @cfg {Boolean} enableAutocomplete default false
29392  * @cfg {Boolean} enableReverseGeocode default true
29393  * @cfg {String} markerTitle
29394  * 
29395  * @constructor
29396  * Create a new LocationPicker
29397  * @param {Object} config The config object
29398  */
29399
29400
29401 Roo.bootstrap.LocationPicker = function(config){
29402     
29403     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29404     
29405     this.addEvents({
29406         /**
29407          * @event initial
29408          * Fires when the picker initialized.
29409          * @param {Roo.bootstrap.LocationPicker} this
29410          * @param {Google Location} location
29411          */
29412         initial : true,
29413         /**
29414          * @event positionchanged
29415          * Fires when the picker position changed.
29416          * @param {Roo.bootstrap.LocationPicker} this
29417          * @param {Google Location} location
29418          */
29419         positionchanged : true,
29420         /**
29421          * @event resize
29422          * Fires when the map resize.
29423          * @param {Roo.bootstrap.LocationPicker} this
29424          */
29425         resize : true,
29426         /**
29427          * @event show
29428          * Fires when the map show.
29429          * @param {Roo.bootstrap.LocationPicker} this
29430          */
29431         show : true,
29432         /**
29433          * @event hide
29434          * Fires when the map hide.
29435          * @param {Roo.bootstrap.LocationPicker} this
29436          */
29437         hide : true,
29438         /**
29439          * @event mapClick
29440          * Fires when click the map.
29441          * @param {Roo.bootstrap.LocationPicker} this
29442          * @param {Map event} e
29443          */
29444         mapClick : true,
29445         /**
29446          * @event mapRightClick
29447          * Fires when right click the map.
29448          * @param {Roo.bootstrap.LocationPicker} this
29449          * @param {Map event} e
29450          */
29451         mapRightClick : true,
29452         /**
29453          * @event markerClick
29454          * Fires when click the marker.
29455          * @param {Roo.bootstrap.LocationPicker} this
29456          * @param {Map event} e
29457          */
29458         markerClick : true,
29459         /**
29460          * @event markerRightClick
29461          * Fires when right click the marker.
29462          * @param {Roo.bootstrap.LocationPicker} this
29463          * @param {Map event} e
29464          */
29465         markerRightClick : true,
29466         /**
29467          * @event OverlayViewDraw
29468          * Fires when OverlayView Draw
29469          * @param {Roo.bootstrap.LocationPicker} this
29470          */
29471         OverlayViewDraw : true,
29472         /**
29473          * @event OverlayViewOnAdd
29474          * Fires when OverlayView Draw
29475          * @param {Roo.bootstrap.LocationPicker} this
29476          */
29477         OverlayViewOnAdd : true,
29478         /**
29479          * @event OverlayViewOnRemove
29480          * Fires when OverlayView Draw
29481          * @param {Roo.bootstrap.LocationPicker} this
29482          */
29483         OverlayViewOnRemove : true,
29484         /**
29485          * @event OverlayViewShow
29486          * Fires when OverlayView Draw
29487          * @param {Roo.bootstrap.LocationPicker} this
29488          * @param {Pixel} cpx
29489          */
29490         OverlayViewShow : true,
29491         /**
29492          * @event OverlayViewHide
29493          * Fires when OverlayView Draw
29494          * @param {Roo.bootstrap.LocationPicker} this
29495          */
29496         OverlayViewHide : true,
29497         /**
29498          * @event loadexception
29499          * Fires when load google lib failed.
29500          * @param {Roo.bootstrap.LocationPicker} this
29501          */
29502         loadexception : true
29503     });
29504         
29505 };
29506
29507 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29508     
29509     gMapContext: false,
29510     
29511     latitude: 0,
29512     longitude: 0,
29513     zoom: 15,
29514     mapTypeId: false,
29515     mapTypeControl: false,
29516     disableDoubleClickZoom: false,
29517     scrollwheel: true,
29518     streetViewControl: false,
29519     radius: 0,
29520     locationName: '',
29521     draggable: true,
29522     enableAutocomplete: false,
29523     enableReverseGeocode: true,
29524     markerTitle: '',
29525     
29526     getAutoCreate: function()
29527     {
29528
29529         var cfg = {
29530             tag: 'div',
29531             cls: 'roo-location-picker'
29532         };
29533         
29534         return cfg
29535     },
29536     
29537     initEvents: function(ct, position)
29538     {       
29539         if(!this.el.getWidth() || this.isApplied()){
29540             return;
29541         }
29542         
29543         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29544         
29545         this.initial();
29546     },
29547     
29548     initial: function()
29549     {
29550         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29551             this.fireEvent('loadexception', this);
29552             return;
29553         }
29554         
29555         if(!this.mapTypeId){
29556             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29557         }
29558         
29559         this.gMapContext = this.GMapContext();
29560         
29561         this.initOverlayView();
29562         
29563         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29564         
29565         var _this = this;
29566                 
29567         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29568             _this.setPosition(_this.gMapContext.marker.position);
29569         });
29570         
29571         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29572             _this.fireEvent('mapClick', this, event);
29573             
29574         });
29575
29576         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29577             _this.fireEvent('mapRightClick', this, event);
29578             
29579         });
29580         
29581         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29582             _this.fireEvent('markerClick', this, event);
29583             
29584         });
29585
29586         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29587             _this.fireEvent('markerRightClick', this, event);
29588             
29589         });
29590         
29591         this.setPosition(this.gMapContext.location);
29592         
29593         this.fireEvent('initial', this, this.gMapContext.location);
29594     },
29595     
29596     initOverlayView: function()
29597     {
29598         var _this = this;
29599         
29600         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29601             
29602             draw: function()
29603             {
29604                 _this.fireEvent('OverlayViewDraw', _this);
29605             },
29606             
29607             onAdd: function()
29608             {
29609                 _this.fireEvent('OverlayViewOnAdd', _this);
29610             },
29611             
29612             onRemove: function()
29613             {
29614                 _this.fireEvent('OverlayViewOnRemove', _this);
29615             },
29616             
29617             show: function(cpx)
29618             {
29619                 _this.fireEvent('OverlayViewShow', _this, cpx);
29620             },
29621             
29622             hide: function()
29623             {
29624                 _this.fireEvent('OverlayViewHide', _this);
29625             }
29626             
29627         });
29628     },
29629     
29630     fromLatLngToContainerPixel: function(event)
29631     {
29632         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29633     },
29634     
29635     isApplied: function() 
29636     {
29637         return this.getGmapContext() == false ? false : true;
29638     },
29639     
29640     getGmapContext: function() 
29641     {
29642         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29643     },
29644     
29645     GMapContext: function() 
29646     {
29647         var position = new google.maps.LatLng(this.latitude, this.longitude);
29648         
29649         var _map = new google.maps.Map(this.el.dom, {
29650             center: position,
29651             zoom: this.zoom,
29652             mapTypeId: this.mapTypeId,
29653             mapTypeControl: this.mapTypeControl,
29654             disableDoubleClickZoom: this.disableDoubleClickZoom,
29655             scrollwheel: this.scrollwheel,
29656             streetViewControl: this.streetViewControl,
29657             locationName: this.locationName,
29658             draggable: this.draggable,
29659             enableAutocomplete: this.enableAutocomplete,
29660             enableReverseGeocode: this.enableReverseGeocode
29661         });
29662         
29663         var _marker = new google.maps.Marker({
29664             position: position,
29665             map: _map,
29666             title: this.markerTitle,
29667             draggable: this.draggable
29668         });
29669         
29670         return {
29671             map: _map,
29672             marker: _marker,
29673             circle: null,
29674             location: position,
29675             radius: this.radius,
29676             locationName: this.locationName,
29677             addressComponents: {
29678                 formatted_address: null,
29679                 addressLine1: null,
29680                 addressLine2: null,
29681                 streetName: null,
29682                 streetNumber: null,
29683                 city: null,
29684                 district: null,
29685                 state: null,
29686                 stateOrProvince: null
29687             },
29688             settings: this,
29689             domContainer: this.el.dom,
29690             geodecoder: new google.maps.Geocoder()
29691         };
29692     },
29693     
29694     drawCircle: function(center, radius, options) 
29695     {
29696         if (this.gMapContext.circle != null) {
29697             this.gMapContext.circle.setMap(null);
29698         }
29699         if (radius > 0) {
29700             radius *= 1;
29701             options = Roo.apply({}, options, {
29702                 strokeColor: "#0000FF",
29703                 strokeOpacity: .35,
29704                 strokeWeight: 2,
29705                 fillColor: "#0000FF",
29706                 fillOpacity: .2
29707             });
29708             
29709             options.map = this.gMapContext.map;
29710             options.radius = radius;
29711             options.center = center;
29712             this.gMapContext.circle = new google.maps.Circle(options);
29713             return this.gMapContext.circle;
29714         }
29715         
29716         return null;
29717     },
29718     
29719     setPosition: function(location) 
29720     {
29721         this.gMapContext.location = location;
29722         this.gMapContext.marker.setPosition(location);
29723         this.gMapContext.map.panTo(location);
29724         this.drawCircle(location, this.gMapContext.radius, {});
29725         
29726         var _this = this;
29727         
29728         if (this.gMapContext.settings.enableReverseGeocode) {
29729             this.gMapContext.geodecoder.geocode({
29730                 latLng: this.gMapContext.location
29731             }, function(results, status) {
29732                 
29733                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29734                     _this.gMapContext.locationName = results[0].formatted_address;
29735                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29736                     
29737                     _this.fireEvent('positionchanged', this, location);
29738                 }
29739             });
29740             
29741             return;
29742         }
29743         
29744         this.fireEvent('positionchanged', this, location);
29745     },
29746     
29747     resize: function()
29748     {
29749         google.maps.event.trigger(this.gMapContext.map, "resize");
29750         
29751         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29752         
29753         this.fireEvent('resize', this);
29754     },
29755     
29756     setPositionByLatLng: function(latitude, longitude)
29757     {
29758         this.setPosition(new google.maps.LatLng(latitude, longitude));
29759     },
29760     
29761     getCurrentPosition: function() 
29762     {
29763         return {
29764             latitude: this.gMapContext.location.lat(),
29765             longitude: this.gMapContext.location.lng()
29766         };
29767     },
29768     
29769     getAddressName: function() 
29770     {
29771         return this.gMapContext.locationName;
29772     },
29773     
29774     getAddressComponents: function() 
29775     {
29776         return this.gMapContext.addressComponents;
29777     },
29778     
29779     address_component_from_google_geocode: function(address_components) 
29780     {
29781         var result = {};
29782         
29783         for (var i = 0; i < address_components.length; i++) {
29784             var component = address_components[i];
29785             if (component.types.indexOf("postal_code") >= 0) {
29786                 result.postalCode = component.short_name;
29787             } else if (component.types.indexOf("street_number") >= 0) {
29788                 result.streetNumber = component.short_name;
29789             } else if (component.types.indexOf("route") >= 0) {
29790                 result.streetName = component.short_name;
29791             } else if (component.types.indexOf("neighborhood") >= 0) {
29792                 result.city = component.short_name;
29793             } else if (component.types.indexOf("locality") >= 0) {
29794                 result.city = component.short_name;
29795             } else if (component.types.indexOf("sublocality") >= 0) {
29796                 result.district = component.short_name;
29797             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29798                 result.stateOrProvince = component.short_name;
29799             } else if (component.types.indexOf("country") >= 0) {
29800                 result.country = component.short_name;
29801             }
29802         }
29803         
29804         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29805         result.addressLine2 = "";
29806         return result;
29807     },
29808     
29809     setZoomLevel: function(zoom)
29810     {
29811         this.gMapContext.map.setZoom(zoom);
29812     },
29813     
29814     show: function()
29815     {
29816         if(!this.el){
29817             return;
29818         }
29819         
29820         this.el.show();
29821         
29822         this.resize();
29823         
29824         this.fireEvent('show', this);
29825     },
29826     
29827     hide: function()
29828     {
29829         if(!this.el){
29830             return;
29831         }
29832         
29833         this.el.hide();
29834         
29835         this.fireEvent('hide', this);
29836     }
29837     
29838 });
29839
29840 Roo.apply(Roo.bootstrap.LocationPicker, {
29841     
29842     OverlayView : function(map, options)
29843     {
29844         options = options || {};
29845         
29846         this.setMap(map);
29847     }
29848     
29849     
29850 });/**
29851  * @class Roo.bootstrap.Alert
29852  * @extends Roo.bootstrap.Component
29853  * Bootstrap Alert class - shows an alert area box
29854  * eg
29855  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29856   Enter a valid email address
29857 </div>
29858  * @licence LGPL
29859  * @cfg {String} title The title of alert
29860  * @cfg {String} html The content of alert
29861  * @cfg {String} weight (  success | info | warning | danger )
29862  * @cfg {String} fa font-awesomeicon
29863  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29864  * @cfg {Boolean} close true to show a x closer
29865  * 
29866  * 
29867  * @constructor
29868  * Create a new alert
29869  * @param {Object} config The config object
29870  */
29871
29872
29873 Roo.bootstrap.Alert = function(config){
29874     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29875     
29876 };
29877
29878 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29879     
29880     title: '',
29881     html: '',
29882     weight: false,
29883     fa: false,
29884     faicon: false, // BC
29885     close : false,
29886     
29887     
29888     getAutoCreate : function()
29889     {
29890         
29891         var cfg = {
29892             tag : 'div',
29893             cls : 'alert',
29894             cn : [
29895                 {
29896                     tag: 'button',
29897                     type :  "button",
29898                     cls: "close",
29899                     html : '×',
29900                     style : this.close ? '' : 'display:none'
29901                 },
29902                 {
29903                     tag : 'i',
29904                     cls : 'roo-alert-icon'
29905                     
29906                 },
29907                 {
29908                     tag : 'b',
29909                     cls : 'roo-alert-title',
29910                     html : this.title
29911                 },
29912                 {
29913                     tag : 'span',
29914                     cls : 'roo-alert-text',
29915                     html : this.html
29916                 }
29917             ]
29918         };
29919         
29920         if(this.faicon){
29921             cfg.cn[0].cls += ' fa ' + this.faicon;
29922         }
29923         if(this.fa){
29924             cfg.cn[0].cls += ' fa ' + this.fa;
29925         }
29926         
29927         if(this.weight){
29928             cfg.cls += ' alert-' + this.weight;
29929         }
29930         
29931         return cfg;
29932     },
29933     
29934     initEvents: function() 
29935     {
29936         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29937         this.titleEl =  this.el.select('.roo-alert-title',true).first();
29938         this.iconEl = this.el.select('.roo-alert-icon',true).first();
29939         if (this.seconds > 0) {
29940             this.hide.defer(this.seconds, this);
29941         }
29942     },
29943     
29944     setTitle : function(str)
29945     {
29946         this.titleEl.dom.innerHTML = str;
29947     },
29948     
29949     setText : function(str)
29950     {
29951         this.titleEl.dom.innerHTML = str;
29952     },
29953     
29954     setWeight : function(weight)
29955     {
29956         if(this.weight){
29957             this.el.removeClass('alert-' + this.weight);
29958         }
29959         
29960         this.weight = weight;
29961         
29962         this.el.addClass('alert-' + this.weight);
29963     },
29964     
29965     setIcon : function(icon)
29966     {
29967         if(this.faicon){
29968             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29969         }
29970         
29971         this.faicon = icon;
29972         
29973         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
29974     },
29975     
29976     hide: function() 
29977     {
29978         this.el.hide();   
29979     },
29980     
29981     show: function() 
29982     {  
29983         this.el.show();   
29984     }
29985     
29986 });
29987
29988  
29989 /*
29990 * Licence: LGPL
29991 */
29992
29993 /**
29994  * @class Roo.bootstrap.UploadCropbox
29995  * @extends Roo.bootstrap.Component
29996  * Bootstrap UploadCropbox class
29997  * @cfg {String} emptyText show when image has been loaded
29998  * @cfg {String} rotateNotify show when image too small to rotate
29999  * @cfg {Number} errorTimeout default 3000
30000  * @cfg {Number} minWidth default 300
30001  * @cfg {Number} minHeight default 300
30002  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30003  * @cfg {Boolean} isDocument (true|false) default false
30004  * @cfg {String} url action url
30005  * @cfg {String} paramName default 'imageUpload'
30006  * @cfg {String} method default POST
30007  * @cfg {Boolean} loadMask (true|false) default true
30008  * @cfg {Boolean} loadingText default 'Loading...'
30009  * 
30010  * @constructor
30011  * Create a new UploadCropbox
30012  * @param {Object} config The config object
30013  */
30014
30015 Roo.bootstrap.UploadCropbox = function(config){
30016     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30017     
30018     this.addEvents({
30019         /**
30020          * @event beforeselectfile
30021          * Fire before select file
30022          * @param {Roo.bootstrap.UploadCropbox} this
30023          */
30024         "beforeselectfile" : true,
30025         /**
30026          * @event initial
30027          * Fire after initEvent
30028          * @param {Roo.bootstrap.UploadCropbox} this
30029          */
30030         "initial" : true,
30031         /**
30032          * @event crop
30033          * Fire after initEvent
30034          * @param {Roo.bootstrap.UploadCropbox} this
30035          * @param {String} data
30036          */
30037         "crop" : true,
30038         /**
30039          * @event prepare
30040          * Fire when preparing the file data
30041          * @param {Roo.bootstrap.UploadCropbox} this
30042          * @param {Object} file
30043          */
30044         "prepare" : true,
30045         /**
30046          * @event exception
30047          * Fire when get exception
30048          * @param {Roo.bootstrap.UploadCropbox} this
30049          * @param {XMLHttpRequest} xhr
30050          */
30051         "exception" : true,
30052         /**
30053          * @event beforeloadcanvas
30054          * Fire before load the canvas
30055          * @param {Roo.bootstrap.UploadCropbox} this
30056          * @param {String} src
30057          */
30058         "beforeloadcanvas" : true,
30059         /**
30060          * @event trash
30061          * Fire when trash image
30062          * @param {Roo.bootstrap.UploadCropbox} this
30063          */
30064         "trash" : true,
30065         /**
30066          * @event download
30067          * Fire when download the image
30068          * @param {Roo.bootstrap.UploadCropbox} this
30069          */
30070         "download" : true,
30071         /**
30072          * @event footerbuttonclick
30073          * Fire when footerbuttonclick
30074          * @param {Roo.bootstrap.UploadCropbox} this
30075          * @param {String} type
30076          */
30077         "footerbuttonclick" : true,
30078         /**
30079          * @event resize
30080          * Fire when resize
30081          * @param {Roo.bootstrap.UploadCropbox} this
30082          */
30083         "resize" : true,
30084         /**
30085          * @event rotate
30086          * Fire when rotate the image
30087          * @param {Roo.bootstrap.UploadCropbox} this
30088          * @param {String} pos
30089          */
30090         "rotate" : true,
30091         /**
30092          * @event inspect
30093          * Fire when inspect the file
30094          * @param {Roo.bootstrap.UploadCropbox} this
30095          * @param {Object} file
30096          */
30097         "inspect" : true,
30098         /**
30099          * @event upload
30100          * Fire when xhr upload the file
30101          * @param {Roo.bootstrap.UploadCropbox} this
30102          * @param {Object} data
30103          */
30104         "upload" : true,
30105         /**
30106          * @event arrange
30107          * Fire when arrange the file data
30108          * @param {Roo.bootstrap.UploadCropbox} this
30109          * @param {Object} formData
30110          */
30111         "arrange" : true
30112     });
30113     
30114     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30115 };
30116
30117 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30118     
30119     emptyText : 'Click to upload image',
30120     rotateNotify : 'Image is too small to rotate',
30121     errorTimeout : 3000,
30122     scale : 0,
30123     baseScale : 1,
30124     rotate : 0,
30125     dragable : false,
30126     pinching : false,
30127     mouseX : 0,
30128     mouseY : 0,
30129     cropData : false,
30130     minWidth : 300,
30131     minHeight : 300,
30132     file : false,
30133     exif : {},
30134     baseRotate : 1,
30135     cropType : 'image/jpeg',
30136     buttons : false,
30137     canvasLoaded : false,
30138     isDocument : false,
30139     method : 'POST',
30140     paramName : 'imageUpload',
30141     loadMask : true,
30142     loadingText : 'Loading...',
30143     maskEl : false,
30144     
30145     getAutoCreate : function()
30146     {
30147         var cfg = {
30148             tag : 'div',
30149             cls : 'roo-upload-cropbox',
30150             cn : [
30151                 {
30152                     tag : 'input',
30153                     cls : 'roo-upload-cropbox-selector',
30154                     type : 'file'
30155                 },
30156                 {
30157                     tag : 'div',
30158                     cls : 'roo-upload-cropbox-body',
30159                     style : 'cursor:pointer',
30160                     cn : [
30161                         {
30162                             tag : 'div',
30163                             cls : 'roo-upload-cropbox-preview'
30164                         },
30165                         {
30166                             tag : 'div',
30167                             cls : 'roo-upload-cropbox-thumb'
30168                         },
30169                         {
30170                             tag : 'div',
30171                             cls : 'roo-upload-cropbox-empty-notify',
30172                             html : this.emptyText
30173                         },
30174                         {
30175                             tag : 'div',
30176                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30177                             html : this.rotateNotify
30178                         }
30179                     ]
30180                 },
30181                 {
30182                     tag : 'div',
30183                     cls : 'roo-upload-cropbox-footer',
30184                     cn : {
30185                         tag : 'div',
30186                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30187                         cn : []
30188                     }
30189                 }
30190             ]
30191         };
30192         
30193         return cfg;
30194     },
30195     
30196     onRender : function(ct, position)
30197     {
30198         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30199         
30200         if (this.buttons.length) {
30201             
30202             Roo.each(this.buttons, function(bb) {
30203                 
30204                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30205                 
30206                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30207                 
30208             }, this);
30209         }
30210         
30211         if(this.loadMask){
30212             this.maskEl = this.el;
30213         }
30214     },
30215     
30216     initEvents : function()
30217     {
30218         this.urlAPI = (window.createObjectURL && window) || 
30219                                 (window.URL && URL.revokeObjectURL && URL) || 
30220                                 (window.webkitURL && webkitURL);
30221                         
30222         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30223         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30224         
30225         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30226         this.selectorEl.hide();
30227         
30228         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30229         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30230         
30231         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30232         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30233         this.thumbEl.hide();
30234         
30235         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30236         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30237         
30238         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30239         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30240         this.errorEl.hide();
30241         
30242         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30243         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30244         this.footerEl.hide();
30245         
30246         this.setThumbBoxSize();
30247         
30248         this.bind();
30249         
30250         this.resize();
30251         
30252         this.fireEvent('initial', this);
30253     },
30254
30255     bind : function()
30256     {
30257         var _this = this;
30258         
30259         window.addEventListener("resize", function() { _this.resize(); } );
30260         
30261         this.bodyEl.on('click', this.beforeSelectFile, this);
30262         
30263         if(Roo.isTouch){
30264             this.bodyEl.on('touchstart', this.onTouchStart, this);
30265             this.bodyEl.on('touchmove', this.onTouchMove, this);
30266             this.bodyEl.on('touchend', this.onTouchEnd, this);
30267         }
30268         
30269         if(!Roo.isTouch){
30270             this.bodyEl.on('mousedown', this.onMouseDown, this);
30271             this.bodyEl.on('mousemove', this.onMouseMove, this);
30272             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30273             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30274             Roo.get(document).on('mouseup', this.onMouseUp, this);
30275         }
30276         
30277         this.selectorEl.on('change', this.onFileSelected, this);
30278     },
30279     
30280     reset : function()
30281     {    
30282         this.scale = 0;
30283         this.baseScale = 1;
30284         this.rotate = 0;
30285         this.baseRotate = 1;
30286         this.dragable = false;
30287         this.pinching = false;
30288         this.mouseX = 0;
30289         this.mouseY = 0;
30290         this.cropData = false;
30291         this.notifyEl.dom.innerHTML = this.emptyText;
30292         
30293         this.selectorEl.dom.value = '';
30294         
30295     },
30296     
30297     resize : function()
30298     {
30299         if(this.fireEvent('resize', this) != false){
30300             this.setThumbBoxPosition();
30301             this.setCanvasPosition();
30302         }
30303     },
30304     
30305     onFooterButtonClick : function(e, el, o, type)
30306     {
30307         switch (type) {
30308             case 'rotate-left' :
30309                 this.onRotateLeft(e);
30310                 break;
30311             case 'rotate-right' :
30312                 this.onRotateRight(e);
30313                 break;
30314             case 'picture' :
30315                 this.beforeSelectFile(e);
30316                 break;
30317             case 'trash' :
30318                 this.trash(e);
30319                 break;
30320             case 'crop' :
30321                 this.crop(e);
30322                 break;
30323             case 'download' :
30324                 this.download(e);
30325                 break;
30326             default :
30327                 break;
30328         }
30329         
30330         this.fireEvent('footerbuttonclick', this, type);
30331     },
30332     
30333     beforeSelectFile : function(e)
30334     {
30335         e.preventDefault();
30336         
30337         if(this.fireEvent('beforeselectfile', this) != false){
30338             this.selectorEl.dom.click();
30339         }
30340     },
30341     
30342     onFileSelected : function(e)
30343     {
30344         e.preventDefault();
30345         
30346         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30347             return;
30348         }
30349         
30350         var file = this.selectorEl.dom.files[0];
30351         
30352         if(this.fireEvent('inspect', this, file) != false){
30353             this.prepare(file);
30354         }
30355         
30356     },
30357     
30358     trash : function(e)
30359     {
30360         this.fireEvent('trash', this);
30361     },
30362     
30363     download : function(e)
30364     {
30365         this.fireEvent('download', this);
30366     },
30367     
30368     loadCanvas : function(src)
30369     {   
30370         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30371             
30372             this.reset();
30373             
30374             this.imageEl = document.createElement('img');
30375             
30376             var _this = this;
30377             
30378             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30379             
30380             this.imageEl.src = src;
30381         }
30382     },
30383     
30384     onLoadCanvas : function()
30385     {   
30386         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30387         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30388         
30389         this.bodyEl.un('click', this.beforeSelectFile, this);
30390         
30391         this.notifyEl.hide();
30392         this.thumbEl.show();
30393         this.footerEl.show();
30394         
30395         this.baseRotateLevel();
30396         
30397         if(this.isDocument){
30398             this.setThumbBoxSize();
30399         }
30400         
30401         this.setThumbBoxPosition();
30402         
30403         this.baseScaleLevel();
30404         
30405         this.draw();
30406         
30407         this.resize();
30408         
30409         this.canvasLoaded = true;
30410         
30411         if(this.loadMask){
30412             this.maskEl.unmask();
30413         }
30414         
30415     },
30416     
30417     setCanvasPosition : function()
30418     {   
30419         if(!this.canvasEl){
30420             return;
30421         }
30422         
30423         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30424         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30425         
30426         this.previewEl.setLeft(pw);
30427         this.previewEl.setTop(ph);
30428         
30429     },
30430     
30431     onMouseDown : function(e)
30432     {   
30433         e.stopEvent();
30434         
30435         this.dragable = true;
30436         this.pinching = false;
30437         
30438         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30439             this.dragable = false;
30440             return;
30441         }
30442         
30443         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30444         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30445         
30446     },
30447     
30448     onMouseMove : function(e)
30449     {   
30450         e.stopEvent();
30451         
30452         if(!this.canvasLoaded){
30453             return;
30454         }
30455         
30456         if (!this.dragable){
30457             return;
30458         }
30459         
30460         var minX = Math.ceil(this.thumbEl.getLeft(true));
30461         var minY = Math.ceil(this.thumbEl.getTop(true));
30462         
30463         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30464         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30465         
30466         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30467         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30468         
30469         x = x - this.mouseX;
30470         y = y - this.mouseY;
30471         
30472         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30473         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30474         
30475         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30476         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30477         
30478         this.previewEl.setLeft(bgX);
30479         this.previewEl.setTop(bgY);
30480         
30481         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30482         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30483     },
30484     
30485     onMouseUp : function(e)
30486     {   
30487         e.stopEvent();
30488         
30489         this.dragable = false;
30490     },
30491     
30492     onMouseWheel : function(e)
30493     {   
30494         e.stopEvent();
30495         
30496         this.startScale = this.scale;
30497         
30498         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30499         
30500         if(!this.zoomable()){
30501             this.scale = this.startScale;
30502             return;
30503         }
30504         
30505         this.draw();
30506         
30507         return;
30508     },
30509     
30510     zoomable : function()
30511     {
30512         var minScale = this.thumbEl.getWidth() / this.minWidth;
30513         
30514         if(this.minWidth < this.minHeight){
30515             minScale = this.thumbEl.getHeight() / this.minHeight;
30516         }
30517         
30518         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30519         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30520         
30521         if(
30522                 this.isDocument &&
30523                 (this.rotate == 0 || this.rotate == 180) && 
30524                 (
30525                     width > this.imageEl.OriginWidth || 
30526                     height > this.imageEl.OriginHeight ||
30527                     (width < this.minWidth && height < this.minHeight)
30528                 )
30529         ){
30530             return false;
30531         }
30532         
30533         if(
30534                 this.isDocument &&
30535                 (this.rotate == 90 || this.rotate == 270) && 
30536                 (
30537                     width > this.imageEl.OriginWidth || 
30538                     height > this.imageEl.OriginHeight ||
30539                     (width < this.minHeight && height < this.minWidth)
30540                 )
30541         ){
30542             return false;
30543         }
30544         
30545         if(
30546                 !this.isDocument &&
30547                 (this.rotate == 0 || this.rotate == 180) && 
30548                 (
30549                     width < this.minWidth || 
30550                     width > this.imageEl.OriginWidth || 
30551                     height < this.minHeight || 
30552                     height > this.imageEl.OriginHeight
30553                 )
30554         ){
30555             return false;
30556         }
30557         
30558         if(
30559                 !this.isDocument &&
30560                 (this.rotate == 90 || this.rotate == 270) && 
30561                 (
30562                     width < this.minHeight || 
30563                     width > this.imageEl.OriginWidth || 
30564                     height < this.minWidth || 
30565                     height > this.imageEl.OriginHeight
30566                 )
30567         ){
30568             return false;
30569         }
30570         
30571         return true;
30572         
30573     },
30574     
30575     onRotateLeft : function(e)
30576     {   
30577         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30578             
30579             var minScale = this.thumbEl.getWidth() / this.minWidth;
30580             
30581             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30582             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30583             
30584             this.startScale = this.scale;
30585             
30586             while (this.getScaleLevel() < minScale){
30587             
30588                 this.scale = this.scale + 1;
30589                 
30590                 if(!this.zoomable()){
30591                     break;
30592                 }
30593                 
30594                 if(
30595                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30596                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30597                 ){
30598                     continue;
30599                 }
30600                 
30601                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30602
30603                 this.draw();
30604                 
30605                 return;
30606             }
30607             
30608             this.scale = this.startScale;
30609             
30610             this.onRotateFail();
30611             
30612             return false;
30613         }
30614         
30615         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30616
30617         if(this.isDocument){
30618             this.setThumbBoxSize();
30619             this.setThumbBoxPosition();
30620             this.setCanvasPosition();
30621         }
30622         
30623         this.draw();
30624         
30625         this.fireEvent('rotate', this, 'left');
30626         
30627     },
30628     
30629     onRotateRight : function(e)
30630     {
30631         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30632             
30633             var minScale = this.thumbEl.getWidth() / this.minWidth;
30634         
30635             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30636             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30637             
30638             this.startScale = this.scale;
30639             
30640             while (this.getScaleLevel() < minScale){
30641             
30642                 this.scale = this.scale + 1;
30643                 
30644                 if(!this.zoomable()){
30645                     break;
30646                 }
30647                 
30648                 if(
30649                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30650                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30651                 ){
30652                     continue;
30653                 }
30654                 
30655                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30656
30657                 this.draw();
30658                 
30659                 return;
30660             }
30661             
30662             this.scale = this.startScale;
30663             
30664             this.onRotateFail();
30665             
30666             return false;
30667         }
30668         
30669         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30670
30671         if(this.isDocument){
30672             this.setThumbBoxSize();
30673             this.setThumbBoxPosition();
30674             this.setCanvasPosition();
30675         }
30676         
30677         this.draw();
30678         
30679         this.fireEvent('rotate', this, 'right');
30680     },
30681     
30682     onRotateFail : function()
30683     {
30684         this.errorEl.show(true);
30685         
30686         var _this = this;
30687         
30688         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30689     },
30690     
30691     draw : function()
30692     {
30693         this.previewEl.dom.innerHTML = '';
30694         
30695         var canvasEl = document.createElement("canvas");
30696         
30697         var contextEl = canvasEl.getContext("2d");
30698         
30699         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30700         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30701         var center = this.imageEl.OriginWidth / 2;
30702         
30703         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30704             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30705             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30706             center = this.imageEl.OriginHeight / 2;
30707         }
30708         
30709         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30710         
30711         contextEl.translate(center, center);
30712         contextEl.rotate(this.rotate * Math.PI / 180);
30713
30714         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30715         
30716         this.canvasEl = document.createElement("canvas");
30717         
30718         this.contextEl = this.canvasEl.getContext("2d");
30719         
30720         switch (this.rotate) {
30721             case 0 :
30722                 
30723                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30724                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30725                 
30726                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30727                 
30728                 break;
30729             case 90 : 
30730                 
30731                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30732                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30733                 
30734                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30735                     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);
30736                     break;
30737                 }
30738                 
30739                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30740                 
30741                 break;
30742             case 180 :
30743                 
30744                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30745                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30746                 
30747                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30748                     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);
30749                     break;
30750                 }
30751                 
30752                 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);
30753                 
30754                 break;
30755             case 270 :
30756                 
30757                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30758                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30759         
30760                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30761                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30762                     break;
30763                 }
30764                 
30765                 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);
30766                 
30767                 break;
30768             default : 
30769                 break;
30770         }
30771         
30772         this.previewEl.appendChild(this.canvasEl);
30773         
30774         this.setCanvasPosition();
30775     },
30776     
30777     crop : function()
30778     {
30779         if(!this.canvasLoaded){
30780             return;
30781         }
30782         
30783         var imageCanvas = document.createElement("canvas");
30784         
30785         var imageContext = imageCanvas.getContext("2d");
30786         
30787         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30788         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30789         
30790         var center = imageCanvas.width / 2;
30791         
30792         imageContext.translate(center, center);
30793         
30794         imageContext.rotate(this.rotate * Math.PI / 180);
30795         
30796         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30797         
30798         var canvas = document.createElement("canvas");
30799         
30800         var context = canvas.getContext("2d");
30801                 
30802         canvas.width = this.minWidth;
30803         canvas.height = this.minHeight;
30804
30805         switch (this.rotate) {
30806             case 0 :
30807                 
30808                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30809                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30810                 
30811                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30812                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30813                 
30814                 var targetWidth = this.minWidth - 2 * x;
30815                 var targetHeight = this.minHeight - 2 * y;
30816                 
30817                 var scale = 1;
30818                 
30819                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30820                     scale = targetWidth / width;
30821                 }
30822                 
30823                 if(x > 0 && y == 0){
30824                     scale = targetHeight / height;
30825                 }
30826                 
30827                 if(x > 0 && y > 0){
30828                     scale = targetWidth / width;
30829                     
30830                     if(width < height){
30831                         scale = targetHeight / height;
30832                     }
30833                 }
30834                 
30835                 context.scale(scale, scale);
30836                 
30837                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30838                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30839
30840                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30841                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30842
30843                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30844                 
30845                 break;
30846             case 90 : 
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                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30884                 
30885                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30886                 
30887                 break;
30888             case 180 :
30889                 
30890                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30891                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30892                 
30893                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30894                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30895                 
30896                 var targetWidth = this.minWidth - 2 * x;
30897                 var targetHeight = this.minHeight - 2 * y;
30898                 
30899                 var scale = 1;
30900                 
30901                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30902                     scale = targetWidth / width;
30903                 }
30904                 
30905                 if(x > 0 && y == 0){
30906                     scale = targetHeight / height;
30907                 }
30908                 
30909                 if(x > 0 && y > 0){
30910                     scale = targetWidth / width;
30911                     
30912                     if(width < height){
30913                         scale = targetHeight / height;
30914                     }
30915                 }
30916                 
30917                 context.scale(scale, scale);
30918                 
30919                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30920                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30921
30922                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30923                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30924
30925                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30926                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30927                 
30928                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30929                 
30930                 break;
30931             case 270 :
30932                 
30933                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30934                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30935                 
30936                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30937                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30938                 
30939                 var targetWidth = this.minWidth - 2 * x;
30940                 var targetHeight = this.minHeight - 2 * y;
30941                 
30942                 var scale = 1;
30943                 
30944                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30945                     scale = targetWidth / width;
30946                 }
30947                 
30948                 if(x > 0 && y == 0){
30949                     scale = targetHeight / height;
30950                 }
30951                 
30952                 if(x > 0 && y > 0){
30953                     scale = targetWidth / width;
30954                     
30955                     if(width < height){
30956                         scale = targetHeight / height;
30957                     }
30958                 }
30959                 
30960                 context.scale(scale, scale);
30961                 
30962                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30963                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30964
30965                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30966                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30967                 
30968                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30969                 
30970                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30971                 
30972                 break;
30973             default : 
30974                 break;
30975         }
30976         
30977         this.cropData = canvas.toDataURL(this.cropType);
30978         
30979         if(this.fireEvent('crop', this, this.cropData) !== false){
30980             this.process(this.file, this.cropData);
30981         }
30982         
30983         return;
30984         
30985     },
30986     
30987     setThumbBoxSize : function()
30988     {
30989         var width, height;
30990         
30991         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30992             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30993             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30994             
30995             this.minWidth = width;
30996             this.minHeight = height;
30997             
30998             if(this.rotate == 90 || this.rotate == 270){
30999                 this.minWidth = height;
31000                 this.minHeight = width;
31001             }
31002         }
31003         
31004         height = 300;
31005         width = Math.ceil(this.minWidth * height / this.minHeight);
31006         
31007         if(this.minWidth > this.minHeight){
31008             width = 300;
31009             height = Math.ceil(this.minHeight * width / this.minWidth);
31010         }
31011         
31012         this.thumbEl.setStyle({
31013             width : width + 'px',
31014             height : height + 'px'
31015         });
31016
31017         return;
31018             
31019     },
31020     
31021     setThumbBoxPosition : function()
31022     {
31023         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31024         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31025         
31026         this.thumbEl.setLeft(x);
31027         this.thumbEl.setTop(y);
31028         
31029     },
31030     
31031     baseRotateLevel : function()
31032     {
31033         this.baseRotate = 1;
31034         
31035         if(
31036                 typeof(this.exif) != 'undefined' &&
31037                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31038                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31039         ){
31040             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31041         }
31042         
31043         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31044         
31045     },
31046     
31047     baseScaleLevel : function()
31048     {
31049         var width, height;
31050         
31051         if(this.isDocument){
31052             
31053             if(this.baseRotate == 6 || this.baseRotate == 8){
31054             
31055                 height = this.thumbEl.getHeight();
31056                 this.baseScale = height / this.imageEl.OriginWidth;
31057
31058                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31059                     width = this.thumbEl.getWidth();
31060                     this.baseScale = width / this.imageEl.OriginHeight;
31061                 }
31062
31063                 return;
31064             }
31065
31066             height = this.thumbEl.getHeight();
31067             this.baseScale = height / this.imageEl.OriginHeight;
31068
31069             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31070                 width = this.thumbEl.getWidth();
31071                 this.baseScale = width / this.imageEl.OriginWidth;
31072             }
31073
31074             return;
31075         }
31076         
31077         if(this.baseRotate == 6 || this.baseRotate == 8){
31078             
31079             width = this.thumbEl.getHeight();
31080             this.baseScale = width / this.imageEl.OriginHeight;
31081             
31082             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31083                 height = this.thumbEl.getWidth();
31084                 this.baseScale = height / this.imageEl.OriginHeight;
31085             }
31086             
31087             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31088                 height = this.thumbEl.getWidth();
31089                 this.baseScale = height / this.imageEl.OriginHeight;
31090                 
31091                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31092                     width = this.thumbEl.getHeight();
31093                     this.baseScale = width / this.imageEl.OriginWidth;
31094                 }
31095             }
31096             
31097             return;
31098         }
31099         
31100         width = this.thumbEl.getWidth();
31101         this.baseScale = width / this.imageEl.OriginWidth;
31102         
31103         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31104             height = this.thumbEl.getHeight();
31105             this.baseScale = height / this.imageEl.OriginHeight;
31106         }
31107         
31108         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31109             
31110             height = this.thumbEl.getHeight();
31111             this.baseScale = height / this.imageEl.OriginHeight;
31112             
31113             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31114                 width = this.thumbEl.getWidth();
31115                 this.baseScale = width / this.imageEl.OriginWidth;
31116             }
31117             
31118         }
31119         
31120         return;
31121     },
31122     
31123     getScaleLevel : function()
31124     {
31125         return this.baseScale * Math.pow(1.1, this.scale);
31126     },
31127     
31128     onTouchStart : function(e)
31129     {
31130         if(!this.canvasLoaded){
31131             this.beforeSelectFile(e);
31132             return;
31133         }
31134         
31135         var touches = e.browserEvent.touches;
31136         
31137         if(!touches){
31138             return;
31139         }
31140         
31141         if(touches.length == 1){
31142             this.onMouseDown(e);
31143             return;
31144         }
31145         
31146         if(touches.length != 2){
31147             return;
31148         }
31149         
31150         var coords = [];
31151         
31152         for(var i = 0, finger; finger = touches[i]; i++){
31153             coords.push(finger.pageX, finger.pageY);
31154         }
31155         
31156         var x = Math.pow(coords[0] - coords[2], 2);
31157         var y = Math.pow(coords[1] - coords[3], 2);
31158         
31159         this.startDistance = Math.sqrt(x + y);
31160         
31161         this.startScale = this.scale;
31162         
31163         this.pinching = true;
31164         this.dragable = false;
31165         
31166     },
31167     
31168     onTouchMove : function(e)
31169     {
31170         if(!this.pinching && !this.dragable){
31171             return;
31172         }
31173         
31174         var touches = e.browserEvent.touches;
31175         
31176         if(!touches){
31177             return;
31178         }
31179         
31180         if(this.dragable){
31181             this.onMouseMove(e);
31182             return;
31183         }
31184         
31185         var coords = [];
31186         
31187         for(var i = 0, finger; finger = touches[i]; i++){
31188             coords.push(finger.pageX, finger.pageY);
31189         }
31190         
31191         var x = Math.pow(coords[0] - coords[2], 2);
31192         var y = Math.pow(coords[1] - coords[3], 2);
31193         
31194         this.endDistance = Math.sqrt(x + y);
31195         
31196         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31197         
31198         if(!this.zoomable()){
31199             this.scale = this.startScale;
31200             return;
31201         }
31202         
31203         this.draw();
31204         
31205     },
31206     
31207     onTouchEnd : function(e)
31208     {
31209         this.pinching = false;
31210         this.dragable = false;
31211         
31212     },
31213     
31214     process : function(file, crop)
31215     {
31216         if(this.loadMask){
31217             this.maskEl.mask(this.loadingText);
31218         }
31219         
31220         this.xhr = new XMLHttpRequest();
31221         
31222         file.xhr = this.xhr;
31223
31224         this.xhr.open(this.method, this.url, true);
31225         
31226         var headers = {
31227             "Accept": "application/json",
31228             "Cache-Control": "no-cache",
31229             "X-Requested-With": "XMLHttpRequest"
31230         };
31231         
31232         for (var headerName in headers) {
31233             var headerValue = headers[headerName];
31234             if (headerValue) {
31235                 this.xhr.setRequestHeader(headerName, headerValue);
31236             }
31237         }
31238         
31239         var _this = this;
31240         
31241         this.xhr.onload = function()
31242         {
31243             _this.xhrOnLoad(_this.xhr);
31244         }
31245         
31246         this.xhr.onerror = function()
31247         {
31248             _this.xhrOnError(_this.xhr);
31249         }
31250         
31251         var formData = new FormData();
31252
31253         formData.append('returnHTML', 'NO');
31254         
31255         if(crop){
31256             formData.append('crop', crop);
31257         }
31258         
31259         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31260             formData.append(this.paramName, file, file.name);
31261         }
31262         
31263         if(typeof(file.filename) != 'undefined'){
31264             formData.append('filename', file.filename);
31265         }
31266         
31267         if(typeof(file.mimetype) != 'undefined'){
31268             formData.append('mimetype', file.mimetype);
31269         }
31270         
31271         if(this.fireEvent('arrange', this, formData) != false){
31272             this.xhr.send(formData);
31273         };
31274     },
31275     
31276     xhrOnLoad : function(xhr)
31277     {
31278         if(this.loadMask){
31279             this.maskEl.unmask();
31280         }
31281         
31282         if (xhr.readyState !== 4) {
31283             this.fireEvent('exception', this, xhr);
31284             return;
31285         }
31286
31287         var response = Roo.decode(xhr.responseText);
31288         
31289         if(!response.success){
31290             this.fireEvent('exception', this, xhr);
31291             return;
31292         }
31293         
31294         var response = Roo.decode(xhr.responseText);
31295         
31296         this.fireEvent('upload', this, response);
31297         
31298     },
31299     
31300     xhrOnError : function()
31301     {
31302         if(this.loadMask){
31303             this.maskEl.unmask();
31304         }
31305         
31306         Roo.log('xhr on error');
31307         
31308         var response = Roo.decode(xhr.responseText);
31309           
31310         Roo.log(response);
31311         
31312     },
31313     
31314     prepare : function(file)
31315     {   
31316         if(this.loadMask){
31317             this.maskEl.mask(this.loadingText);
31318         }
31319         
31320         this.file = false;
31321         this.exif = {};
31322         
31323         if(typeof(file) === 'string'){
31324             this.loadCanvas(file);
31325             return;
31326         }
31327         
31328         if(!file || !this.urlAPI){
31329             return;
31330         }
31331         
31332         this.file = file;
31333         this.cropType = file.type;
31334         
31335         var _this = this;
31336         
31337         if(this.fireEvent('prepare', this, this.file) != false){
31338             
31339             var reader = new FileReader();
31340             
31341             reader.onload = function (e) {
31342                 if (e.target.error) {
31343                     Roo.log(e.target.error);
31344                     return;
31345                 }
31346                 
31347                 var buffer = e.target.result,
31348                     dataView = new DataView(buffer),
31349                     offset = 2,
31350                     maxOffset = dataView.byteLength - 4,
31351                     markerBytes,
31352                     markerLength;
31353                 
31354                 if (dataView.getUint16(0) === 0xffd8) {
31355                     while (offset < maxOffset) {
31356                         markerBytes = dataView.getUint16(offset);
31357                         
31358                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31359                             markerLength = dataView.getUint16(offset + 2) + 2;
31360                             if (offset + markerLength > dataView.byteLength) {
31361                                 Roo.log('Invalid meta data: Invalid segment size.');
31362                                 break;
31363                             }
31364                             
31365                             if(markerBytes == 0xffe1){
31366                                 _this.parseExifData(
31367                                     dataView,
31368                                     offset,
31369                                     markerLength
31370                                 );
31371                             }
31372                             
31373                             offset += markerLength;
31374                             
31375                             continue;
31376                         }
31377                         
31378                         break;
31379                     }
31380                     
31381                 }
31382                 
31383                 var url = _this.urlAPI.createObjectURL(_this.file);
31384                 
31385                 _this.loadCanvas(url);
31386                 
31387                 return;
31388             }
31389             
31390             reader.readAsArrayBuffer(this.file);
31391             
31392         }
31393         
31394     },
31395     
31396     parseExifData : function(dataView, offset, length)
31397     {
31398         var tiffOffset = offset + 10,
31399             littleEndian,
31400             dirOffset;
31401     
31402         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31403             // No Exif data, might be XMP data instead
31404             return;
31405         }
31406         
31407         // Check for the ASCII code for "Exif" (0x45786966):
31408         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31409             // No Exif data, might be XMP data instead
31410             return;
31411         }
31412         if (tiffOffset + 8 > dataView.byteLength) {
31413             Roo.log('Invalid Exif data: Invalid segment size.');
31414             return;
31415         }
31416         // Check for the two null bytes:
31417         if (dataView.getUint16(offset + 8) !== 0x0000) {
31418             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31419             return;
31420         }
31421         // Check the byte alignment:
31422         switch (dataView.getUint16(tiffOffset)) {
31423         case 0x4949:
31424             littleEndian = true;
31425             break;
31426         case 0x4D4D:
31427             littleEndian = false;
31428             break;
31429         default:
31430             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31431             return;
31432         }
31433         // Check for the TIFF tag marker (0x002A):
31434         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31435             Roo.log('Invalid Exif data: Missing TIFF marker.');
31436             return;
31437         }
31438         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31439         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31440         
31441         this.parseExifTags(
31442             dataView,
31443             tiffOffset,
31444             tiffOffset + dirOffset,
31445             littleEndian
31446         );
31447     },
31448     
31449     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31450     {
31451         var tagsNumber,
31452             dirEndOffset,
31453             i;
31454         if (dirOffset + 6 > dataView.byteLength) {
31455             Roo.log('Invalid Exif data: Invalid directory offset.');
31456             return;
31457         }
31458         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31459         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31460         if (dirEndOffset + 4 > dataView.byteLength) {
31461             Roo.log('Invalid Exif data: Invalid directory size.');
31462             return;
31463         }
31464         for (i = 0; i < tagsNumber; i += 1) {
31465             this.parseExifTag(
31466                 dataView,
31467                 tiffOffset,
31468                 dirOffset + 2 + 12 * i, // tag offset
31469                 littleEndian
31470             );
31471         }
31472         // Return the offset to the next directory:
31473         return dataView.getUint32(dirEndOffset, littleEndian);
31474     },
31475     
31476     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31477     {
31478         var tag = dataView.getUint16(offset, littleEndian);
31479         
31480         this.exif[tag] = this.getExifValue(
31481             dataView,
31482             tiffOffset,
31483             offset,
31484             dataView.getUint16(offset + 2, littleEndian), // tag type
31485             dataView.getUint32(offset + 4, littleEndian), // tag length
31486             littleEndian
31487         );
31488     },
31489     
31490     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31491     {
31492         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31493             tagSize,
31494             dataOffset,
31495             values,
31496             i,
31497             str,
31498             c;
31499     
31500         if (!tagType) {
31501             Roo.log('Invalid Exif data: Invalid tag type.');
31502             return;
31503         }
31504         
31505         tagSize = tagType.size * length;
31506         // Determine if the value is contained in the dataOffset bytes,
31507         // or if the value at the dataOffset is a pointer to the actual data:
31508         dataOffset = tagSize > 4 ?
31509                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31510         if (dataOffset + tagSize > dataView.byteLength) {
31511             Roo.log('Invalid Exif data: Invalid data offset.');
31512             return;
31513         }
31514         if (length === 1) {
31515             return tagType.getValue(dataView, dataOffset, littleEndian);
31516         }
31517         values = [];
31518         for (i = 0; i < length; i += 1) {
31519             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31520         }
31521         
31522         if (tagType.ascii) {
31523             str = '';
31524             // Concatenate the chars:
31525             for (i = 0; i < values.length; i += 1) {
31526                 c = values[i];
31527                 // Ignore the terminating NULL byte(s):
31528                 if (c === '\u0000') {
31529                     break;
31530                 }
31531                 str += c;
31532             }
31533             return str;
31534         }
31535         return values;
31536     }
31537     
31538 });
31539
31540 Roo.apply(Roo.bootstrap.UploadCropbox, {
31541     tags : {
31542         'Orientation': 0x0112
31543     },
31544     
31545     Orientation: {
31546             1: 0, //'top-left',
31547 //            2: 'top-right',
31548             3: 180, //'bottom-right',
31549 //            4: 'bottom-left',
31550 //            5: 'left-top',
31551             6: 90, //'right-top',
31552 //            7: 'right-bottom',
31553             8: 270 //'left-bottom'
31554     },
31555     
31556     exifTagTypes : {
31557         // byte, 8-bit unsigned int:
31558         1: {
31559             getValue: function (dataView, dataOffset) {
31560                 return dataView.getUint8(dataOffset);
31561             },
31562             size: 1
31563         },
31564         // ascii, 8-bit byte:
31565         2: {
31566             getValue: function (dataView, dataOffset) {
31567                 return String.fromCharCode(dataView.getUint8(dataOffset));
31568             },
31569             size: 1,
31570             ascii: true
31571         },
31572         // short, 16 bit int:
31573         3: {
31574             getValue: function (dataView, dataOffset, littleEndian) {
31575                 return dataView.getUint16(dataOffset, littleEndian);
31576             },
31577             size: 2
31578         },
31579         // long, 32 bit int:
31580         4: {
31581             getValue: function (dataView, dataOffset, littleEndian) {
31582                 return dataView.getUint32(dataOffset, littleEndian);
31583             },
31584             size: 4
31585         },
31586         // rational = two long values, first is numerator, second is denominator:
31587         5: {
31588             getValue: function (dataView, dataOffset, littleEndian) {
31589                 return dataView.getUint32(dataOffset, littleEndian) /
31590                     dataView.getUint32(dataOffset + 4, littleEndian);
31591             },
31592             size: 8
31593         },
31594         // slong, 32 bit signed int:
31595         9: {
31596             getValue: function (dataView, dataOffset, littleEndian) {
31597                 return dataView.getInt32(dataOffset, littleEndian);
31598             },
31599             size: 4
31600         },
31601         // srational, two slongs, first is numerator, second is denominator:
31602         10: {
31603             getValue: function (dataView, dataOffset, littleEndian) {
31604                 return dataView.getInt32(dataOffset, littleEndian) /
31605                     dataView.getInt32(dataOffset + 4, littleEndian);
31606             },
31607             size: 8
31608         }
31609     },
31610     
31611     footer : {
31612         STANDARD : [
31613             {
31614                 tag : 'div',
31615                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31616                 action : 'rotate-left',
31617                 cn : [
31618                     {
31619                         tag : 'button',
31620                         cls : 'btn btn-default',
31621                         html : '<i class="fa fa-undo"></i>'
31622                     }
31623                 ]
31624             },
31625             {
31626                 tag : 'div',
31627                 cls : 'btn-group roo-upload-cropbox-picture',
31628                 action : 'picture',
31629                 cn : [
31630                     {
31631                         tag : 'button',
31632                         cls : 'btn btn-default',
31633                         html : '<i class="fa fa-picture-o"></i>'
31634                     }
31635                 ]
31636             },
31637             {
31638                 tag : 'div',
31639                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31640                 action : 'rotate-right',
31641                 cn : [
31642                     {
31643                         tag : 'button',
31644                         cls : 'btn btn-default',
31645                         html : '<i class="fa fa-repeat"></i>'
31646                     }
31647                 ]
31648             }
31649         ],
31650         DOCUMENT : [
31651             {
31652                 tag : 'div',
31653                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31654                 action : 'rotate-left',
31655                 cn : [
31656                     {
31657                         tag : 'button',
31658                         cls : 'btn btn-default',
31659                         html : '<i class="fa fa-undo"></i>'
31660                     }
31661                 ]
31662             },
31663             {
31664                 tag : 'div',
31665                 cls : 'btn-group roo-upload-cropbox-download',
31666                 action : 'download',
31667                 cn : [
31668                     {
31669                         tag : 'button',
31670                         cls : 'btn btn-default',
31671                         html : '<i class="fa fa-download"></i>'
31672                     }
31673                 ]
31674             },
31675             {
31676                 tag : 'div',
31677                 cls : 'btn-group roo-upload-cropbox-crop',
31678                 action : 'crop',
31679                 cn : [
31680                     {
31681                         tag : 'button',
31682                         cls : 'btn btn-default',
31683                         html : '<i class="fa fa-crop"></i>'
31684                     }
31685                 ]
31686             },
31687             {
31688                 tag : 'div',
31689                 cls : 'btn-group roo-upload-cropbox-trash',
31690                 action : 'trash',
31691                 cn : [
31692                     {
31693                         tag : 'button',
31694                         cls : 'btn btn-default',
31695                         html : '<i class="fa fa-trash"></i>'
31696                     }
31697                 ]
31698             },
31699             {
31700                 tag : 'div',
31701                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31702                 action : 'rotate-right',
31703                 cn : [
31704                     {
31705                         tag : 'button',
31706                         cls : 'btn btn-default',
31707                         html : '<i class="fa fa-repeat"></i>'
31708                     }
31709                 ]
31710             }
31711         ],
31712         ROTATOR : [
31713             {
31714                 tag : 'div',
31715                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31716                 action : 'rotate-left',
31717                 cn : [
31718                     {
31719                         tag : 'button',
31720                         cls : 'btn btn-default',
31721                         html : '<i class="fa fa-undo"></i>'
31722                     }
31723                 ]
31724             },
31725             {
31726                 tag : 'div',
31727                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31728                 action : 'rotate-right',
31729                 cn : [
31730                     {
31731                         tag : 'button',
31732                         cls : 'btn btn-default',
31733                         html : '<i class="fa fa-repeat"></i>'
31734                     }
31735                 ]
31736             }
31737         ]
31738     }
31739 });
31740
31741 /*
31742 * Licence: LGPL
31743 */
31744
31745 /**
31746  * @class Roo.bootstrap.DocumentManager
31747  * @extends Roo.bootstrap.Component
31748  * Bootstrap DocumentManager class
31749  * @cfg {String} paramName default 'imageUpload'
31750  * @cfg {String} toolTipName default 'filename'
31751  * @cfg {String} method default POST
31752  * @cfg {String} url action url
31753  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31754  * @cfg {Boolean} multiple multiple upload default true
31755  * @cfg {Number} thumbSize default 300
31756  * @cfg {String} fieldLabel
31757  * @cfg {Number} labelWidth default 4
31758  * @cfg {String} labelAlign (left|top) default left
31759  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31760 * @cfg {Number} labellg set the width of label (1-12)
31761  * @cfg {Number} labelmd set the width of label (1-12)
31762  * @cfg {Number} labelsm set the width of label (1-12)
31763  * @cfg {Number} labelxs set the width of label (1-12)
31764  * 
31765  * @constructor
31766  * Create a new DocumentManager
31767  * @param {Object} config The config object
31768  */
31769
31770 Roo.bootstrap.DocumentManager = function(config){
31771     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31772     
31773     this.files = [];
31774     this.delegates = [];
31775     
31776     this.addEvents({
31777         /**
31778          * @event initial
31779          * Fire when initial the DocumentManager
31780          * @param {Roo.bootstrap.DocumentManager} this
31781          */
31782         "initial" : true,
31783         /**
31784          * @event inspect
31785          * inspect selected file
31786          * @param {Roo.bootstrap.DocumentManager} this
31787          * @param {File} file
31788          */
31789         "inspect" : true,
31790         /**
31791          * @event exception
31792          * Fire when xhr load exception
31793          * @param {Roo.bootstrap.DocumentManager} this
31794          * @param {XMLHttpRequest} xhr
31795          */
31796         "exception" : true,
31797         /**
31798          * @event afterupload
31799          * Fire when xhr load exception
31800          * @param {Roo.bootstrap.DocumentManager} this
31801          * @param {XMLHttpRequest} xhr
31802          */
31803         "afterupload" : true,
31804         /**
31805          * @event prepare
31806          * prepare the form data
31807          * @param {Roo.bootstrap.DocumentManager} this
31808          * @param {Object} formData
31809          */
31810         "prepare" : true,
31811         /**
31812          * @event remove
31813          * Fire when remove the file
31814          * @param {Roo.bootstrap.DocumentManager} this
31815          * @param {Object} file
31816          */
31817         "remove" : true,
31818         /**
31819          * @event refresh
31820          * Fire after refresh the file
31821          * @param {Roo.bootstrap.DocumentManager} this
31822          */
31823         "refresh" : true,
31824         /**
31825          * @event click
31826          * Fire after click the image
31827          * @param {Roo.bootstrap.DocumentManager} this
31828          * @param {Object} file
31829          */
31830         "click" : true,
31831         /**
31832          * @event edit
31833          * Fire when upload a image and editable set to true
31834          * @param {Roo.bootstrap.DocumentManager} this
31835          * @param {Object} file
31836          */
31837         "edit" : true,
31838         /**
31839          * @event beforeselectfile
31840          * Fire before select file
31841          * @param {Roo.bootstrap.DocumentManager} this
31842          */
31843         "beforeselectfile" : true,
31844         /**
31845          * @event process
31846          * Fire before process file
31847          * @param {Roo.bootstrap.DocumentManager} this
31848          * @param {Object} file
31849          */
31850         "process" : true,
31851         /**
31852          * @event previewrendered
31853          * Fire when preview rendered
31854          * @param {Roo.bootstrap.DocumentManager} this
31855          * @param {Object} file
31856          */
31857         "previewrendered" : true,
31858         /**
31859          */
31860         "previewResize" : true
31861         
31862     });
31863 };
31864
31865 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31866     
31867     boxes : 0,
31868     inputName : '',
31869     thumbSize : 300,
31870     multiple : true,
31871     files : false,
31872     method : 'POST',
31873     url : '',
31874     paramName : 'imageUpload',
31875     toolTipName : 'filename',
31876     fieldLabel : '',
31877     labelWidth : 4,
31878     labelAlign : 'left',
31879     editable : true,
31880     delegates : false,
31881     xhr : false, 
31882     
31883     labellg : 0,
31884     labelmd : 0,
31885     labelsm : 0,
31886     labelxs : 0,
31887     
31888     getAutoCreate : function()
31889     {   
31890         var managerWidget = {
31891             tag : 'div',
31892             cls : 'roo-document-manager',
31893             cn : [
31894                 {
31895                     tag : 'input',
31896                     cls : 'roo-document-manager-selector',
31897                     type : 'file'
31898                 },
31899                 {
31900                     tag : 'div',
31901                     cls : 'roo-document-manager-uploader',
31902                     cn : [
31903                         {
31904                             tag : 'div',
31905                             cls : 'roo-document-manager-upload-btn',
31906                             html : '<i class="fa fa-plus"></i>'
31907                         }
31908                     ]
31909                     
31910                 }
31911             ]
31912         };
31913         
31914         var content = [
31915             {
31916                 tag : 'div',
31917                 cls : 'column col-md-12',
31918                 cn : managerWidget
31919             }
31920         ];
31921         
31922         if(this.fieldLabel.length){
31923             
31924             content = [
31925                 {
31926                     tag : 'div',
31927                     cls : 'column col-md-12',
31928                     html : this.fieldLabel
31929                 },
31930                 {
31931                     tag : 'div',
31932                     cls : 'column col-md-12',
31933                     cn : managerWidget
31934                 }
31935             ];
31936
31937             if(this.labelAlign == 'left'){
31938                 content = [
31939                     {
31940                         tag : 'div',
31941                         cls : 'column',
31942                         html : this.fieldLabel
31943                     },
31944                     {
31945                         tag : 'div',
31946                         cls : 'column',
31947                         cn : managerWidget
31948                     }
31949                 ];
31950                 
31951                 if(this.labelWidth > 12){
31952                     content[0].style = "width: " + this.labelWidth + 'px';
31953                 }
31954
31955                 if(this.labelWidth < 13 && this.labelmd == 0){
31956                     this.labelmd = this.labelWidth;
31957                 }
31958
31959                 if(this.labellg > 0){
31960                     content[0].cls += ' col-lg-' + this.labellg;
31961                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31962                 }
31963
31964                 if(this.labelmd > 0){
31965                     content[0].cls += ' col-md-' + this.labelmd;
31966                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31967                 }
31968
31969                 if(this.labelsm > 0){
31970                     content[0].cls += ' col-sm-' + this.labelsm;
31971                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31972                 }
31973
31974                 if(this.labelxs > 0){
31975                     content[0].cls += ' col-xs-' + this.labelxs;
31976                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31977                 }
31978                 
31979             }
31980         }
31981         
31982         var cfg = {
31983             tag : 'div',
31984             cls : 'row clearfix',
31985             cn : content
31986         };
31987         
31988         return cfg;
31989         
31990     },
31991     
31992     initEvents : function()
31993     {
31994         this.managerEl = this.el.select('.roo-document-manager', true).first();
31995         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31996         
31997         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31998         this.selectorEl.hide();
31999         
32000         if(this.multiple){
32001             this.selectorEl.attr('multiple', 'multiple');
32002         }
32003         
32004         this.selectorEl.on('change', this.onFileSelected, this);
32005         
32006         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32007         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32008         
32009         this.uploader.on('click', this.onUploaderClick, this);
32010         
32011         this.renderProgressDialog();
32012         
32013         var _this = this;
32014         
32015         window.addEventListener("resize", function() { _this.refresh(); } );
32016         
32017         this.fireEvent('initial', this);
32018     },
32019     
32020     renderProgressDialog : function()
32021     {
32022         var _this = this;
32023         
32024         this.progressDialog = new Roo.bootstrap.Modal({
32025             cls : 'roo-document-manager-progress-dialog',
32026             allow_close : false,
32027             animate : false,
32028             title : '',
32029             buttons : [
32030                 {
32031                     name  :'cancel',
32032                     weight : 'danger',
32033                     html : 'Cancel'
32034                 }
32035             ], 
32036             listeners : { 
32037                 btnclick : function() {
32038                     _this.uploadCancel();
32039                     this.hide();
32040                 }
32041             }
32042         });
32043          
32044         this.progressDialog.render(Roo.get(document.body));
32045          
32046         this.progress = new Roo.bootstrap.Progress({
32047             cls : 'roo-document-manager-progress',
32048             active : true,
32049             striped : true
32050         });
32051         
32052         this.progress.render(this.progressDialog.getChildContainer());
32053         
32054         this.progressBar = new Roo.bootstrap.ProgressBar({
32055             cls : 'roo-document-manager-progress-bar',
32056             aria_valuenow : 0,
32057             aria_valuemin : 0,
32058             aria_valuemax : 12,
32059             panel : 'success'
32060         });
32061         
32062         this.progressBar.render(this.progress.getChildContainer());
32063     },
32064     
32065     onUploaderClick : function(e)
32066     {
32067         e.preventDefault();
32068      
32069         if(this.fireEvent('beforeselectfile', this) != false){
32070             this.selectorEl.dom.click();
32071         }
32072         
32073     },
32074     
32075     onFileSelected : function(e)
32076     {
32077         e.preventDefault();
32078         
32079         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32080             return;
32081         }
32082         
32083         Roo.each(this.selectorEl.dom.files, function(file){
32084             if(this.fireEvent('inspect', this, file) != false){
32085                 this.files.push(file);
32086             }
32087         }, this);
32088         
32089         this.queue();
32090         
32091     },
32092     
32093     queue : function()
32094     {
32095         this.selectorEl.dom.value = '';
32096         
32097         if(!this.files || !this.files.length){
32098             return;
32099         }
32100         
32101         if(this.boxes > 0 && this.files.length > this.boxes){
32102             this.files = this.files.slice(0, this.boxes);
32103         }
32104         
32105         this.uploader.show();
32106         
32107         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32108             this.uploader.hide();
32109         }
32110         
32111         var _this = this;
32112         
32113         var files = [];
32114         
32115         var docs = [];
32116         
32117         Roo.each(this.files, function(file){
32118             
32119             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32120                 var f = this.renderPreview(file);
32121                 files.push(f);
32122                 return;
32123             }
32124             
32125             if(file.type.indexOf('image') != -1){
32126                 this.delegates.push(
32127                     (function(){
32128                         _this.process(file);
32129                     }).createDelegate(this)
32130                 );
32131         
32132                 return;
32133             }
32134             
32135             docs.push(
32136                 (function(){
32137                     _this.process(file);
32138                 }).createDelegate(this)
32139             );
32140             
32141         }, this);
32142         
32143         this.files = files;
32144         
32145         this.delegates = this.delegates.concat(docs);
32146         
32147         if(!this.delegates.length){
32148             this.refresh();
32149             return;
32150         }
32151         
32152         this.progressBar.aria_valuemax = this.delegates.length;
32153         
32154         this.arrange();
32155         
32156         return;
32157     },
32158     
32159     arrange : function()
32160     {
32161         if(!this.delegates.length){
32162             this.progressDialog.hide();
32163             this.refresh();
32164             return;
32165         }
32166         
32167         var delegate = this.delegates.shift();
32168         
32169         this.progressDialog.show();
32170         
32171         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32172         
32173         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32174         
32175         delegate();
32176     },
32177     
32178     refresh : function()
32179     {
32180         this.uploader.show();
32181         
32182         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32183             this.uploader.hide();
32184         }
32185         
32186         Roo.isTouch ? this.closable(false) : this.closable(true);
32187         
32188         this.fireEvent('refresh', this);
32189     },
32190     
32191     onRemove : function(e, el, o)
32192     {
32193         e.preventDefault();
32194         
32195         this.fireEvent('remove', this, o);
32196         
32197     },
32198     
32199     remove : function(o)
32200     {
32201         var files = [];
32202         
32203         Roo.each(this.files, function(file){
32204             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32205                 files.push(file);
32206                 return;
32207             }
32208
32209             o.target.remove();
32210
32211         }, this);
32212         
32213         this.files = files;
32214         
32215         this.refresh();
32216     },
32217     
32218     clear : function()
32219     {
32220         Roo.each(this.files, function(file){
32221             if(!file.target){
32222                 return;
32223             }
32224             
32225             file.target.remove();
32226
32227         }, this);
32228         
32229         this.files = [];
32230         
32231         this.refresh();
32232     },
32233     
32234     onClick : function(e, el, o)
32235     {
32236         e.preventDefault();
32237         
32238         this.fireEvent('click', this, o);
32239         
32240     },
32241     
32242     closable : function(closable)
32243     {
32244         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32245             
32246             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32247             
32248             if(closable){
32249                 el.show();
32250                 return;
32251             }
32252             
32253             el.hide();
32254             
32255         }, this);
32256     },
32257     
32258     xhrOnLoad : function(xhr)
32259     {
32260         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32261             el.remove();
32262         }, this);
32263         
32264         if (xhr.readyState !== 4) {
32265             this.arrange();
32266             this.fireEvent('exception', this, xhr);
32267             return;
32268         }
32269
32270         var response = Roo.decode(xhr.responseText);
32271         
32272         if(!response.success){
32273             this.arrange();
32274             this.fireEvent('exception', this, xhr);
32275             return;
32276         }
32277         
32278         var file = this.renderPreview(response.data);
32279         
32280         this.files.push(file);
32281         
32282         this.arrange();
32283         
32284         this.fireEvent('afterupload', this, xhr);
32285         
32286     },
32287     
32288     xhrOnError : function(xhr)
32289     {
32290         Roo.log('xhr on error');
32291         
32292         var response = Roo.decode(xhr.responseText);
32293           
32294         Roo.log(response);
32295         
32296         this.arrange();
32297     },
32298     
32299     process : function(file)
32300     {
32301         if(this.fireEvent('process', this, file) !== false){
32302             if(this.editable && file.type.indexOf('image') != -1){
32303                 this.fireEvent('edit', this, file);
32304                 return;
32305             }
32306
32307             this.uploadStart(file, false);
32308
32309             return;
32310         }
32311         
32312     },
32313     
32314     uploadStart : function(file, crop)
32315     {
32316         this.xhr = new XMLHttpRequest();
32317         
32318         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32319             this.arrange();
32320             return;
32321         }
32322         
32323         file.xhr = this.xhr;
32324             
32325         this.managerEl.createChild({
32326             tag : 'div',
32327             cls : 'roo-document-manager-loading',
32328             cn : [
32329                 {
32330                     tag : 'div',
32331                     tooltip : file.name,
32332                     cls : 'roo-document-manager-thumb',
32333                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32334                 }
32335             ]
32336
32337         });
32338
32339         this.xhr.open(this.method, this.url, true);
32340         
32341         var headers = {
32342             "Accept": "application/json",
32343             "Cache-Control": "no-cache",
32344             "X-Requested-With": "XMLHttpRequest"
32345         };
32346         
32347         for (var headerName in headers) {
32348             var headerValue = headers[headerName];
32349             if (headerValue) {
32350                 this.xhr.setRequestHeader(headerName, headerValue);
32351             }
32352         }
32353         
32354         var _this = this;
32355         
32356         this.xhr.onload = function()
32357         {
32358             _this.xhrOnLoad(_this.xhr);
32359         }
32360         
32361         this.xhr.onerror = function()
32362         {
32363             _this.xhrOnError(_this.xhr);
32364         }
32365         
32366         var formData = new FormData();
32367
32368         formData.append('returnHTML', 'NO');
32369         
32370         if(crop){
32371             formData.append('crop', crop);
32372         }
32373         
32374         formData.append(this.paramName, file, file.name);
32375         
32376         var options = {
32377             file : file, 
32378             manually : false
32379         };
32380         
32381         if(this.fireEvent('prepare', this, formData, options) != false){
32382             
32383             if(options.manually){
32384                 return;
32385             }
32386             
32387             this.xhr.send(formData);
32388             return;
32389         };
32390         
32391         this.uploadCancel();
32392     },
32393     
32394     uploadCancel : function()
32395     {
32396         if (this.xhr) {
32397             this.xhr.abort();
32398         }
32399         
32400         this.delegates = [];
32401         
32402         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32403             el.remove();
32404         }, this);
32405         
32406         this.arrange();
32407     },
32408     
32409     renderPreview : function(file)
32410     {
32411         if(typeof(file.target) != 'undefined' && file.target){
32412             return file;
32413         }
32414         
32415         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32416         
32417         var previewEl = this.managerEl.createChild({
32418             tag : 'div',
32419             cls : 'roo-document-manager-preview',
32420             cn : [
32421                 {
32422                     tag : 'div',
32423                     tooltip : file[this.toolTipName],
32424                     cls : 'roo-document-manager-thumb',
32425                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32426                 },
32427                 {
32428                     tag : 'button',
32429                     cls : 'close',
32430                     html : '<i class="fa fa-times-circle"></i>'
32431                 }
32432             ]
32433         });
32434
32435         var close = previewEl.select('button.close', true).first();
32436
32437         close.on('click', this.onRemove, this, file);
32438
32439         file.target = previewEl;
32440
32441         var image = previewEl.select('img', true).first();
32442         
32443         var _this = this;
32444         
32445         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32446         
32447         image.on('click', this.onClick, this, file);
32448         
32449         this.fireEvent('previewrendered', this, file);
32450         
32451         return file;
32452         
32453     },
32454     
32455     onPreviewLoad : function(file, image)
32456     {
32457         if(typeof(file.target) == 'undefined' || !file.target){
32458             return;
32459         }
32460         
32461         var width = image.dom.naturalWidth || image.dom.width;
32462         var height = image.dom.naturalHeight || image.dom.height;
32463         
32464         if(!this.previewResize) {
32465             return;
32466         }
32467         
32468         if(width > height){
32469             file.target.addClass('wide');
32470             return;
32471         }
32472         
32473         file.target.addClass('tall');
32474         return;
32475         
32476     },
32477     
32478     uploadFromSource : function(file, crop)
32479     {
32480         this.xhr = new XMLHttpRequest();
32481         
32482         this.managerEl.createChild({
32483             tag : 'div',
32484             cls : 'roo-document-manager-loading',
32485             cn : [
32486                 {
32487                     tag : 'div',
32488                     tooltip : file.name,
32489                     cls : 'roo-document-manager-thumb',
32490                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32491                 }
32492             ]
32493
32494         });
32495
32496         this.xhr.open(this.method, this.url, true);
32497         
32498         var headers = {
32499             "Accept": "application/json",
32500             "Cache-Control": "no-cache",
32501             "X-Requested-With": "XMLHttpRequest"
32502         };
32503         
32504         for (var headerName in headers) {
32505             var headerValue = headers[headerName];
32506             if (headerValue) {
32507                 this.xhr.setRequestHeader(headerName, headerValue);
32508             }
32509         }
32510         
32511         var _this = this;
32512         
32513         this.xhr.onload = function()
32514         {
32515             _this.xhrOnLoad(_this.xhr);
32516         }
32517         
32518         this.xhr.onerror = function()
32519         {
32520             _this.xhrOnError(_this.xhr);
32521         }
32522         
32523         var formData = new FormData();
32524
32525         formData.append('returnHTML', 'NO');
32526         
32527         formData.append('crop', crop);
32528         
32529         if(typeof(file.filename) != 'undefined'){
32530             formData.append('filename', file.filename);
32531         }
32532         
32533         if(typeof(file.mimetype) != 'undefined'){
32534             formData.append('mimetype', file.mimetype);
32535         }
32536         
32537         Roo.log(formData);
32538         
32539         if(this.fireEvent('prepare', this, formData) != false){
32540             this.xhr.send(formData);
32541         };
32542     }
32543 });
32544
32545 /*
32546 * Licence: LGPL
32547 */
32548
32549 /**
32550  * @class Roo.bootstrap.DocumentViewer
32551  * @extends Roo.bootstrap.Component
32552  * Bootstrap DocumentViewer class
32553  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32554  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32555  * 
32556  * @constructor
32557  * Create a new DocumentViewer
32558  * @param {Object} config The config object
32559  */
32560
32561 Roo.bootstrap.DocumentViewer = function(config){
32562     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32563     
32564     this.addEvents({
32565         /**
32566          * @event initial
32567          * Fire after initEvent
32568          * @param {Roo.bootstrap.DocumentViewer} this
32569          */
32570         "initial" : true,
32571         /**
32572          * @event click
32573          * Fire after click
32574          * @param {Roo.bootstrap.DocumentViewer} this
32575          */
32576         "click" : true,
32577         /**
32578          * @event download
32579          * Fire after download button
32580          * @param {Roo.bootstrap.DocumentViewer} this
32581          */
32582         "download" : true,
32583         /**
32584          * @event trash
32585          * Fire after trash button
32586          * @param {Roo.bootstrap.DocumentViewer} this
32587          */
32588         "trash" : true
32589         
32590     });
32591 };
32592
32593 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32594     
32595     showDownload : true,
32596     
32597     showTrash : true,
32598     
32599     getAutoCreate : function()
32600     {
32601         var cfg = {
32602             tag : 'div',
32603             cls : 'roo-document-viewer',
32604             cn : [
32605                 {
32606                     tag : 'div',
32607                     cls : 'roo-document-viewer-body',
32608                     cn : [
32609                         {
32610                             tag : 'div',
32611                             cls : 'roo-document-viewer-thumb',
32612                             cn : [
32613                                 {
32614                                     tag : 'img',
32615                                     cls : 'roo-document-viewer-image'
32616                                 }
32617                             ]
32618                         }
32619                     ]
32620                 },
32621                 {
32622                     tag : 'div',
32623                     cls : 'roo-document-viewer-footer',
32624                     cn : {
32625                         tag : 'div',
32626                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32627                         cn : [
32628                             {
32629                                 tag : 'div',
32630                                 cls : 'btn-group roo-document-viewer-download',
32631                                 cn : [
32632                                     {
32633                                         tag : 'button',
32634                                         cls : 'btn btn-default',
32635                                         html : '<i class="fa fa-download"></i>'
32636                                     }
32637                                 ]
32638                             },
32639                             {
32640                                 tag : 'div',
32641                                 cls : 'btn-group roo-document-viewer-trash',
32642                                 cn : [
32643                                     {
32644                                         tag : 'button',
32645                                         cls : 'btn btn-default',
32646                                         html : '<i class="fa fa-trash"></i>'
32647                                     }
32648                                 ]
32649                             }
32650                         ]
32651                     }
32652                 }
32653             ]
32654         };
32655         
32656         return cfg;
32657     },
32658     
32659     initEvents : function()
32660     {
32661         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32662         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32663         
32664         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32665         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32666         
32667         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32668         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32669         
32670         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32671         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32672         
32673         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32674         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32675         
32676         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32677         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32678         
32679         this.bodyEl.on('click', this.onClick, this);
32680         this.downloadBtn.on('click', this.onDownload, this);
32681         this.trashBtn.on('click', this.onTrash, this);
32682         
32683         this.downloadBtn.hide();
32684         this.trashBtn.hide();
32685         
32686         if(this.showDownload){
32687             this.downloadBtn.show();
32688         }
32689         
32690         if(this.showTrash){
32691             this.trashBtn.show();
32692         }
32693         
32694         if(!this.showDownload && !this.showTrash) {
32695             this.footerEl.hide();
32696         }
32697         
32698     },
32699     
32700     initial : function()
32701     {
32702         this.fireEvent('initial', this);
32703         
32704     },
32705     
32706     onClick : function(e)
32707     {
32708         e.preventDefault();
32709         
32710         this.fireEvent('click', this);
32711     },
32712     
32713     onDownload : function(e)
32714     {
32715         e.preventDefault();
32716         
32717         this.fireEvent('download', this);
32718     },
32719     
32720     onTrash : function(e)
32721     {
32722         e.preventDefault();
32723         
32724         this.fireEvent('trash', this);
32725     }
32726     
32727 });
32728 /*
32729  * - LGPL
32730  *
32731  * nav progress bar
32732  * 
32733  */
32734
32735 /**
32736  * @class Roo.bootstrap.NavProgressBar
32737  * @extends Roo.bootstrap.Component
32738  * Bootstrap NavProgressBar class
32739  * 
32740  * @constructor
32741  * Create a new nav progress bar
32742  * @param {Object} config The config object
32743  */
32744
32745 Roo.bootstrap.NavProgressBar = function(config){
32746     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32747
32748     this.bullets = this.bullets || [];
32749    
32750 //    Roo.bootstrap.NavProgressBar.register(this);
32751      this.addEvents({
32752         /**
32753              * @event changed
32754              * Fires when the active item changes
32755              * @param {Roo.bootstrap.NavProgressBar} this
32756              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32757              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32758          */
32759         'changed': true
32760      });
32761     
32762 };
32763
32764 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32765     
32766     bullets : [],
32767     barItems : [],
32768     
32769     getAutoCreate : function()
32770     {
32771         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32772         
32773         cfg = {
32774             tag : 'div',
32775             cls : 'roo-navigation-bar-group',
32776             cn : [
32777                 {
32778                     tag : 'div',
32779                     cls : 'roo-navigation-top-bar'
32780                 },
32781                 {
32782                     tag : 'div',
32783                     cls : 'roo-navigation-bullets-bar',
32784                     cn : [
32785                         {
32786                             tag : 'ul',
32787                             cls : 'roo-navigation-bar'
32788                         }
32789                     ]
32790                 },
32791                 
32792                 {
32793                     tag : 'div',
32794                     cls : 'roo-navigation-bottom-bar'
32795                 }
32796             ]
32797             
32798         };
32799         
32800         return cfg;
32801         
32802     },
32803     
32804     initEvents: function() 
32805     {
32806         
32807     },
32808     
32809     onRender : function(ct, position) 
32810     {
32811         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32812         
32813         if(this.bullets.length){
32814             Roo.each(this.bullets, function(b){
32815                this.addItem(b);
32816             }, this);
32817         }
32818         
32819         this.format();
32820         
32821     },
32822     
32823     addItem : function(cfg)
32824     {
32825         var item = new Roo.bootstrap.NavProgressItem(cfg);
32826         
32827         item.parentId = this.id;
32828         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32829         
32830         if(cfg.html){
32831             var top = new Roo.bootstrap.Element({
32832                 tag : 'div',
32833                 cls : 'roo-navigation-bar-text'
32834             });
32835             
32836             var bottom = new Roo.bootstrap.Element({
32837                 tag : 'div',
32838                 cls : 'roo-navigation-bar-text'
32839             });
32840             
32841             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32842             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32843             
32844             var topText = new Roo.bootstrap.Element({
32845                 tag : 'span',
32846                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32847             });
32848             
32849             var bottomText = new Roo.bootstrap.Element({
32850                 tag : 'span',
32851                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32852             });
32853             
32854             topText.onRender(top.el, null);
32855             bottomText.onRender(bottom.el, null);
32856             
32857             item.topEl = top;
32858             item.bottomEl = bottom;
32859         }
32860         
32861         this.barItems.push(item);
32862         
32863         return item;
32864     },
32865     
32866     getActive : function()
32867     {
32868         var active = false;
32869         
32870         Roo.each(this.barItems, function(v){
32871             
32872             if (!v.isActive()) {
32873                 return;
32874             }
32875             
32876             active = v;
32877             return false;
32878             
32879         });
32880         
32881         return active;
32882     },
32883     
32884     setActiveItem : function(item)
32885     {
32886         var prev = false;
32887         
32888         Roo.each(this.barItems, function(v){
32889             if (v.rid == item.rid) {
32890                 return ;
32891             }
32892             
32893             if (v.isActive()) {
32894                 v.setActive(false);
32895                 prev = v;
32896             }
32897         });
32898
32899         item.setActive(true);
32900         
32901         this.fireEvent('changed', this, item, prev);
32902     },
32903     
32904     getBarItem: function(rid)
32905     {
32906         var ret = false;
32907         
32908         Roo.each(this.barItems, function(e) {
32909             if (e.rid != rid) {
32910                 return;
32911             }
32912             
32913             ret =  e;
32914             return false;
32915         });
32916         
32917         return ret;
32918     },
32919     
32920     indexOfItem : function(item)
32921     {
32922         var index = false;
32923         
32924         Roo.each(this.barItems, function(v, i){
32925             
32926             if (v.rid != item.rid) {
32927                 return;
32928             }
32929             
32930             index = i;
32931             return false
32932         });
32933         
32934         return index;
32935     },
32936     
32937     setActiveNext : function()
32938     {
32939         var i = this.indexOfItem(this.getActive());
32940         
32941         if (i > this.barItems.length) {
32942             return;
32943         }
32944         
32945         this.setActiveItem(this.barItems[i+1]);
32946     },
32947     
32948     setActivePrev : function()
32949     {
32950         var i = this.indexOfItem(this.getActive());
32951         
32952         if (i  < 1) {
32953             return;
32954         }
32955         
32956         this.setActiveItem(this.barItems[i-1]);
32957     },
32958     
32959     format : function()
32960     {
32961         if(!this.barItems.length){
32962             return;
32963         }
32964      
32965         var width = 100 / this.barItems.length;
32966         
32967         Roo.each(this.barItems, function(i){
32968             i.el.setStyle('width', width + '%');
32969             i.topEl.el.setStyle('width', width + '%');
32970             i.bottomEl.el.setStyle('width', width + '%');
32971         }, this);
32972         
32973     }
32974     
32975 });
32976 /*
32977  * - LGPL
32978  *
32979  * Nav Progress Item
32980  * 
32981  */
32982
32983 /**
32984  * @class Roo.bootstrap.NavProgressItem
32985  * @extends Roo.bootstrap.Component
32986  * Bootstrap NavProgressItem class
32987  * @cfg {String} rid the reference id
32988  * @cfg {Boolean} active (true|false) Is item active default false
32989  * @cfg {Boolean} disabled (true|false) Is item active default false
32990  * @cfg {String} html
32991  * @cfg {String} position (top|bottom) text position default bottom
32992  * @cfg {String} icon show icon instead of number
32993  * 
32994  * @constructor
32995  * Create a new NavProgressItem
32996  * @param {Object} config The config object
32997  */
32998 Roo.bootstrap.NavProgressItem = function(config){
32999     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33000     this.addEvents({
33001         // raw events
33002         /**
33003          * @event click
33004          * The raw click event for the entire grid.
33005          * @param {Roo.bootstrap.NavProgressItem} this
33006          * @param {Roo.EventObject} e
33007          */
33008         "click" : true
33009     });
33010    
33011 };
33012
33013 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33014     
33015     rid : '',
33016     active : false,
33017     disabled : false,
33018     html : '',
33019     position : 'bottom',
33020     icon : false,
33021     
33022     getAutoCreate : function()
33023     {
33024         var iconCls = 'roo-navigation-bar-item-icon';
33025         
33026         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33027         
33028         var cfg = {
33029             tag: 'li',
33030             cls: 'roo-navigation-bar-item',
33031             cn : [
33032                 {
33033                     tag : 'i',
33034                     cls : iconCls
33035                 }
33036             ]
33037         };
33038         
33039         if(this.active){
33040             cfg.cls += ' active';
33041         }
33042         if(this.disabled){
33043             cfg.cls += ' disabled';
33044         }
33045         
33046         return cfg;
33047     },
33048     
33049     disable : function()
33050     {
33051         this.setDisabled(true);
33052     },
33053     
33054     enable : function()
33055     {
33056         this.setDisabled(false);
33057     },
33058     
33059     initEvents: function() 
33060     {
33061         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33062         
33063         this.iconEl.on('click', this.onClick, this);
33064     },
33065     
33066     onClick : function(e)
33067     {
33068         e.preventDefault();
33069         
33070         if(this.disabled){
33071             return;
33072         }
33073         
33074         if(this.fireEvent('click', this, e) === false){
33075             return;
33076         };
33077         
33078         this.parent().setActiveItem(this);
33079     },
33080     
33081     isActive: function () 
33082     {
33083         return this.active;
33084     },
33085     
33086     setActive : function(state)
33087     {
33088         if(this.active == state){
33089             return;
33090         }
33091         
33092         this.active = state;
33093         
33094         if (state) {
33095             this.el.addClass('active');
33096             return;
33097         }
33098         
33099         this.el.removeClass('active');
33100         
33101         return;
33102     },
33103     
33104     setDisabled : function(state)
33105     {
33106         if(this.disabled == state){
33107             return;
33108         }
33109         
33110         this.disabled = state;
33111         
33112         if (state) {
33113             this.el.addClass('disabled');
33114             return;
33115         }
33116         
33117         this.el.removeClass('disabled');
33118     },
33119     
33120     tooltipEl : function()
33121     {
33122         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33123     }
33124 });
33125  
33126
33127  /*
33128  * - LGPL
33129  *
33130  * FieldLabel
33131  * 
33132  */
33133
33134 /**
33135  * @class Roo.bootstrap.FieldLabel
33136  * @extends Roo.bootstrap.Component
33137  * Bootstrap FieldLabel class
33138  * @cfg {String} html contents of the element
33139  * @cfg {String} tag tag of the element default label
33140  * @cfg {String} cls class of the element
33141  * @cfg {String} target label target 
33142  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33143  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33144  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33145  * @cfg {String} iconTooltip default "This field is required"
33146  * @cfg {String} indicatorpos (left|right) default left
33147  * 
33148  * @constructor
33149  * Create a new FieldLabel
33150  * @param {Object} config The config object
33151  */
33152
33153 Roo.bootstrap.FieldLabel = function(config){
33154     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33155     
33156     this.addEvents({
33157             /**
33158              * @event invalid
33159              * Fires after the field has been marked as invalid.
33160              * @param {Roo.form.FieldLabel} this
33161              * @param {String} msg The validation message
33162              */
33163             invalid : true,
33164             /**
33165              * @event valid
33166              * Fires after the field has been validated with no errors.
33167              * @param {Roo.form.FieldLabel} this
33168              */
33169             valid : true
33170         });
33171 };
33172
33173 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33174     
33175     tag: 'label',
33176     cls: '',
33177     html: '',
33178     target: '',
33179     allowBlank : true,
33180     invalidClass : 'has-warning',
33181     validClass : 'has-success',
33182     iconTooltip : 'This field is required',
33183     indicatorpos : 'left',
33184     
33185     getAutoCreate : function(){
33186         
33187         var cls = "";
33188         if (!this.allowBlank) {
33189             cls  = "visible";
33190         }
33191         
33192         var cfg = {
33193             tag : this.tag,
33194             cls : 'roo-bootstrap-field-label ' + this.cls,
33195             for : this.target,
33196             cn : [
33197                 {
33198                     tag : 'i',
33199                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33200                     tooltip : this.iconTooltip
33201                 },
33202                 {
33203                     tag : 'span',
33204                     html : this.html
33205                 }
33206             ] 
33207         };
33208         
33209         if(this.indicatorpos == 'right'){
33210             var cfg = {
33211                 tag : this.tag,
33212                 cls : 'roo-bootstrap-field-label ' + this.cls,
33213                 for : this.target,
33214                 cn : [
33215                     {
33216                         tag : 'span',
33217                         html : this.html
33218                     },
33219                     {
33220                         tag : 'i',
33221                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33222                         tooltip : this.iconTooltip
33223                     }
33224                 ] 
33225             };
33226         }
33227         
33228         return cfg;
33229     },
33230     
33231     initEvents: function() 
33232     {
33233         Roo.bootstrap.Element.superclass.initEvents.call(this);
33234         
33235         this.indicator = this.indicatorEl();
33236         
33237         if(this.indicator){
33238             this.indicator.removeClass('visible');
33239             this.indicator.addClass('invisible');
33240         }
33241         
33242         Roo.bootstrap.FieldLabel.register(this);
33243     },
33244     
33245     indicatorEl : function()
33246     {
33247         var indicator = this.el.select('i.roo-required-indicator',true).first();
33248         
33249         if(!indicator){
33250             return false;
33251         }
33252         
33253         return indicator;
33254         
33255     },
33256     
33257     /**
33258      * Mark this field as valid
33259      */
33260     markValid : function()
33261     {
33262         if(this.indicator){
33263             this.indicator.removeClass('visible');
33264             this.indicator.addClass('invisible');
33265         }
33266         if (Roo.bootstrap.version == 3) {
33267             this.el.removeClass(this.invalidClass);
33268             this.el.addClass(this.validClass);
33269         } else {
33270             this.el.removeClass('is-invalid');
33271             this.el.addClass('is-valid');
33272         }
33273         
33274         
33275         this.fireEvent('valid', this);
33276     },
33277     
33278     /**
33279      * Mark this field as invalid
33280      * @param {String} msg The validation message
33281      */
33282     markInvalid : function(msg)
33283     {
33284         if(this.indicator){
33285             this.indicator.removeClass('invisible');
33286             this.indicator.addClass('visible');
33287         }
33288           if (Roo.bootstrap.version == 3) {
33289             this.el.removeClass(this.validClass);
33290             this.el.addClass(this.invalidClass);
33291         } else {
33292             this.el.removeClass('is-valid');
33293             this.el.addClass('is-invalid');
33294         }
33295         
33296         
33297         this.fireEvent('invalid', this, msg);
33298     }
33299     
33300    
33301 });
33302
33303 Roo.apply(Roo.bootstrap.FieldLabel, {
33304     
33305     groups: {},
33306     
33307      /**
33308     * register a FieldLabel Group
33309     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33310     */
33311     register : function(label)
33312     {
33313         if(this.groups.hasOwnProperty(label.target)){
33314             return;
33315         }
33316      
33317         this.groups[label.target] = label;
33318         
33319     },
33320     /**
33321     * fetch a FieldLabel Group based on the target
33322     * @param {string} target
33323     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33324     */
33325     get: function(target) {
33326         if (typeof(this.groups[target]) == 'undefined') {
33327             return false;
33328         }
33329         
33330         return this.groups[target] ;
33331     }
33332 });
33333
33334  
33335
33336  /*
33337  * - LGPL
33338  *
33339  * page DateSplitField.
33340  * 
33341  */
33342
33343
33344 /**
33345  * @class Roo.bootstrap.DateSplitField
33346  * @extends Roo.bootstrap.Component
33347  * Bootstrap DateSplitField class
33348  * @cfg {string} fieldLabel - the label associated
33349  * @cfg {Number} labelWidth set the width of label (0-12)
33350  * @cfg {String} labelAlign (top|left)
33351  * @cfg {Boolean} dayAllowBlank (true|false) default false
33352  * @cfg {Boolean} monthAllowBlank (true|false) default false
33353  * @cfg {Boolean} yearAllowBlank (true|false) default false
33354  * @cfg {string} dayPlaceholder 
33355  * @cfg {string} monthPlaceholder
33356  * @cfg {string} yearPlaceholder
33357  * @cfg {string} dayFormat default 'd'
33358  * @cfg {string} monthFormat default 'm'
33359  * @cfg {string} yearFormat default 'Y'
33360  * @cfg {Number} labellg set the width of label (1-12)
33361  * @cfg {Number} labelmd set the width of label (1-12)
33362  * @cfg {Number} labelsm set the width of label (1-12)
33363  * @cfg {Number} labelxs set the width of label (1-12)
33364
33365  *     
33366  * @constructor
33367  * Create a new DateSplitField
33368  * @param {Object} config The config object
33369  */
33370
33371 Roo.bootstrap.DateSplitField = function(config){
33372     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33373     
33374     this.addEvents({
33375         // raw events
33376          /**
33377          * @event years
33378          * getting the data of years
33379          * @param {Roo.bootstrap.DateSplitField} this
33380          * @param {Object} years
33381          */
33382         "years" : true,
33383         /**
33384          * @event days
33385          * getting the data of days
33386          * @param {Roo.bootstrap.DateSplitField} this
33387          * @param {Object} days
33388          */
33389         "days" : true,
33390         /**
33391          * @event invalid
33392          * Fires after the field has been marked as invalid.
33393          * @param {Roo.form.Field} this
33394          * @param {String} msg The validation message
33395          */
33396         invalid : true,
33397        /**
33398          * @event valid
33399          * Fires after the field has been validated with no errors.
33400          * @param {Roo.form.Field} this
33401          */
33402         valid : true
33403     });
33404 };
33405
33406 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33407     
33408     fieldLabel : '',
33409     labelAlign : 'top',
33410     labelWidth : 3,
33411     dayAllowBlank : false,
33412     monthAllowBlank : false,
33413     yearAllowBlank : false,
33414     dayPlaceholder : '',
33415     monthPlaceholder : '',
33416     yearPlaceholder : '',
33417     dayFormat : 'd',
33418     monthFormat : 'm',
33419     yearFormat : 'Y',
33420     isFormField : true,
33421     labellg : 0,
33422     labelmd : 0,
33423     labelsm : 0,
33424     labelxs : 0,
33425     
33426     getAutoCreate : function()
33427     {
33428         var cfg = {
33429             tag : 'div',
33430             cls : 'row roo-date-split-field-group',
33431             cn : [
33432                 {
33433                     tag : 'input',
33434                     type : 'hidden',
33435                     cls : 'form-hidden-field roo-date-split-field-group-value',
33436                     name : this.name
33437                 }
33438             ]
33439         };
33440         
33441         var labelCls = 'col-md-12';
33442         var contentCls = 'col-md-4';
33443         
33444         if(this.fieldLabel){
33445             
33446             var label = {
33447                 tag : 'div',
33448                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33449                 cn : [
33450                     {
33451                         tag : 'label',
33452                         html : this.fieldLabel
33453                     }
33454                 ]
33455             };
33456             
33457             if(this.labelAlign == 'left'){
33458             
33459                 if(this.labelWidth > 12){
33460                     label.style = "width: " + this.labelWidth + 'px';
33461                 }
33462
33463                 if(this.labelWidth < 13 && this.labelmd == 0){
33464                     this.labelmd = this.labelWidth;
33465                 }
33466
33467                 if(this.labellg > 0){
33468                     labelCls = ' col-lg-' + this.labellg;
33469                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33470                 }
33471
33472                 if(this.labelmd > 0){
33473                     labelCls = ' col-md-' + this.labelmd;
33474                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33475                 }
33476
33477                 if(this.labelsm > 0){
33478                     labelCls = ' col-sm-' + this.labelsm;
33479                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33480                 }
33481
33482                 if(this.labelxs > 0){
33483                     labelCls = ' col-xs-' + this.labelxs;
33484                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33485                 }
33486             }
33487             
33488             label.cls += ' ' + labelCls;
33489             
33490             cfg.cn.push(label);
33491         }
33492         
33493         Roo.each(['day', 'month', 'year'], function(t){
33494             cfg.cn.push({
33495                 tag : 'div',
33496                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33497             });
33498         }, this);
33499         
33500         return cfg;
33501     },
33502     
33503     inputEl: function ()
33504     {
33505         return this.el.select('.roo-date-split-field-group-value', true).first();
33506     },
33507     
33508     onRender : function(ct, position) 
33509     {
33510         var _this = this;
33511         
33512         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33513         
33514         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33515         
33516         this.dayField = new Roo.bootstrap.ComboBox({
33517             allowBlank : this.dayAllowBlank,
33518             alwaysQuery : true,
33519             displayField : 'value',
33520             editable : false,
33521             fieldLabel : '',
33522             forceSelection : true,
33523             mode : 'local',
33524             placeholder : this.dayPlaceholder,
33525             selectOnFocus : true,
33526             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33527             triggerAction : 'all',
33528             typeAhead : true,
33529             valueField : 'value',
33530             store : new Roo.data.SimpleStore({
33531                 data : (function() {    
33532                     var days = [];
33533                     _this.fireEvent('days', _this, days);
33534                     return days;
33535                 })(),
33536                 fields : [ 'value' ]
33537             }),
33538             listeners : {
33539                 select : function (_self, record, index)
33540                 {
33541                     _this.setValue(_this.getValue());
33542                 }
33543             }
33544         });
33545
33546         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33547         
33548         this.monthField = new Roo.bootstrap.MonthField({
33549             after : '<i class=\"fa fa-calendar\"></i>',
33550             allowBlank : this.monthAllowBlank,
33551             placeholder : this.monthPlaceholder,
33552             readOnly : true,
33553             listeners : {
33554                 render : function (_self)
33555                 {
33556                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33557                         e.preventDefault();
33558                         _self.focus();
33559                     });
33560                 },
33561                 select : function (_self, oldvalue, newvalue)
33562                 {
33563                     _this.setValue(_this.getValue());
33564                 }
33565             }
33566         });
33567         
33568         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33569         
33570         this.yearField = new Roo.bootstrap.ComboBox({
33571             allowBlank : this.yearAllowBlank,
33572             alwaysQuery : true,
33573             displayField : 'value',
33574             editable : false,
33575             fieldLabel : '',
33576             forceSelection : true,
33577             mode : 'local',
33578             placeholder : this.yearPlaceholder,
33579             selectOnFocus : true,
33580             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33581             triggerAction : 'all',
33582             typeAhead : true,
33583             valueField : 'value',
33584             store : new Roo.data.SimpleStore({
33585                 data : (function() {
33586                     var years = [];
33587                     _this.fireEvent('years', _this, years);
33588                     return years;
33589                 })(),
33590                 fields : [ 'value' ]
33591             }),
33592             listeners : {
33593                 select : function (_self, record, index)
33594                 {
33595                     _this.setValue(_this.getValue());
33596                 }
33597             }
33598         });
33599
33600         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33601     },
33602     
33603     setValue : function(v, format)
33604     {
33605         this.inputEl.dom.value = v;
33606         
33607         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33608         
33609         var d = Date.parseDate(v, f);
33610         
33611         if(!d){
33612             this.validate();
33613             return;
33614         }
33615         
33616         this.setDay(d.format(this.dayFormat));
33617         this.setMonth(d.format(this.monthFormat));
33618         this.setYear(d.format(this.yearFormat));
33619         
33620         this.validate();
33621         
33622         return;
33623     },
33624     
33625     setDay : function(v)
33626     {
33627         this.dayField.setValue(v);
33628         this.inputEl.dom.value = this.getValue();
33629         this.validate();
33630         return;
33631     },
33632     
33633     setMonth : function(v)
33634     {
33635         this.monthField.setValue(v, true);
33636         this.inputEl.dom.value = this.getValue();
33637         this.validate();
33638         return;
33639     },
33640     
33641     setYear : function(v)
33642     {
33643         this.yearField.setValue(v);
33644         this.inputEl.dom.value = this.getValue();
33645         this.validate();
33646         return;
33647     },
33648     
33649     getDay : function()
33650     {
33651         return this.dayField.getValue();
33652     },
33653     
33654     getMonth : function()
33655     {
33656         return this.monthField.getValue();
33657     },
33658     
33659     getYear : function()
33660     {
33661         return this.yearField.getValue();
33662     },
33663     
33664     getValue : function()
33665     {
33666         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33667         
33668         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33669         
33670         return date;
33671     },
33672     
33673     reset : function()
33674     {
33675         this.setDay('');
33676         this.setMonth('');
33677         this.setYear('');
33678         this.inputEl.dom.value = '';
33679         this.validate();
33680         return;
33681     },
33682     
33683     validate : function()
33684     {
33685         var d = this.dayField.validate();
33686         var m = this.monthField.validate();
33687         var y = this.yearField.validate();
33688         
33689         var valid = true;
33690         
33691         if(
33692                 (!this.dayAllowBlank && !d) ||
33693                 (!this.monthAllowBlank && !m) ||
33694                 (!this.yearAllowBlank && !y)
33695         ){
33696             valid = false;
33697         }
33698         
33699         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33700             return valid;
33701         }
33702         
33703         if(valid){
33704             this.markValid();
33705             return valid;
33706         }
33707         
33708         this.markInvalid();
33709         
33710         return valid;
33711     },
33712     
33713     markValid : function()
33714     {
33715         
33716         var label = this.el.select('label', true).first();
33717         var icon = this.el.select('i.fa-star', true).first();
33718
33719         if(label && icon){
33720             icon.remove();
33721         }
33722         
33723         this.fireEvent('valid', this);
33724     },
33725     
33726      /**
33727      * Mark this field as invalid
33728      * @param {String} msg The validation message
33729      */
33730     markInvalid : function(msg)
33731     {
33732         
33733         var label = this.el.select('label', true).first();
33734         var icon = this.el.select('i.fa-star', true).first();
33735
33736         if(label && !icon){
33737             this.el.select('.roo-date-split-field-label', true).createChild({
33738                 tag : 'i',
33739                 cls : 'text-danger fa fa-lg fa-star',
33740                 tooltip : 'This field is required',
33741                 style : 'margin-right:5px;'
33742             }, label, true);
33743         }
33744         
33745         this.fireEvent('invalid', this, msg);
33746     },
33747     
33748     clearInvalid : function()
33749     {
33750         var label = this.el.select('label', true).first();
33751         var icon = this.el.select('i.fa-star', true).first();
33752
33753         if(label && icon){
33754             icon.remove();
33755         }
33756         
33757         this.fireEvent('valid', this);
33758     },
33759     
33760     getName: function()
33761     {
33762         return this.name;
33763     }
33764     
33765 });
33766
33767  /**
33768  *
33769  * This is based on 
33770  * http://masonry.desandro.com
33771  *
33772  * The idea is to render all the bricks based on vertical width...
33773  *
33774  * The original code extends 'outlayer' - we might need to use that....
33775  * 
33776  */
33777
33778
33779 /**
33780  * @class Roo.bootstrap.LayoutMasonry
33781  * @extends Roo.bootstrap.Component
33782  * Bootstrap Layout Masonry class
33783  * 
33784  * @constructor
33785  * Create a new Element
33786  * @param {Object} config The config object
33787  */
33788
33789 Roo.bootstrap.LayoutMasonry = function(config){
33790     
33791     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33792     
33793     this.bricks = [];
33794     
33795     Roo.bootstrap.LayoutMasonry.register(this);
33796     
33797     this.addEvents({
33798         // raw events
33799         /**
33800          * @event layout
33801          * Fire after layout the items
33802          * @param {Roo.bootstrap.LayoutMasonry} this
33803          * @param {Roo.EventObject} e
33804          */
33805         "layout" : true
33806     });
33807     
33808 };
33809
33810 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33811     
33812     /**
33813      * @cfg {Boolean} isLayoutInstant = no animation?
33814      */   
33815     isLayoutInstant : false, // needed?
33816    
33817     /**
33818      * @cfg {Number} boxWidth  width of the columns
33819      */   
33820     boxWidth : 450,
33821     
33822       /**
33823      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33824      */   
33825     boxHeight : 0,
33826     
33827     /**
33828      * @cfg {Number} padWidth padding below box..
33829      */   
33830     padWidth : 10, 
33831     
33832     /**
33833      * @cfg {Number} gutter gutter width..
33834      */   
33835     gutter : 10,
33836     
33837      /**
33838      * @cfg {Number} maxCols maximum number of columns
33839      */   
33840     
33841     maxCols: 0,
33842     
33843     /**
33844      * @cfg {Boolean} isAutoInitial defalut true
33845      */   
33846     isAutoInitial : true, 
33847     
33848     containerWidth: 0,
33849     
33850     /**
33851      * @cfg {Boolean} isHorizontal defalut false
33852      */   
33853     isHorizontal : false, 
33854
33855     currentSize : null,
33856     
33857     tag: 'div',
33858     
33859     cls: '',
33860     
33861     bricks: null, //CompositeElement
33862     
33863     cols : 1,
33864     
33865     _isLayoutInited : false,
33866     
33867 //    isAlternative : false, // only use for vertical layout...
33868     
33869     /**
33870      * @cfg {Number} alternativePadWidth padding below box..
33871      */   
33872     alternativePadWidth : 50,
33873     
33874     selectedBrick : [],
33875     
33876     getAutoCreate : function(){
33877         
33878         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33879         
33880         var cfg = {
33881             tag: this.tag,
33882             cls: 'blog-masonary-wrapper ' + this.cls,
33883             cn : {
33884                 cls : 'mas-boxes masonary'
33885             }
33886         };
33887         
33888         return cfg;
33889     },
33890     
33891     getChildContainer: function( )
33892     {
33893         if (this.boxesEl) {
33894             return this.boxesEl;
33895         }
33896         
33897         this.boxesEl = this.el.select('.mas-boxes').first();
33898         
33899         return this.boxesEl;
33900     },
33901     
33902     
33903     initEvents : function()
33904     {
33905         var _this = this;
33906         
33907         if(this.isAutoInitial){
33908             Roo.log('hook children rendered');
33909             this.on('childrenrendered', function() {
33910                 Roo.log('children rendered');
33911                 _this.initial();
33912             } ,this);
33913         }
33914     },
33915     
33916     initial : function()
33917     {
33918         this.selectedBrick = [];
33919         
33920         this.currentSize = this.el.getBox(true);
33921         
33922         Roo.EventManager.onWindowResize(this.resize, this); 
33923
33924         if(!this.isAutoInitial){
33925             this.layout();
33926             return;
33927         }
33928         
33929         this.layout();
33930         
33931         return;
33932         //this.layout.defer(500,this);
33933         
33934     },
33935     
33936     resize : function()
33937     {
33938         var cs = this.el.getBox(true);
33939         
33940         if (
33941                 this.currentSize.width == cs.width && 
33942                 this.currentSize.x == cs.x && 
33943                 this.currentSize.height == cs.height && 
33944                 this.currentSize.y == cs.y 
33945         ) {
33946             Roo.log("no change in with or X or Y");
33947             return;
33948         }
33949         
33950         this.currentSize = cs;
33951         
33952         this.layout();
33953         
33954     },
33955     
33956     layout : function()
33957     {   
33958         this._resetLayout();
33959         
33960         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33961         
33962         this.layoutItems( isInstant );
33963       
33964         this._isLayoutInited = true;
33965         
33966         this.fireEvent('layout', this);
33967         
33968     },
33969     
33970     _resetLayout : function()
33971     {
33972         if(this.isHorizontal){
33973             this.horizontalMeasureColumns();
33974             return;
33975         }
33976         
33977         this.verticalMeasureColumns();
33978         
33979     },
33980     
33981     verticalMeasureColumns : function()
33982     {
33983         this.getContainerWidth();
33984         
33985 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33986 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33987 //            return;
33988 //        }
33989         
33990         var boxWidth = this.boxWidth + this.padWidth;
33991         
33992         if(this.containerWidth < this.boxWidth){
33993             boxWidth = this.containerWidth
33994         }
33995         
33996         var containerWidth = this.containerWidth;
33997         
33998         var cols = Math.floor(containerWidth / boxWidth);
33999         
34000         this.cols = Math.max( cols, 1 );
34001         
34002         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34003         
34004         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34005         
34006         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34007         
34008         this.colWidth = boxWidth + avail - this.padWidth;
34009         
34010         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34011         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34012     },
34013     
34014     horizontalMeasureColumns : function()
34015     {
34016         this.getContainerWidth();
34017         
34018         var boxWidth = this.boxWidth;
34019         
34020         if(this.containerWidth < boxWidth){
34021             boxWidth = this.containerWidth;
34022         }
34023         
34024         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34025         
34026         this.el.setHeight(boxWidth);
34027         
34028     },
34029     
34030     getContainerWidth : function()
34031     {
34032         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34033     },
34034     
34035     layoutItems : function( isInstant )
34036     {
34037         Roo.log(this.bricks);
34038         
34039         var items = Roo.apply([], this.bricks);
34040         
34041         if(this.isHorizontal){
34042             this._horizontalLayoutItems( items , isInstant );
34043             return;
34044         }
34045         
34046 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34047 //            this._verticalAlternativeLayoutItems( items , isInstant );
34048 //            return;
34049 //        }
34050         
34051         this._verticalLayoutItems( items , isInstant );
34052         
34053     },
34054     
34055     _verticalLayoutItems : function ( items , isInstant)
34056     {
34057         if ( !items || !items.length ) {
34058             return;
34059         }
34060         
34061         var standard = [
34062             ['xs', 'xs', 'xs', 'tall'],
34063             ['xs', 'xs', 'tall'],
34064             ['xs', 'xs', 'sm'],
34065             ['xs', 'xs', 'xs'],
34066             ['xs', 'tall'],
34067             ['xs', 'sm'],
34068             ['xs', 'xs'],
34069             ['xs'],
34070             
34071             ['sm', 'xs', 'xs'],
34072             ['sm', 'xs'],
34073             ['sm'],
34074             
34075             ['tall', 'xs', 'xs', 'xs'],
34076             ['tall', 'xs', 'xs'],
34077             ['tall', 'xs'],
34078             ['tall']
34079             
34080         ];
34081         
34082         var queue = [];
34083         
34084         var boxes = [];
34085         
34086         var box = [];
34087         
34088         Roo.each(items, function(item, k){
34089             
34090             switch (item.size) {
34091                 // these layouts take up a full box,
34092                 case 'md' :
34093                 case 'md-left' :
34094                 case 'md-right' :
34095                 case 'wide' :
34096                     
34097                     if(box.length){
34098                         boxes.push(box);
34099                         box = [];
34100                     }
34101                     
34102                     boxes.push([item]);
34103                     
34104                     break;
34105                     
34106                 case 'xs' :
34107                 case 'sm' :
34108                 case 'tall' :
34109                     
34110                     box.push(item);
34111                     
34112                     break;
34113                 default :
34114                     break;
34115                     
34116             }
34117             
34118         }, this);
34119         
34120         if(box.length){
34121             boxes.push(box);
34122             box = [];
34123         }
34124         
34125         var filterPattern = function(box, length)
34126         {
34127             if(!box.length){
34128                 return;
34129             }
34130             
34131             var match = false;
34132             
34133             var pattern = box.slice(0, length);
34134             
34135             var format = [];
34136             
34137             Roo.each(pattern, function(i){
34138                 format.push(i.size);
34139             }, this);
34140             
34141             Roo.each(standard, function(s){
34142                 
34143                 if(String(s) != String(format)){
34144                     return;
34145                 }
34146                 
34147                 match = true;
34148                 return false;
34149                 
34150             }, this);
34151             
34152             if(!match && length == 1){
34153                 return;
34154             }
34155             
34156             if(!match){
34157                 filterPattern(box, length - 1);
34158                 return;
34159             }
34160                 
34161             queue.push(pattern);
34162
34163             box = box.slice(length, box.length);
34164
34165             filterPattern(box, 4);
34166
34167             return;
34168             
34169         }
34170         
34171         Roo.each(boxes, function(box, k){
34172             
34173             if(!box.length){
34174                 return;
34175             }
34176             
34177             if(box.length == 1){
34178                 queue.push(box);
34179                 return;
34180             }
34181             
34182             filterPattern(box, 4);
34183             
34184         }, this);
34185         
34186         this._processVerticalLayoutQueue( queue, isInstant );
34187         
34188     },
34189     
34190 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34191 //    {
34192 //        if ( !items || !items.length ) {
34193 //            return;
34194 //        }
34195 //
34196 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34197 //        
34198 //    },
34199     
34200     _horizontalLayoutItems : function ( items , isInstant)
34201     {
34202         if ( !items || !items.length || items.length < 3) {
34203             return;
34204         }
34205         
34206         items.reverse();
34207         
34208         var eItems = items.slice(0, 3);
34209         
34210         items = items.slice(3, items.length);
34211         
34212         var standard = [
34213             ['xs', 'xs', 'xs', 'wide'],
34214             ['xs', 'xs', 'wide'],
34215             ['xs', 'xs', 'sm'],
34216             ['xs', 'xs', 'xs'],
34217             ['xs', 'wide'],
34218             ['xs', 'sm'],
34219             ['xs', 'xs'],
34220             ['xs'],
34221             
34222             ['sm', 'xs', 'xs'],
34223             ['sm', 'xs'],
34224             ['sm'],
34225             
34226             ['wide', 'xs', 'xs', 'xs'],
34227             ['wide', 'xs', 'xs'],
34228             ['wide', 'xs'],
34229             ['wide'],
34230             
34231             ['wide-thin']
34232         ];
34233         
34234         var queue = [];
34235         
34236         var boxes = [];
34237         
34238         var box = [];
34239         
34240         Roo.each(items, function(item, k){
34241             
34242             switch (item.size) {
34243                 case 'md' :
34244                 case 'md-left' :
34245                 case 'md-right' :
34246                 case 'tall' :
34247                     
34248                     if(box.length){
34249                         boxes.push(box);
34250                         box = [];
34251                     }
34252                     
34253                     boxes.push([item]);
34254                     
34255                     break;
34256                     
34257                 case 'xs' :
34258                 case 'sm' :
34259                 case 'wide' :
34260                 case 'wide-thin' :
34261                     
34262                     box.push(item);
34263                     
34264                     break;
34265                 default :
34266                     break;
34267                     
34268             }
34269             
34270         }, this);
34271         
34272         if(box.length){
34273             boxes.push(box);
34274             box = [];
34275         }
34276         
34277         var filterPattern = function(box, length)
34278         {
34279             if(!box.length){
34280                 return;
34281             }
34282             
34283             var match = false;
34284             
34285             var pattern = box.slice(0, length);
34286             
34287             var format = [];
34288             
34289             Roo.each(pattern, function(i){
34290                 format.push(i.size);
34291             }, this);
34292             
34293             Roo.each(standard, function(s){
34294                 
34295                 if(String(s) != String(format)){
34296                     return;
34297                 }
34298                 
34299                 match = true;
34300                 return false;
34301                 
34302             }, this);
34303             
34304             if(!match && length == 1){
34305                 return;
34306             }
34307             
34308             if(!match){
34309                 filterPattern(box, length - 1);
34310                 return;
34311             }
34312                 
34313             queue.push(pattern);
34314
34315             box = box.slice(length, box.length);
34316
34317             filterPattern(box, 4);
34318
34319             return;
34320             
34321         }
34322         
34323         Roo.each(boxes, function(box, k){
34324             
34325             if(!box.length){
34326                 return;
34327             }
34328             
34329             if(box.length == 1){
34330                 queue.push(box);
34331                 return;
34332             }
34333             
34334             filterPattern(box, 4);
34335             
34336         }, this);
34337         
34338         
34339         var prune = [];
34340         
34341         var pos = this.el.getBox(true);
34342         
34343         var minX = pos.x;
34344         
34345         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34346         
34347         var hit_end = false;
34348         
34349         Roo.each(queue, function(box){
34350             
34351             if(hit_end){
34352                 
34353                 Roo.each(box, function(b){
34354                 
34355                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34356                     b.el.hide();
34357
34358                 }, this);
34359
34360                 return;
34361             }
34362             
34363             var mx = 0;
34364             
34365             Roo.each(box, function(b){
34366                 
34367                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34368                 b.el.show();
34369
34370                 mx = Math.max(mx, b.x);
34371                 
34372             }, this);
34373             
34374             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34375             
34376             if(maxX < minX){
34377                 
34378                 Roo.each(box, function(b){
34379                 
34380                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34381                     b.el.hide();
34382                     
34383                 }, this);
34384                 
34385                 hit_end = true;
34386                 
34387                 return;
34388             }
34389             
34390             prune.push(box);
34391             
34392         }, this);
34393         
34394         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34395     },
34396     
34397     /** Sets position of item in DOM
34398     * @param {Element} item
34399     * @param {Number} x - horizontal position
34400     * @param {Number} y - vertical position
34401     * @param {Boolean} isInstant - disables transitions
34402     */
34403     _processVerticalLayoutQueue : function( queue, isInstant )
34404     {
34405         var pos = this.el.getBox(true);
34406         var x = pos.x;
34407         var y = pos.y;
34408         var maxY = [];
34409         
34410         for (var i = 0; i < this.cols; i++){
34411             maxY[i] = pos.y;
34412         }
34413         
34414         Roo.each(queue, function(box, k){
34415             
34416             var col = k % this.cols;
34417             
34418             Roo.each(box, function(b,kk){
34419                 
34420                 b.el.position('absolute');
34421                 
34422                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34423                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34424                 
34425                 if(b.size == 'md-left' || b.size == 'md-right'){
34426                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34427                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34428                 }
34429                 
34430                 b.el.setWidth(width);
34431                 b.el.setHeight(height);
34432                 // iframe?
34433                 b.el.select('iframe',true).setSize(width,height);
34434                 
34435             }, this);
34436             
34437             for (var i = 0; i < this.cols; i++){
34438                 
34439                 if(maxY[i] < maxY[col]){
34440                     col = i;
34441                     continue;
34442                 }
34443                 
34444                 col = Math.min(col, i);
34445                 
34446             }
34447             
34448             x = pos.x + col * (this.colWidth + this.padWidth);
34449             
34450             y = maxY[col];
34451             
34452             var positions = [];
34453             
34454             switch (box.length){
34455                 case 1 :
34456                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34457                     break;
34458                 case 2 :
34459                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34460                     break;
34461                 case 3 :
34462                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34463                     break;
34464                 case 4 :
34465                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34466                     break;
34467                 default :
34468                     break;
34469             }
34470             
34471             Roo.each(box, function(b,kk){
34472                 
34473                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34474                 
34475                 var sz = b.el.getSize();
34476                 
34477                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34478                 
34479             }, this);
34480             
34481         }, this);
34482         
34483         var mY = 0;
34484         
34485         for (var i = 0; i < this.cols; i++){
34486             mY = Math.max(mY, maxY[i]);
34487         }
34488         
34489         this.el.setHeight(mY - pos.y);
34490         
34491     },
34492     
34493 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34494 //    {
34495 //        var pos = this.el.getBox(true);
34496 //        var x = pos.x;
34497 //        var y = pos.y;
34498 //        var maxX = pos.right;
34499 //        
34500 //        var maxHeight = 0;
34501 //        
34502 //        Roo.each(items, function(item, k){
34503 //            
34504 //            var c = k % 2;
34505 //            
34506 //            item.el.position('absolute');
34507 //                
34508 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34509 //
34510 //            item.el.setWidth(width);
34511 //
34512 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34513 //
34514 //            item.el.setHeight(height);
34515 //            
34516 //            if(c == 0){
34517 //                item.el.setXY([x, y], isInstant ? false : true);
34518 //            } else {
34519 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34520 //            }
34521 //            
34522 //            y = y + height + this.alternativePadWidth;
34523 //            
34524 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34525 //            
34526 //        }, this);
34527 //        
34528 //        this.el.setHeight(maxHeight);
34529 //        
34530 //    },
34531     
34532     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34533     {
34534         var pos = this.el.getBox(true);
34535         
34536         var minX = pos.x;
34537         var minY = pos.y;
34538         
34539         var maxX = pos.right;
34540         
34541         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34542         
34543         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34544         
34545         Roo.each(queue, function(box, k){
34546             
34547             Roo.each(box, function(b, kk){
34548                 
34549                 b.el.position('absolute');
34550                 
34551                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34552                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34553                 
34554                 if(b.size == 'md-left' || b.size == 'md-right'){
34555                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34556                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34557                 }
34558                 
34559                 b.el.setWidth(width);
34560                 b.el.setHeight(height);
34561                 
34562             }, this);
34563             
34564             if(!box.length){
34565                 return;
34566             }
34567             
34568             var positions = [];
34569             
34570             switch (box.length){
34571                 case 1 :
34572                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34573                     break;
34574                 case 2 :
34575                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34576                     break;
34577                 case 3 :
34578                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34579                     break;
34580                 case 4 :
34581                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34582                     break;
34583                 default :
34584                     break;
34585             }
34586             
34587             Roo.each(box, function(b,kk){
34588                 
34589                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34590                 
34591                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34592                 
34593             }, this);
34594             
34595         }, this);
34596         
34597     },
34598     
34599     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34600     {
34601         Roo.each(eItems, function(b,k){
34602             
34603             b.size = (k == 0) ? 'sm' : 'xs';
34604             b.x = (k == 0) ? 2 : 1;
34605             b.y = (k == 0) ? 2 : 1;
34606             
34607             b.el.position('absolute');
34608             
34609             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34610                 
34611             b.el.setWidth(width);
34612             
34613             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34614             
34615             b.el.setHeight(height);
34616             
34617         }, this);
34618
34619         var positions = [];
34620         
34621         positions.push({
34622             x : maxX - this.unitWidth * 2 - this.gutter,
34623             y : minY
34624         });
34625         
34626         positions.push({
34627             x : maxX - this.unitWidth,
34628             y : minY + (this.unitWidth + this.gutter) * 2
34629         });
34630         
34631         positions.push({
34632             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34633             y : minY
34634         });
34635         
34636         Roo.each(eItems, function(b,k){
34637             
34638             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34639
34640         }, this);
34641         
34642     },
34643     
34644     getVerticalOneBoxColPositions : function(x, y, box)
34645     {
34646         var pos = [];
34647         
34648         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34649         
34650         if(box[0].size == 'md-left'){
34651             rand = 0;
34652         }
34653         
34654         if(box[0].size == 'md-right'){
34655             rand = 1;
34656         }
34657         
34658         pos.push({
34659             x : x + (this.unitWidth + this.gutter) * rand,
34660             y : y
34661         });
34662         
34663         return pos;
34664     },
34665     
34666     getVerticalTwoBoxColPositions : function(x, y, box)
34667     {
34668         var pos = [];
34669         
34670         if(box[0].size == 'xs'){
34671             
34672             pos.push({
34673                 x : x,
34674                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34675             });
34676
34677             pos.push({
34678                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34679                 y : y
34680             });
34681             
34682             return pos;
34683             
34684         }
34685         
34686         pos.push({
34687             x : x,
34688             y : y
34689         });
34690
34691         pos.push({
34692             x : x + (this.unitWidth + this.gutter) * 2,
34693             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34694         });
34695         
34696         return pos;
34697         
34698     },
34699     
34700     getVerticalThreeBoxColPositions : function(x, y, box)
34701     {
34702         var pos = [];
34703         
34704         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34705             
34706             pos.push({
34707                 x : x,
34708                 y : y
34709             });
34710
34711             pos.push({
34712                 x : x + (this.unitWidth + this.gutter) * 1,
34713                 y : y
34714             });
34715             
34716             pos.push({
34717                 x : x + (this.unitWidth + this.gutter) * 2,
34718                 y : y
34719             });
34720             
34721             return pos;
34722             
34723         }
34724         
34725         if(box[0].size == 'xs' && box[1].size == 'xs'){
34726             
34727             pos.push({
34728                 x : x,
34729                 y : y
34730             });
34731
34732             pos.push({
34733                 x : x,
34734                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34735             });
34736             
34737             pos.push({
34738                 x : x + (this.unitWidth + this.gutter) * 1,
34739                 y : y
34740             });
34741             
34742             return pos;
34743             
34744         }
34745         
34746         pos.push({
34747             x : x,
34748             y : y
34749         });
34750
34751         pos.push({
34752             x : x + (this.unitWidth + this.gutter) * 2,
34753             y : y
34754         });
34755
34756         pos.push({
34757             x : x + (this.unitWidth + this.gutter) * 2,
34758             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34759         });
34760             
34761         return pos;
34762         
34763     },
34764     
34765     getVerticalFourBoxColPositions : function(x, y, box)
34766     {
34767         var pos = [];
34768         
34769         if(box[0].size == 'xs'){
34770             
34771             pos.push({
34772                 x : x,
34773                 y : y
34774             });
34775
34776             pos.push({
34777                 x : x,
34778                 y : y + (this.unitHeight + this.gutter) * 1
34779             });
34780             
34781             pos.push({
34782                 x : x,
34783                 y : y + (this.unitHeight + this.gutter) * 2
34784             });
34785             
34786             pos.push({
34787                 x : x + (this.unitWidth + this.gutter) * 1,
34788                 y : y
34789             });
34790             
34791             return pos;
34792             
34793         }
34794         
34795         pos.push({
34796             x : x,
34797             y : y
34798         });
34799
34800         pos.push({
34801             x : x + (this.unitWidth + this.gutter) * 2,
34802             y : y
34803         });
34804
34805         pos.push({
34806             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34807             y : y + (this.unitHeight + this.gutter) * 1
34808         });
34809
34810         pos.push({
34811             x : x + (this.unitWidth + this.gutter) * 2,
34812             y : y + (this.unitWidth + this.gutter) * 2
34813         });
34814
34815         return pos;
34816         
34817     },
34818     
34819     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34820     {
34821         var pos = [];
34822         
34823         if(box[0].size == 'md-left'){
34824             pos.push({
34825                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34826                 y : minY
34827             });
34828             
34829             return pos;
34830         }
34831         
34832         if(box[0].size == 'md-right'){
34833             pos.push({
34834                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34835                 y : minY + (this.unitWidth + this.gutter) * 1
34836             });
34837             
34838             return pos;
34839         }
34840         
34841         var rand = Math.floor(Math.random() * (4 - box[0].y));
34842         
34843         pos.push({
34844             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34845             y : minY + (this.unitWidth + this.gutter) * rand
34846         });
34847         
34848         return pos;
34849         
34850     },
34851     
34852     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34853     {
34854         var pos = [];
34855         
34856         if(box[0].size == 'xs'){
34857             
34858             pos.push({
34859                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34860                 y : minY
34861             });
34862
34863             pos.push({
34864                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34865                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34866             });
34867             
34868             return pos;
34869             
34870         }
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[1].x - this.gutter * (box[1].x - 1),
34879             y : minY + (this.unitWidth + this.gutter) * 2
34880         });
34881         
34882         return pos;
34883         
34884     },
34885     
34886     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34887     {
34888         var pos = [];
34889         
34890         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34891             
34892             pos.push({
34893                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34894                 y : minY
34895             });
34896
34897             pos.push({
34898                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34899                 y : minY + (this.unitWidth + this.gutter) * 1
34900             });
34901             
34902             pos.push({
34903                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34904                 y : minY + (this.unitWidth + this.gutter) * 2
34905             });
34906             
34907             return pos;
34908             
34909         }
34910         
34911         if(box[0].size == 'xs' && box[1].size == 'xs'){
34912             
34913             pos.push({
34914                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34915                 y : minY
34916             });
34917
34918             pos.push({
34919                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34920                 y : minY
34921             });
34922             
34923             pos.push({
34924                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34925                 y : minY + (this.unitWidth + this.gutter) * 1
34926             });
34927             
34928             return pos;
34929             
34930         }
34931         
34932         pos.push({
34933             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34934             y : minY
34935         });
34936
34937         pos.push({
34938             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34939             y : minY + (this.unitWidth + this.gutter) * 2
34940         });
34941
34942         pos.push({
34943             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34944             y : minY + (this.unitWidth + this.gutter) * 2
34945         });
34946             
34947         return pos;
34948         
34949     },
34950     
34951     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34952     {
34953         var pos = [];
34954         
34955         if(box[0].size == 'xs'){
34956             
34957             pos.push({
34958                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34959                 y : minY
34960             });
34961
34962             pos.push({
34963                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34964                 y : minY
34965             });
34966             
34967             pos.push({
34968                 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),
34969                 y : minY
34970             });
34971             
34972             pos.push({
34973                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34974                 y : minY + (this.unitWidth + this.gutter) * 1
34975             });
34976             
34977             return pos;
34978             
34979         }
34980         
34981         pos.push({
34982             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34983             y : minY
34984         });
34985         
34986         pos.push({
34987             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34988             y : minY + (this.unitWidth + this.gutter) * 2
34989         });
34990         
34991         pos.push({
34992             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34993             y : minY + (this.unitWidth + this.gutter) * 2
34994         });
34995         
34996         pos.push({
34997             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),
34998             y : minY + (this.unitWidth + this.gutter) * 2
34999         });
35000
35001         return pos;
35002         
35003     },
35004     
35005     /**
35006     * remove a Masonry Brick
35007     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35008     */
35009     removeBrick : function(brick_id)
35010     {
35011         if (!brick_id) {
35012             return;
35013         }
35014         
35015         for (var i = 0; i<this.bricks.length; i++) {
35016             if (this.bricks[i].id == brick_id) {
35017                 this.bricks.splice(i,1);
35018                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35019                 this.initial();
35020             }
35021         }
35022     },
35023     
35024     /**
35025     * adds a Masonry Brick
35026     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35027     */
35028     addBrick : function(cfg)
35029     {
35030         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35031         //this.register(cn);
35032         cn.parentId = this.id;
35033         cn.render(this.el);
35034         return cn;
35035     },
35036     
35037     /**
35038     * register a Masonry Brick
35039     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35040     */
35041     
35042     register : function(brick)
35043     {
35044         this.bricks.push(brick);
35045         brick.masonryId = this.id;
35046     },
35047     
35048     /**
35049     * clear all the Masonry Brick
35050     */
35051     clearAll : function()
35052     {
35053         this.bricks = [];
35054         //this.getChildContainer().dom.innerHTML = "";
35055         this.el.dom.innerHTML = '';
35056     },
35057     
35058     getSelected : function()
35059     {
35060         if (!this.selectedBrick) {
35061             return false;
35062         }
35063         
35064         return this.selectedBrick;
35065     }
35066 });
35067
35068 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35069     
35070     groups: {},
35071      /**
35072     * register a Masonry Layout
35073     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35074     */
35075     
35076     register : function(layout)
35077     {
35078         this.groups[layout.id] = layout;
35079     },
35080     /**
35081     * fetch a  Masonry Layout based on the masonry layout ID
35082     * @param {string} the masonry layout to add
35083     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35084     */
35085     
35086     get: function(layout_id) {
35087         if (typeof(this.groups[layout_id]) == 'undefined') {
35088             return false;
35089         }
35090         return this.groups[layout_id] ;
35091     }
35092     
35093     
35094     
35095 });
35096
35097  
35098
35099  /**
35100  *
35101  * This is based on 
35102  * http://masonry.desandro.com
35103  *
35104  * The idea is to render all the bricks based on vertical width...
35105  *
35106  * The original code extends 'outlayer' - we might need to use that....
35107  * 
35108  */
35109
35110
35111 /**
35112  * @class Roo.bootstrap.LayoutMasonryAuto
35113  * @extends Roo.bootstrap.Component
35114  * Bootstrap Layout Masonry class
35115  * 
35116  * @constructor
35117  * Create a new Element
35118  * @param {Object} config The config object
35119  */
35120
35121 Roo.bootstrap.LayoutMasonryAuto = function(config){
35122     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35123 };
35124
35125 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35126     
35127       /**
35128      * @cfg {Boolean} isFitWidth  - resize the width..
35129      */   
35130     isFitWidth : false,  // options..
35131     /**
35132      * @cfg {Boolean} isOriginLeft = left align?
35133      */   
35134     isOriginLeft : true,
35135     /**
35136      * @cfg {Boolean} isOriginTop = top align?
35137      */   
35138     isOriginTop : false,
35139     /**
35140      * @cfg {Boolean} isLayoutInstant = no animation?
35141      */   
35142     isLayoutInstant : false, // needed?
35143     /**
35144      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35145      */   
35146     isResizingContainer : true,
35147     /**
35148      * @cfg {Number} columnWidth  width of the columns 
35149      */   
35150     
35151     columnWidth : 0,
35152     
35153     /**
35154      * @cfg {Number} maxCols maximum number of columns
35155      */   
35156     
35157     maxCols: 0,
35158     /**
35159      * @cfg {Number} padHeight padding below box..
35160      */   
35161     
35162     padHeight : 10, 
35163     
35164     /**
35165      * @cfg {Boolean} isAutoInitial defalut true
35166      */   
35167     
35168     isAutoInitial : true, 
35169     
35170     // private?
35171     gutter : 0,
35172     
35173     containerWidth: 0,
35174     initialColumnWidth : 0,
35175     currentSize : null,
35176     
35177     colYs : null, // array.
35178     maxY : 0,
35179     padWidth: 10,
35180     
35181     
35182     tag: 'div',
35183     cls: '',
35184     bricks: null, //CompositeElement
35185     cols : 0, // array?
35186     // element : null, // wrapped now this.el
35187     _isLayoutInited : null, 
35188     
35189     
35190     getAutoCreate : function(){
35191         
35192         var cfg = {
35193             tag: this.tag,
35194             cls: 'blog-masonary-wrapper ' + this.cls,
35195             cn : {
35196                 cls : 'mas-boxes masonary'
35197             }
35198         };
35199         
35200         return cfg;
35201     },
35202     
35203     getChildContainer: function( )
35204     {
35205         if (this.boxesEl) {
35206             return this.boxesEl;
35207         }
35208         
35209         this.boxesEl = this.el.select('.mas-boxes').first();
35210         
35211         return this.boxesEl;
35212     },
35213     
35214     
35215     initEvents : function()
35216     {
35217         var _this = this;
35218         
35219         if(this.isAutoInitial){
35220             Roo.log('hook children rendered');
35221             this.on('childrenrendered', function() {
35222                 Roo.log('children rendered');
35223                 _this.initial();
35224             } ,this);
35225         }
35226         
35227     },
35228     
35229     initial : function()
35230     {
35231         this.reloadItems();
35232
35233         this.currentSize = this.el.getBox(true);
35234
35235         /// was window resize... - let's see if this works..
35236         Roo.EventManager.onWindowResize(this.resize, this); 
35237
35238         if(!this.isAutoInitial){
35239             this.layout();
35240             return;
35241         }
35242         
35243         this.layout.defer(500,this);
35244     },
35245     
35246     reloadItems: function()
35247     {
35248         this.bricks = this.el.select('.masonry-brick', true);
35249         
35250         this.bricks.each(function(b) {
35251             //Roo.log(b.getSize());
35252             if (!b.attr('originalwidth')) {
35253                 b.attr('originalwidth',  b.getSize().width);
35254             }
35255             
35256         });
35257         
35258         Roo.log(this.bricks.elements.length);
35259     },
35260     
35261     resize : function()
35262     {
35263         Roo.log('resize');
35264         var cs = this.el.getBox(true);
35265         
35266         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35267             Roo.log("no change in with or X");
35268             return;
35269         }
35270         this.currentSize = cs;
35271         this.layout();
35272     },
35273     
35274     layout : function()
35275     {
35276          Roo.log('layout');
35277         this._resetLayout();
35278         //this._manageStamps();
35279       
35280         // don't animate first layout
35281         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35282         this.layoutItems( isInstant );
35283       
35284         // flag for initalized
35285         this._isLayoutInited = true;
35286     },
35287     
35288     layoutItems : function( isInstant )
35289     {
35290         //var items = this._getItemsForLayout( this.items );
35291         // original code supports filtering layout items.. we just ignore it..
35292         
35293         this._layoutItems( this.bricks , isInstant );
35294       
35295         this._postLayout();
35296     },
35297     _layoutItems : function ( items , isInstant)
35298     {
35299        //this.fireEvent( 'layout', this, items );
35300     
35301
35302         if ( !items || !items.elements.length ) {
35303           // no items, emit event with empty array
35304             return;
35305         }
35306
35307         var queue = [];
35308         items.each(function(item) {
35309             Roo.log("layout item");
35310             Roo.log(item);
35311             // get x/y object from method
35312             var position = this._getItemLayoutPosition( item );
35313             // enqueue
35314             position.item = item;
35315             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35316             queue.push( position );
35317         }, this);
35318       
35319         this._processLayoutQueue( queue );
35320     },
35321     /** Sets position of item in DOM
35322     * @param {Element} item
35323     * @param {Number} x - horizontal position
35324     * @param {Number} y - vertical position
35325     * @param {Boolean} isInstant - disables transitions
35326     */
35327     _processLayoutQueue : function( queue )
35328     {
35329         for ( var i=0, len = queue.length; i < len; i++ ) {
35330             var obj = queue[i];
35331             obj.item.position('absolute');
35332             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35333         }
35334     },
35335       
35336     
35337     /**
35338     * Any logic you want to do after each layout,
35339     * i.e. size the container
35340     */
35341     _postLayout : function()
35342     {
35343         this.resizeContainer();
35344     },
35345     
35346     resizeContainer : function()
35347     {
35348         if ( !this.isResizingContainer ) {
35349             return;
35350         }
35351         var size = this._getContainerSize();
35352         if ( size ) {
35353             this.el.setSize(size.width,size.height);
35354             this.boxesEl.setSize(size.width,size.height);
35355         }
35356     },
35357     
35358     
35359     
35360     _resetLayout : function()
35361     {
35362         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35363         this.colWidth = this.el.getWidth();
35364         //this.gutter = this.el.getWidth(); 
35365         
35366         this.measureColumns();
35367
35368         // reset column Y
35369         var i = this.cols;
35370         this.colYs = [];
35371         while (i--) {
35372             this.colYs.push( 0 );
35373         }
35374     
35375         this.maxY = 0;
35376     },
35377
35378     measureColumns : function()
35379     {
35380         this.getContainerWidth();
35381       // if columnWidth is 0, default to outerWidth of first item
35382         if ( !this.columnWidth ) {
35383             var firstItem = this.bricks.first();
35384             Roo.log(firstItem);
35385             this.columnWidth  = this.containerWidth;
35386             if (firstItem && firstItem.attr('originalwidth') ) {
35387                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35388             }
35389             // columnWidth fall back to item of first element
35390             Roo.log("set column width?");
35391                         this.initialColumnWidth = this.columnWidth  ;
35392
35393             // if first elem has no width, default to size of container
35394             
35395         }
35396         
35397         
35398         if (this.initialColumnWidth) {
35399             this.columnWidth = this.initialColumnWidth;
35400         }
35401         
35402         
35403             
35404         // column width is fixed at the top - however if container width get's smaller we should
35405         // reduce it...
35406         
35407         // this bit calcs how man columns..
35408             
35409         var columnWidth = this.columnWidth += this.gutter;
35410       
35411         // calculate columns
35412         var containerWidth = this.containerWidth + this.gutter;
35413         
35414         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35415         // fix rounding errors, typically with gutters
35416         var excess = columnWidth - containerWidth % columnWidth;
35417         
35418         
35419         // if overshoot is less than a pixel, round up, otherwise floor it
35420         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35421         cols = Math[ mathMethod ]( cols );
35422         this.cols = Math.max( cols, 1 );
35423         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35424         
35425          // padding positioning..
35426         var totalColWidth = this.cols * this.columnWidth;
35427         var padavail = this.containerWidth - totalColWidth;
35428         // so for 2 columns - we need 3 'pads'
35429         
35430         var padNeeded = (1+this.cols) * this.padWidth;
35431         
35432         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35433         
35434         this.columnWidth += padExtra
35435         //this.padWidth = Math.floor(padavail /  ( this.cols));
35436         
35437         // adjust colum width so that padding is fixed??
35438         
35439         // we have 3 columns ... total = width * 3
35440         // we have X left over... that should be used by 
35441         
35442         //if (this.expandC) {
35443             
35444         //}
35445         
35446         
35447         
35448     },
35449     
35450     getContainerWidth : function()
35451     {
35452        /* // container is parent if fit width
35453         var container = this.isFitWidth ? this.element.parentNode : this.element;
35454         // check that this.size and size are there
35455         // IE8 triggers resize on body size change, so they might not be
35456         
35457         var size = getSize( container );  //FIXME
35458         this.containerWidth = size && size.innerWidth; //FIXME
35459         */
35460          
35461         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35462         
35463     },
35464     
35465     _getItemLayoutPosition : function( item )  // what is item?
35466     {
35467         // we resize the item to our columnWidth..
35468       
35469         item.setWidth(this.columnWidth);
35470         item.autoBoxAdjust  = false;
35471         
35472         var sz = item.getSize();
35473  
35474         // how many columns does this brick span
35475         var remainder = this.containerWidth % this.columnWidth;
35476         
35477         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35478         // round if off by 1 pixel, otherwise use ceil
35479         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35480         colSpan = Math.min( colSpan, this.cols );
35481         
35482         // normally this should be '1' as we dont' currently allow multi width columns..
35483         
35484         var colGroup = this._getColGroup( colSpan );
35485         // get the minimum Y value from the columns
35486         var minimumY = Math.min.apply( Math, colGroup );
35487         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35488         
35489         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35490          
35491         // position the brick
35492         var position = {
35493             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35494             y: this.currentSize.y + minimumY + this.padHeight
35495         };
35496         
35497         Roo.log(position);
35498         // apply setHeight to necessary columns
35499         var setHeight = minimumY + sz.height + this.padHeight;
35500         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35501         
35502         var setSpan = this.cols + 1 - colGroup.length;
35503         for ( var i = 0; i < setSpan; i++ ) {
35504           this.colYs[ shortColIndex + i ] = setHeight ;
35505         }
35506       
35507         return position;
35508     },
35509     
35510     /**
35511      * @param {Number} colSpan - number of columns the element spans
35512      * @returns {Array} colGroup
35513      */
35514     _getColGroup : function( colSpan )
35515     {
35516         if ( colSpan < 2 ) {
35517           // if brick spans only one column, use all the column Ys
35518           return this.colYs;
35519         }
35520       
35521         var colGroup = [];
35522         // how many different places could this brick fit horizontally
35523         var groupCount = this.cols + 1 - colSpan;
35524         // for each group potential horizontal position
35525         for ( var i = 0; i < groupCount; i++ ) {
35526           // make an array of colY values for that one group
35527           var groupColYs = this.colYs.slice( i, i + colSpan );
35528           // and get the max value of the array
35529           colGroup[i] = Math.max.apply( Math, groupColYs );
35530         }
35531         return colGroup;
35532     },
35533     /*
35534     _manageStamp : function( stamp )
35535     {
35536         var stampSize =  stamp.getSize();
35537         var offset = stamp.getBox();
35538         // get the columns that this stamp affects
35539         var firstX = this.isOriginLeft ? offset.x : offset.right;
35540         var lastX = firstX + stampSize.width;
35541         var firstCol = Math.floor( firstX / this.columnWidth );
35542         firstCol = Math.max( 0, firstCol );
35543         
35544         var lastCol = Math.floor( lastX / this.columnWidth );
35545         // lastCol should not go over if multiple of columnWidth #425
35546         lastCol -= lastX % this.columnWidth ? 0 : 1;
35547         lastCol = Math.min( this.cols - 1, lastCol );
35548         
35549         // set colYs to bottom of the stamp
35550         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35551             stampSize.height;
35552             
35553         for ( var i = firstCol; i <= lastCol; i++ ) {
35554           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35555         }
35556     },
35557     */
35558     
35559     _getContainerSize : function()
35560     {
35561         this.maxY = Math.max.apply( Math, this.colYs );
35562         var size = {
35563             height: this.maxY
35564         };
35565       
35566         if ( this.isFitWidth ) {
35567             size.width = this._getContainerFitWidth();
35568         }
35569       
35570         return size;
35571     },
35572     
35573     _getContainerFitWidth : function()
35574     {
35575         var unusedCols = 0;
35576         // count unused columns
35577         var i = this.cols;
35578         while ( --i ) {
35579           if ( this.colYs[i] !== 0 ) {
35580             break;
35581           }
35582           unusedCols++;
35583         }
35584         // fit container to columns that have been used
35585         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35586     },
35587     
35588     needsResizeLayout : function()
35589     {
35590         var previousWidth = this.containerWidth;
35591         this.getContainerWidth();
35592         return previousWidth !== this.containerWidth;
35593     }
35594  
35595 });
35596
35597  
35598
35599  /*
35600  * - LGPL
35601  *
35602  * element
35603  * 
35604  */
35605
35606 /**
35607  * @class Roo.bootstrap.MasonryBrick
35608  * @extends Roo.bootstrap.Component
35609  * Bootstrap MasonryBrick class
35610  * 
35611  * @constructor
35612  * Create a new MasonryBrick
35613  * @param {Object} config The config object
35614  */
35615
35616 Roo.bootstrap.MasonryBrick = function(config){
35617     
35618     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35619     
35620     Roo.bootstrap.MasonryBrick.register(this);
35621     
35622     this.addEvents({
35623         // raw events
35624         /**
35625          * @event click
35626          * When a MasonryBrick is clcik
35627          * @param {Roo.bootstrap.MasonryBrick} this
35628          * @param {Roo.EventObject} e
35629          */
35630         "click" : true
35631     });
35632 };
35633
35634 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35635     
35636     /**
35637      * @cfg {String} title
35638      */   
35639     title : '',
35640     /**
35641      * @cfg {String} html
35642      */   
35643     html : '',
35644     /**
35645      * @cfg {String} bgimage
35646      */   
35647     bgimage : '',
35648     /**
35649      * @cfg {String} videourl
35650      */   
35651     videourl : '',
35652     /**
35653      * @cfg {String} cls
35654      */   
35655     cls : '',
35656     /**
35657      * @cfg {String} href
35658      */   
35659     href : '',
35660     /**
35661      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35662      */   
35663     size : 'xs',
35664     
35665     /**
35666      * @cfg {String} placetitle (center|bottom)
35667      */   
35668     placetitle : '',
35669     
35670     /**
35671      * @cfg {Boolean} isFitContainer defalut true
35672      */   
35673     isFitContainer : true, 
35674     
35675     /**
35676      * @cfg {Boolean} preventDefault defalut false
35677      */   
35678     preventDefault : false, 
35679     
35680     /**
35681      * @cfg {Boolean} inverse defalut false
35682      */   
35683     maskInverse : false, 
35684     
35685     getAutoCreate : function()
35686     {
35687         if(!this.isFitContainer){
35688             return this.getSplitAutoCreate();
35689         }
35690         
35691         var cls = 'masonry-brick masonry-brick-full';
35692         
35693         if(this.href.length){
35694             cls += ' masonry-brick-link';
35695         }
35696         
35697         if(this.bgimage.length){
35698             cls += ' masonry-brick-image';
35699         }
35700         
35701         if(this.maskInverse){
35702             cls += ' mask-inverse';
35703         }
35704         
35705         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35706             cls += ' enable-mask';
35707         }
35708         
35709         if(this.size){
35710             cls += ' masonry-' + this.size + '-brick';
35711         }
35712         
35713         if(this.placetitle.length){
35714             
35715             switch (this.placetitle) {
35716                 case 'center' :
35717                     cls += ' masonry-center-title';
35718                     break;
35719                 case 'bottom' :
35720                     cls += ' masonry-bottom-title';
35721                     break;
35722                 default:
35723                     break;
35724             }
35725             
35726         } else {
35727             if(!this.html.length && !this.bgimage.length){
35728                 cls += ' masonry-center-title';
35729             }
35730
35731             if(!this.html.length && this.bgimage.length){
35732                 cls += ' masonry-bottom-title';
35733             }
35734         }
35735         
35736         if(this.cls){
35737             cls += ' ' + this.cls;
35738         }
35739         
35740         var cfg = {
35741             tag: (this.href.length) ? 'a' : 'div',
35742             cls: cls,
35743             cn: [
35744                 {
35745                     tag: 'div',
35746                     cls: 'masonry-brick-mask'
35747                 },
35748                 {
35749                     tag: 'div',
35750                     cls: 'masonry-brick-paragraph',
35751                     cn: []
35752                 }
35753             ]
35754         };
35755         
35756         if(this.href.length){
35757             cfg.href = this.href;
35758         }
35759         
35760         var cn = cfg.cn[1].cn;
35761         
35762         if(this.title.length){
35763             cn.push({
35764                 tag: 'h4',
35765                 cls: 'masonry-brick-title',
35766                 html: this.title
35767             });
35768         }
35769         
35770         if(this.html.length){
35771             cn.push({
35772                 tag: 'p',
35773                 cls: 'masonry-brick-text',
35774                 html: this.html
35775             });
35776         }
35777         
35778         if (!this.title.length && !this.html.length) {
35779             cfg.cn[1].cls += ' hide';
35780         }
35781         
35782         if(this.bgimage.length){
35783             cfg.cn.push({
35784                 tag: 'img',
35785                 cls: 'masonry-brick-image-view',
35786                 src: this.bgimage
35787             });
35788         }
35789         
35790         if(this.videourl.length){
35791             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35792             // youtube support only?
35793             cfg.cn.push({
35794                 tag: 'iframe',
35795                 cls: 'masonry-brick-image-view',
35796                 src: vurl,
35797                 frameborder : 0,
35798                 allowfullscreen : true
35799             });
35800         }
35801         
35802         return cfg;
35803         
35804     },
35805     
35806     getSplitAutoCreate : function()
35807     {
35808         var cls = 'masonry-brick masonry-brick-split';
35809         
35810         if(this.href.length){
35811             cls += ' masonry-brick-link';
35812         }
35813         
35814         if(this.bgimage.length){
35815             cls += ' masonry-brick-image';
35816         }
35817         
35818         if(this.size){
35819             cls += ' masonry-' + this.size + '-brick';
35820         }
35821         
35822         switch (this.placetitle) {
35823             case 'center' :
35824                 cls += ' masonry-center-title';
35825                 break;
35826             case 'bottom' :
35827                 cls += ' masonry-bottom-title';
35828                 break;
35829             default:
35830                 if(!this.bgimage.length){
35831                     cls += ' masonry-center-title';
35832                 }
35833
35834                 if(this.bgimage.length){
35835                     cls += ' masonry-bottom-title';
35836                 }
35837                 break;
35838         }
35839         
35840         if(this.cls){
35841             cls += ' ' + this.cls;
35842         }
35843         
35844         var cfg = {
35845             tag: (this.href.length) ? 'a' : 'div',
35846             cls: cls,
35847             cn: [
35848                 {
35849                     tag: 'div',
35850                     cls: 'masonry-brick-split-head',
35851                     cn: [
35852                         {
35853                             tag: 'div',
35854                             cls: 'masonry-brick-paragraph',
35855                             cn: []
35856                         }
35857                     ]
35858                 },
35859                 {
35860                     tag: 'div',
35861                     cls: 'masonry-brick-split-body',
35862                     cn: []
35863                 }
35864             ]
35865         };
35866         
35867         if(this.href.length){
35868             cfg.href = this.href;
35869         }
35870         
35871         if(this.title.length){
35872             cfg.cn[0].cn[0].cn.push({
35873                 tag: 'h4',
35874                 cls: 'masonry-brick-title',
35875                 html: this.title
35876             });
35877         }
35878         
35879         if(this.html.length){
35880             cfg.cn[1].cn.push({
35881                 tag: 'p',
35882                 cls: 'masonry-brick-text',
35883                 html: this.html
35884             });
35885         }
35886
35887         if(this.bgimage.length){
35888             cfg.cn[0].cn.push({
35889                 tag: 'img',
35890                 cls: 'masonry-brick-image-view',
35891                 src: this.bgimage
35892             });
35893         }
35894         
35895         if(this.videourl.length){
35896             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35897             // youtube support only?
35898             cfg.cn[0].cn.cn.push({
35899                 tag: 'iframe',
35900                 cls: 'masonry-brick-image-view',
35901                 src: vurl,
35902                 frameborder : 0,
35903                 allowfullscreen : true
35904             });
35905         }
35906         
35907         return cfg;
35908     },
35909     
35910     initEvents: function() 
35911     {
35912         switch (this.size) {
35913             case 'xs' :
35914                 this.x = 1;
35915                 this.y = 1;
35916                 break;
35917             case 'sm' :
35918                 this.x = 2;
35919                 this.y = 2;
35920                 break;
35921             case 'md' :
35922             case 'md-left' :
35923             case 'md-right' :
35924                 this.x = 3;
35925                 this.y = 3;
35926                 break;
35927             case 'tall' :
35928                 this.x = 2;
35929                 this.y = 3;
35930                 break;
35931             case 'wide' :
35932                 this.x = 3;
35933                 this.y = 2;
35934                 break;
35935             case 'wide-thin' :
35936                 this.x = 3;
35937                 this.y = 1;
35938                 break;
35939                         
35940             default :
35941                 break;
35942         }
35943         
35944         if(Roo.isTouch){
35945             this.el.on('touchstart', this.onTouchStart, this);
35946             this.el.on('touchmove', this.onTouchMove, this);
35947             this.el.on('touchend', this.onTouchEnd, this);
35948             this.el.on('contextmenu', this.onContextMenu, this);
35949         } else {
35950             this.el.on('mouseenter'  ,this.enter, this);
35951             this.el.on('mouseleave', this.leave, this);
35952             this.el.on('click', this.onClick, this);
35953         }
35954         
35955         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35956             this.parent().bricks.push(this);   
35957         }
35958         
35959     },
35960     
35961     onClick: function(e, el)
35962     {
35963         var time = this.endTimer - this.startTimer;
35964         // Roo.log(e.preventDefault());
35965         if(Roo.isTouch){
35966             if(time > 1000){
35967                 e.preventDefault();
35968                 return;
35969             }
35970         }
35971         
35972         if(!this.preventDefault){
35973             return;
35974         }
35975         
35976         e.preventDefault();
35977         
35978         if (this.activeClass != '') {
35979             this.selectBrick();
35980         }
35981         
35982         this.fireEvent('click', this, e);
35983     },
35984     
35985     enter: function(e, el)
35986     {
35987         e.preventDefault();
35988         
35989         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35990             return;
35991         }
35992         
35993         if(this.bgimage.length && this.html.length){
35994             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35995         }
35996     },
35997     
35998     leave: function(e, el)
35999     {
36000         e.preventDefault();
36001         
36002         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36003             return;
36004         }
36005         
36006         if(this.bgimage.length && this.html.length){
36007             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36008         }
36009     },
36010     
36011     onTouchStart: function(e, el)
36012     {
36013 //        e.preventDefault();
36014         
36015         this.touchmoved = false;
36016         
36017         if(!this.isFitContainer){
36018             return;
36019         }
36020         
36021         if(!this.bgimage.length || !this.html.length){
36022             return;
36023         }
36024         
36025         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36026         
36027         this.timer = new Date().getTime();
36028         
36029     },
36030     
36031     onTouchMove: function(e, el)
36032     {
36033         this.touchmoved = true;
36034     },
36035     
36036     onContextMenu : function(e,el)
36037     {
36038         e.preventDefault();
36039         e.stopPropagation();
36040         return false;
36041     },
36042     
36043     onTouchEnd: function(e, el)
36044     {
36045 //        e.preventDefault();
36046         
36047         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36048         
36049             this.leave(e,el);
36050             
36051             return;
36052         }
36053         
36054         if(!this.bgimage.length || !this.html.length){
36055             
36056             if(this.href.length){
36057                 window.location.href = this.href;
36058             }
36059             
36060             return;
36061         }
36062         
36063         if(!this.isFitContainer){
36064             return;
36065         }
36066         
36067         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36068         
36069         window.location.href = this.href;
36070     },
36071     
36072     //selection on single brick only
36073     selectBrick : function() {
36074         
36075         if (!this.parentId) {
36076             return;
36077         }
36078         
36079         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36080         var index = m.selectedBrick.indexOf(this.id);
36081         
36082         if ( index > -1) {
36083             m.selectedBrick.splice(index,1);
36084             this.el.removeClass(this.activeClass);
36085             return;
36086         }
36087         
36088         for(var i = 0; i < m.selectedBrick.length; i++) {
36089             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36090             b.el.removeClass(b.activeClass);
36091         }
36092         
36093         m.selectedBrick = [];
36094         
36095         m.selectedBrick.push(this.id);
36096         this.el.addClass(this.activeClass);
36097         return;
36098     },
36099     
36100     isSelected : function(){
36101         return this.el.hasClass(this.activeClass);
36102         
36103     }
36104 });
36105
36106 Roo.apply(Roo.bootstrap.MasonryBrick, {
36107     
36108     //groups: {},
36109     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36110      /**
36111     * register a Masonry Brick
36112     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36113     */
36114     
36115     register : function(brick)
36116     {
36117         //this.groups[brick.id] = brick;
36118         this.groups.add(brick.id, brick);
36119     },
36120     /**
36121     * fetch a  masonry brick based on the masonry brick ID
36122     * @param {string} the masonry brick to add
36123     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36124     */
36125     
36126     get: function(brick_id) 
36127     {
36128         // if (typeof(this.groups[brick_id]) == 'undefined') {
36129         //     return false;
36130         // }
36131         // return this.groups[brick_id] ;
36132         
36133         if(this.groups.key(brick_id)) {
36134             return this.groups.key(brick_id);
36135         }
36136         
36137         return false;
36138     }
36139     
36140     
36141     
36142 });
36143
36144  /*
36145  * - LGPL
36146  *
36147  * element
36148  * 
36149  */
36150
36151 /**
36152  * @class Roo.bootstrap.Brick
36153  * @extends Roo.bootstrap.Component
36154  * Bootstrap Brick class
36155  * 
36156  * @constructor
36157  * Create a new Brick
36158  * @param {Object} config The config object
36159  */
36160
36161 Roo.bootstrap.Brick = function(config){
36162     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36163     
36164     this.addEvents({
36165         // raw events
36166         /**
36167          * @event click
36168          * When a Brick is click
36169          * @param {Roo.bootstrap.Brick} this
36170          * @param {Roo.EventObject} e
36171          */
36172         "click" : true
36173     });
36174 };
36175
36176 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36177     
36178     /**
36179      * @cfg {String} title
36180      */   
36181     title : '',
36182     /**
36183      * @cfg {String} html
36184      */   
36185     html : '',
36186     /**
36187      * @cfg {String} bgimage
36188      */   
36189     bgimage : '',
36190     /**
36191      * @cfg {String} cls
36192      */   
36193     cls : '',
36194     /**
36195      * @cfg {String} href
36196      */   
36197     href : '',
36198     /**
36199      * @cfg {String} video
36200      */   
36201     video : '',
36202     /**
36203      * @cfg {Boolean} square
36204      */   
36205     square : true,
36206     
36207     getAutoCreate : function()
36208     {
36209         var cls = 'roo-brick';
36210         
36211         if(this.href.length){
36212             cls += ' roo-brick-link';
36213         }
36214         
36215         if(this.bgimage.length){
36216             cls += ' roo-brick-image';
36217         }
36218         
36219         if(!this.html.length && !this.bgimage.length){
36220             cls += ' roo-brick-center-title';
36221         }
36222         
36223         if(!this.html.length && this.bgimage.length){
36224             cls += ' roo-brick-bottom-title';
36225         }
36226         
36227         if(this.cls){
36228             cls += ' ' + this.cls;
36229         }
36230         
36231         var cfg = {
36232             tag: (this.href.length) ? 'a' : 'div',
36233             cls: cls,
36234             cn: [
36235                 {
36236                     tag: 'div',
36237                     cls: 'roo-brick-paragraph',
36238                     cn: []
36239                 }
36240             ]
36241         };
36242         
36243         if(this.href.length){
36244             cfg.href = this.href;
36245         }
36246         
36247         var cn = cfg.cn[0].cn;
36248         
36249         if(this.title.length){
36250             cn.push({
36251                 tag: 'h4',
36252                 cls: 'roo-brick-title',
36253                 html: this.title
36254             });
36255         }
36256         
36257         if(this.html.length){
36258             cn.push({
36259                 tag: 'p',
36260                 cls: 'roo-brick-text',
36261                 html: this.html
36262             });
36263         } else {
36264             cn.cls += ' hide';
36265         }
36266         
36267         if(this.bgimage.length){
36268             cfg.cn.push({
36269                 tag: 'img',
36270                 cls: 'roo-brick-image-view',
36271                 src: this.bgimage
36272             });
36273         }
36274         
36275         return cfg;
36276     },
36277     
36278     initEvents: function() 
36279     {
36280         if(this.title.length || this.html.length){
36281             this.el.on('mouseenter'  ,this.enter, this);
36282             this.el.on('mouseleave', this.leave, this);
36283         }
36284         
36285         Roo.EventManager.onWindowResize(this.resize, this); 
36286         
36287         if(this.bgimage.length){
36288             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36289             this.imageEl.on('load', this.onImageLoad, this);
36290             return;
36291         }
36292         
36293         this.resize();
36294     },
36295     
36296     onImageLoad : function()
36297     {
36298         this.resize();
36299     },
36300     
36301     resize : function()
36302     {
36303         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36304         
36305         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36306         
36307         if(this.bgimage.length){
36308             var image = this.el.select('.roo-brick-image-view', true).first();
36309             
36310             image.setWidth(paragraph.getWidth());
36311             
36312             if(this.square){
36313                 image.setHeight(paragraph.getWidth());
36314             }
36315             
36316             this.el.setHeight(image.getHeight());
36317             paragraph.setHeight(image.getHeight());
36318             
36319         }
36320         
36321     },
36322     
36323     enter: function(e, el)
36324     {
36325         e.preventDefault();
36326         
36327         if(this.bgimage.length){
36328             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36329             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36330         }
36331     },
36332     
36333     leave: function(e, el)
36334     {
36335         e.preventDefault();
36336         
36337         if(this.bgimage.length){
36338             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36339             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36340         }
36341     }
36342     
36343 });
36344
36345  
36346
36347  /*
36348  * - LGPL
36349  *
36350  * Number field 
36351  */
36352
36353 /**
36354  * @class Roo.bootstrap.NumberField
36355  * @extends Roo.bootstrap.Input
36356  * Bootstrap NumberField class
36357  * 
36358  * 
36359  * 
36360  * 
36361  * @constructor
36362  * Create a new NumberField
36363  * @param {Object} config The config object
36364  */
36365
36366 Roo.bootstrap.NumberField = function(config){
36367     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36368 };
36369
36370 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36371     
36372     /**
36373      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36374      */
36375     allowDecimals : true,
36376     /**
36377      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36378      */
36379     decimalSeparator : ".",
36380     /**
36381      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36382      */
36383     decimalPrecision : 2,
36384     /**
36385      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36386      */
36387     allowNegative : true,
36388     
36389     /**
36390      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36391      */
36392     allowZero: true,
36393     /**
36394      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36395      */
36396     minValue : Number.NEGATIVE_INFINITY,
36397     /**
36398      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36399      */
36400     maxValue : Number.MAX_VALUE,
36401     /**
36402      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36403      */
36404     minText : "The minimum value for this field is {0}",
36405     /**
36406      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36407      */
36408     maxText : "The maximum value for this field is {0}",
36409     /**
36410      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36411      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36412      */
36413     nanText : "{0} is not a valid number",
36414     /**
36415      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36416      */
36417     thousandsDelimiter : false,
36418     /**
36419      * @cfg {String} valueAlign alignment of value
36420      */
36421     valueAlign : "left",
36422
36423     getAutoCreate : function()
36424     {
36425         var hiddenInput = {
36426             tag: 'input',
36427             type: 'hidden',
36428             id: Roo.id(),
36429             cls: 'hidden-number-input'
36430         };
36431         
36432         if (this.name) {
36433             hiddenInput.name = this.name;
36434         }
36435         
36436         this.name = '';
36437         
36438         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36439         
36440         this.name = hiddenInput.name;
36441         
36442         if(cfg.cn.length > 0) {
36443             cfg.cn.push(hiddenInput);
36444         }
36445         
36446         return cfg;
36447     },
36448
36449     // private
36450     initEvents : function()
36451     {   
36452         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36453         
36454         var allowed = "0123456789";
36455         
36456         if(this.allowDecimals){
36457             allowed += this.decimalSeparator;
36458         }
36459         
36460         if(this.allowNegative){
36461             allowed += "-";
36462         }
36463         
36464         if(this.thousandsDelimiter) {
36465             allowed += ",";
36466         }
36467         
36468         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36469         
36470         var keyPress = function(e){
36471             
36472             var k = e.getKey();
36473             
36474             var c = e.getCharCode();
36475             
36476             if(
36477                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36478                     allowed.indexOf(String.fromCharCode(c)) === -1
36479             ){
36480                 e.stopEvent();
36481                 return;
36482             }
36483             
36484             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36485                 return;
36486             }
36487             
36488             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36489                 e.stopEvent();
36490             }
36491         };
36492         
36493         this.el.on("keypress", keyPress, this);
36494     },
36495     
36496     validateValue : function(value)
36497     {
36498         
36499         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36500             return false;
36501         }
36502         
36503         var num = this.parseValue(value);
36504         
36505         if(isNaN(num)){
36506             this.markInvalid(String.format(this.nanText, value));
36507             return false;
36508         }
36509         
36510         if(num < this.minValue){
36511             this.markInvalid(String.format(this.minText, this.minValue));
36512             return false;
36513         }
36514         
36515         if(num > this.maxValue){
36516             this.markInvalid(String.format(this.maxText, this.maxValue));
36517             return false;
36518         }
36519         
36520         return true;
36521     },
36522
36523     getValue : function()
36524     {
36525         var v = this.hiddenEl().getValue();
36526         
36527         return this.fixPrecision(this.parseValue(v));
36528     },
36529
36530     parseValue : function(value)
36531     {
36532         if(this.thousandsDelimiter) {
36533             value += "";
36534             r = new RegExp(",", "g");
36535             value = value.replace(r, "");
36536         }
36537         
36538         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36539         return isNaN(value) ? '' : value;
36540     },
36541
36542     fixPrecision : function(value)
36543     {
36544         if(this.thousandsDelimiter) {
36545             value += "";
36546             r = new RegExp(",", "g");
36547             value = value.replace(r, "");
36548         }
36549         
36550         var nan = isNaN(value);
36551         
36552         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36553             return nan ? '' : value;
36554         }
36555         return parseFloat(value).toFixed(this.decimalPrecision);
36556     },
36557
36558     setValue : function(v)
36559     {
36560         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36561         
36562         this.value = v;
36563         
36564         if(this.rendered){
36565             
36566             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36567             
36568             this.inputEl().dom.value = (v == '') ? '' :
36569                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36570             
36571             if(!this.allowZero && v === '0') {
36572                 this.hiddenEl().dom.value = '';
36573                 this.inputEl().dom.value = '';
36574             }
36575             
36576             this.validate();
36577         }
36578     },
36579
36580     decimalPrecisionFcn : function(v)
36581     {
36582         return Math.floor(v);
36583     },
36584
36585     beforeBlur : function()
36586     {
36587         var v = this.parseValue(this.getRawValue());
36588         
36589         if(v || v === 0 || v === ''){
36590             this.setValue(v);
36591         }
36592     },
36593     
36594     hiddenEl : function()
36595     {
36596         return this.el.select('input.hidden-number-input',true).first();
36597     }
36598     
36599 });
36600
36601  
36602
36603 /*
36604 * Licence: LGPL
36605 */
36606
36607 /**
36608  * @class Roo.bootstrap.DocumentSlider
36609  * @extends Roo.bootstrap.Component
36610  * Bootstrap DocumentSlider class
36611  * 
36612  * @constructor
36613  * Create a new DocumentViewer
36614  * @param {Object} config The config object
36615  */
36616
36617 Roo.bootstrap.DocumentSlider = function(config){
36618     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36619     
36620     this.files = [];
36621     
36622     this.addEvents({
36623         /**
36624          * @event initial
36625          * Fire after initEvent
36626          * @param {Roo.bootstrap.DocumentSlider} this
36627          */
36628         "initial" : true,
36629         /**
36630          * @event update
36631          * Fire after update
36632          * @param {Roo.bootstrap.DocumentSlider} this
36633          */
36634         "update" : true,
36635         /**
36636          * @event click
36637          * Fire after click
36638          * @param {Roo.bootstrap.DocumentSlider} this
36639          */
36640         "click" : true
36641     });
36642 };
36643
36644 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36645     
36646     files : false,
36647     
36648     indicator : 0,
36649     
36650     getAutoCreate : function()
36651     {
36652         var cfg = {
36653             tag : 'div',
36654             cls : 'roo-document-slider',
36655             cn : [
36656                 {
36657                     tag : 'div',
36658                     cls : 'roo-document-slider-header',
36659                     cn : [
36660                         {
36661                             tag : 'div',
36662                             cls : 'roo-document-slider-header-title'
36663                         }
36664                     ]
36665                 },
36666                 {
36667                     tag : 'div',
36668                     cls : 'roo-document-slider-body',
36669                     cn : [
36670                         {
36671                             tag : 'div',
36672                             cls : 'roo-document-slider-prev',
36673                             cn : [
36674                                 {
36675                                     tag : 'i',
36676                                     cls : 'fa fa-chevron-left'
36677                                 }
36678                             ]
36679                         },
36680                         {
36681                             tag : 'div',
36682                             cls : 'roo-document-slider-thumb',
36683                             cn : [
36684                                 {
36685                                     tag : 'img',
36686                                     cls : 'roo-document-slider-image'
36687                                 }
36688                             ]
36689                         },
36690                         {
36691                             tag : 'div',
36692                             cls : 'roo-document-slider-next',
36693                             cn : [
36694                                 {
36695                                     tag : 'i',
36696                                     cls : 'fa fa-chevron-right'
36697                                 }
36698                             ]
36699                         }
36700                     ]
36701                 }
36702             ]
36703         };
36704         
36705         return cfg;
36706     },
36707     
36708     initEvents : function()
36709     {
36710         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36711         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36712         
36713         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36714         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36715         
36716         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36717         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36718         
36719         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36720         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36721         
36722         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36723         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36724         
36725         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36726         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36727         
36728         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36729         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36730         
36731         this.thumbEl.on('click', this.onClick, this);
36732         
36733         this.prevIndicator.on('click', this.prev, this);
36734         
36735         this.nextIndicator.on('click', this.next, this);
36736         
36737     },
36738     
36739     initial : function()
36740     {
36741         if(this.files.length){
36742             this.indicator = 1;
36743             this.update()
36744         }
36745         
36746         this.fireEvent('initial', this);
36747     },
36748     
36749     update : function()
36750     {
36751         this.imageEl.attr('src', this.files[this.indicator - 1]);
36752         
36753         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36754         
36755         this.prevIndicator.show();
36756         
36757         if(this.indicator == 1){
36758             this.prevIndicator.hide();
36759         }
36760         
36761         this.nextIndicator.show();
36762         
36763         if(this.indicator == this.files.length){
36764             this.nextIndicator.hide();
36765         }
36766         
36767         this.thumbEl.scrollTo('top');
36768         
36769         this.fireEvent('update', this);
36770     },
36771     
36772     onClick : function(e)
36773     {
36774         e.preventDefault();
36775         
36776         this.fireEvent('click', this);
36777     },
36778     
36779     prev : function(e)
36780     {
36781         e.preventDefault();
36782         
36783         this.indicator = Math.max(1, this.indicator - 1);
36784         
36785         this.update();
36786     },
36787     
36788     next : function(e)
36789     {
36790         e.preventDefault();
36791         
36792         this.indicator = Math.min(this.files.length, this.indicator + 1);
36793         
36794         this.update();
36795     }
36796 });
36797 /*
36798  * - LGPL
36799  *
36800  * RadioSet
36801  *
36802  *
36803  */
36804
36805 /**
36806  * @class Roo.bootstrap.RadioSet
36807  * @extends Roo.bootstrap.Input
36808  * Bootstrap RadioSet class
36809  * @cfg {String} indicatorpos (left|right) default left
36810  * @cfg {Boolean} inline (true|false) inline the element (default true)
36811  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36812  * @constructor
36813  * Create a new RadioSet
36814  * @param {Object} config The config object
36815  */
36816
36817 Roo.bootstrap.RadioSet = function(config){
36818     
36819     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36820     
36821     this.radioes = [];
36822     
36823     Roo.bootstrap.RadioSet.register(this);
36824     
36825     this.addEvents({
36826         /**
36827         * @event check
36828         * Fires when the element is checked or unchecked.
36829         * @param {Roo.bootstrap.RadioSet} this This radio
36830         * @param {Roo.bootstrap.Radio} item The checked item
36831         */
36832        check : true,
36833        /**
36834         * @event click
36835         * Fires when the element is click.
36836         * @param {Roo.bootstrap.RadioSet} this This radio set
36837         * @param {Roo.bootstrap.Radio} item The checked item
36838         * @param {Roo.EventObject} e The event object
36839         */
36840        click : true
36841     });
36842     
36843 };
36844
36845 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36846
36847     radioes : false,
36848     
36849     inline : true,
36850     
36851     weight : '',
36852     
36853     indicatorpos : 'left',
36854     
36855     getAutoCreate : function()
36856     {
36857         var label = {
36858             tag : 'label',
36859             cls : 'roo-radio-set-label',
36860             cn : [
36861                 {
36862                     tag : 'span',
36863                     html : this.fieldLabel
36864                 }
36865             ]
36866         };
36867         if (Roo.bootstrap.version == 3) {
36868             
36869             
36870             if(this.indicatorpos == 'left'){
36871                 label.cn.unshift({
36872                     tag : 'i',
36873                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36874                     tooltip : 'This field is required'
36875                 });
36876             } else {
36877                 label.cn.push({
36878                     tag : 'i',
36879                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36880                     tooltip : 'This field is required'
36881                 });
36882             }
36883         }
36884         var items = {
36885             tag : 'div',
36886             cls : 'roo-radio-set-items'
36887         };
36888         
36889         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36890         
36891         if (align === 'left' && this.fieldLabel.length) {
36892             
36893             items = {
36894                 cls : "roo-radio-set-right", 
36895                 cn: [
36896                     items
36897                 ]
36898             };
36899             
36900             if(this.labelWidth > 12){
36901                 label.style = "width: " + this.labelWidth + 'px';
36902             }
36903             
36904             if(this.labelWidth < 13 && this.labelmd == 0){
36905                 this.labelmd = this.labelWidth;
36906             }
36907             
36908             if(this.labellg > 0){
36909                 label.cls += ' col-lg-' + this.labellg;
36910                 items.cls += ' col-lg-' + (12 - this.labellg);
36911             }
36912             
36913             if(this.labelmd > 0){
36914                 label.cls += ' col-md-' + this.labelmd;
36915                 items.cls += ' col-md-' + (12 - this.labelmd);
36916             }
36917             
36918             if(this.labelsm > 0){
36919                 label.cls += ' col-sm-' + this.labelsm;
36920                 items.cls += ' col-sm-' + (12 - this.labelsm);
36921             }
36922             
36923             if(this.labelxs > 0){
36924                 label.cls += ' col-xs-' + this.labelxs;
36925                 items.cls += ' col-xs-' + (12 - this.labelxs);
36926             }
36927         }
36928         
36929         var cfg = {
36930             tag : 'div',
36931             cls : 'roo-radio-set',
36932             cn : [
36933                 {
36934                     tag : 'input',
36935                     cls : 'roo-radio-set-input',
36936                     type : 'hidden',
36937                     name : this.name,
36938                     value : this.value ? this.value :  ''
36939                 },
36940                 label,
36941                 items
36942             ]
36943         };
36944         
36945         if(this.weight.length){
36946             cfg.cls += ' roo-radio-' + this.weight;
36947         }
36948         
36949         if(this.inline) {
36950             cfg.cls += ' roo-radio-set-inline';
36951         }
36952         
36953         var settings=this;
36954         ['xs','sm','md','lg'].map(function(size){
36955             if (settings[size]) {
36956                 cfg.cls += ' col-' + size + '-' + settings[size];
36957             }
36958         });
36959         
36960         return cfg;
36961         
36962     },
36963
36964     initEvents : function()
36965     {
36966         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36967         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36968         
36969         if(!this.fieldLabel.length){
36970             this.labelEl.hide();
36971         }
36972         
36973         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36974         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36975         
36976         this.indicator = this.indicatorEl();
36977         
36978         if(this.indicator){
36979             this.indicator.addClass('invisible');
36980         }
36981         
36982         this.originalValue = this.getValue();
36983         
36984     },
36985     
36986     inputEl: function ()
36987     {
36988         return this.el.select('.roo-radio-set-input', true).first();
36989     },
36990     
36991     getChildContainer : function()
36992     {
36993         return this.itemsEl;
36994     },
36995     
36996     register : function(item)
36997     {
36998         this.radioes.push(item);
36999         
37000     },
37001     
37002     validate : function()
37003     {   
37004         if(this.getVisibilityEl().hasClass('hidden')){
37005             return true;
37006         }
37007         
37008         var valid = false;
37009         
37010         Roo.each(this.radioes, function(i){
37011             if(!i.checked){
37012                 return;
37013             }
37014             
37015             valid = true;
37016             return false;
37017         });
37018         
37019         if(this.allowBlank) {
37020             return true;
37021         }
37022         
37023         if(this.disabled || valid){
37024             this.markValid();
37025             return true;
37026         }
37027         
37028         this.markInvalid();
37029         return false;
37030         
37031     },
37032     
37033     markValid : function()
37034     {
37035         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37036             this.indicatorEl().removeClass('visible');
37037             this.indicatorEl().addClass('invisible');
37038         }
37039         
37040         
37041         if (Roo.bootstrap.version == 3) {
37042             this.el.removeClass([this.invalidClass, this.validClass]);
37043             this.el.addClass(this.validClass);
37044         } else {
37045             this.el.removeClass(['is-invalid','is-valid']);
37046             this.el.addClass(['is-valid']);
37047         }
37048         this.fireEvent('valid', this);
37049     },
37050     
37051     markInvalid : function(msg)
37052     {
37053         if(this.allowBlank || this.disabled){
37054             return;
37055         }
37056         
37057         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37058             this.indicatorEl().removeClass('invisible');
37059             this.indicatorEl().addClass('visible');
37060         }
37061         if (Roo.bootstrap.version == 3) {
37062             this.el.removeClass([this.invalidClass, this.validClass]);
37063             this.el.addClass(this.invalidClass);
37064         } else {
37065             this.el.removeClass(['is-invalid','is-valid']);
37066             this.el.addClass(['is-invalid']);
37067         }
37068         
37069         this.fireEvent('invalid', this, msg);
37070         
37071     },
37072     
37073     setValue : function(v, suppressEvent)
37074     {   
37075         if(this.value === v){
37076             return;
37077         }
37078         
37079         this.value = v;
37080         
37081         if(this.rendered){
37082             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37083         }
37084         
37085         Roo.each(this.radioes, function(i){
37086             i.checked = false;
37087             i.el.removeClass('checked');
37088         });
37089         
37090         Roo.each(this.radioes, function(i){
37091             
37092             if(i.value === v || i.value.toString() === v.toString()){
37093                 i.checked = true;
37094                 i.el.addClass('checked');
37095                 
37096                 if(suppressEvent !== true){
37097                     this.fireEvent('check', this, i);
37098                 }
37099                 
37100                 return false;
37101             }
37102             
37103         }, this);
37104         
37105         this.validate();
37106     },
37107     
37108     clearInvalid : function(){
37109         
37110         if(!this.el || this.preventMark){
37111             return;
37112         }
37113         
37114         this.el.removeClass([this.invalidClass]);
37115         
37116         this.fireEvent('valid', this);
37117     }
37118     
37119 });
37120
37121 Roo.apply(Roo.bootstrap.RadioSet, {
37122     
37123     groups: {},
37124     
37125     register : function(set)
37126     {
37127         this.groups[set.name] = set;
37128     },
37129     
37130     get: function(name) 
37131     {
37132         if (typeof(this.groups[name]) == 'undefined') {
37133             return false;
37134         }
37135         
37136         return this.groups[name] ;
37137     }
37138     
37139 });
37140 /*
37141  * Based on:
37142  * Ext JS Library 1.1.1
37143  * Copyright(c) 2006-2007, Ext JS, LLC.
37144  *
37145  * Originally Released Under LGPL - original licence link has changed is not relivant.
37146  *
37147  * Fork - LGPL
37148  * <script type="text/javascript">
37149  */
37150
37151
37152 /**
37153  * @class Roo.bootstrap.SplitBar
37154  * @extends Roo.util.Observable
37155  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37156  * <br><br>
37157  * Usage:
37158  * <pre><code>
37159 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37160                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37161 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37162 split.minSize = 100;
37163 split.maxSize = 600;
37164 split.animate = true;
37165 split.on('moved', splitterMoved);
37166 </code></pre>
37167  * @constructor
37168  * Create a new SplitBar
37169  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37170  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37171  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37172  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37173                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37174                         position of the SplitBar).
37175  */
37176 Roo.bootstrap.SplitBar = function(cfg){
37177     
37178     /** @private */
37179     
37180     //{
37181     //  dragElement : elm
37182     //  resizingElement: el,
37183         // optional..
37184     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37185     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37186         // existingProxy ???
37187     //}
37188     
37189     this.el = Roo.get(cfg.dragElement, true);
37190     this.el.dom.unselectable = "on";
37191     /** @private */
37192     this.resizingEl = Roo.get(cfg.resizingElement, true);
37193
37194     /**
37195      * @private
37196      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37197      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37198      * @type Number
37199      */
37200     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37201     
37202     /**
37203      * The minimum size of the resizing element. (Defaults to 0)
37204      * @type Number
37205      */
37206     this.minSize = 0;
37207     
37208     /**
37209      * The maximum size of the resizing element. (Defaults to 2000)
37210      * @type Number
37211      */
37212     this.maxSize = 2000;
37213     
37214     /**
37215      * Whether to animate the transition to the new size
37216      * @type Boolean
37217      */
37218     this.animate = false;
37219     
37220     /**
37221      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37222      * @type Boolean
37223      */
37224     this.useShim = false;
37225     
37226     /** @private */
37227     this.shim = null;
37228     
37229     if(!cfg.existingProxy){
37230         /** @private */
37231         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37232     }else{
37233         this.proxy = Roo.get(cfg.existingProxy).dom;
37234     }
37235     /** @private */
37236     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37237     
37238     /** @private */
37239     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37240     
37241     /** @private */
37242     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37243     
37244     /** @private */
37245     this.dragSpecs = {};
37246     
37247     /**
37248      * @private The adapter to use to positon and resize elements
37249      */
37250     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37251     this.adapter.init(this);
37252     
37253     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37254         /** @private */
37255         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37256         this.el.addClass("roo-splitbar-h");
37257     }else{
37258         /** @private */
37259         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37260         this.el.addClass("roo-splitbar-v");
37261     }
37262     
37263     this.addEvents({
37264         /**
37265          * @event resize
37266          * Fires when the splitter is moved (alias for {@link #event-moved})
37267          * @param {Roo.bootstrap.SplitBar} this
37268          * @param {Number} newSize the new width or height
37269          */
37270         "resize" : true,
37271         /**
37272          * @event moved
37273          * Fires when the splitter is moved
37274          * @param {Roo.bootstrap.SplitBar} this
37275          * @param {Number} newSize the new width or height
37276          */
37277         "moved" : true,
37278         /**
37279          * @event beforeresize
37280          * Fires before the splitter is dragged
37281          * @param {Roo.bootstrap.SplitBar} this
37282          */
37283         "beforeresize" : true,
37284
37285         "beforeapply" : true
37286     });
37287
37288     Roo.util.Observable.call(this);
37289 };
37290
37291 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37292     onStartProxyDrag : function(x, y){
37293         this.fireEvent("beforeresize", this);
37294         if(!this.overlay){
37295             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37296             o.unselectable();
37297             o.enableDisplayMode("block");
37298             // all splitbars share the same overlay
37299             Roo.bootstrap.SplitBar.prototype.overlay = o;
37300         }
37301         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37302         this.overlay.show();
37303         Roo.get(this.proxy).setDisplayed("block");
37304         var size = this.adapter.getElementSize(this);
37305         this.activeMinSize = this.getMinimumSize();;
37306         this.activeMaxSize = this.getMaximumSize();;
37307         var c1 = size - this.activeMinSize;
37308         var c2 = Math.max(this.activeMaxSize - size, 0);
37309         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37310             this.dd.resetConstraints();
37311             this.dd.setXConstraint(
37312                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37313                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37314             );
37315             this.dd.setYConstraint(0, 0);
37316         }else{
37317             this.dd.resetConstraints();
37318             this.dd.setXConstraint(0, 0);
37319             this.dd.setYConstraint(
37320                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37321                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37322             );
37323          }
37324         this.dragSpecs.startSize = size;
37325         this.dragSpecs.startPoint = [x, y];
37326         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37327     },
37328     
37329     /** 
37330      * @private Called after the drag operation by the DDProxy
37331      */
37332     onEndProxyDrag : function(e){
37333         Roo.get(this.proxy).setDisplayed(false);
37334         var endPoint = Roo.lib.Event.getXY(e);
37335         if(this.overlay){
37336             this.overlay.hide();
37337         }
37338         var newSize;
37339         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37340             newSize = this.dragSpecs.startSize + 
37341                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37342                     endPoint[0] - this.dragSpecs.startPoint[0] :
37343                     this.dragSpecs.startPoint[0] - endPoint[0]
37344                 );
37345         }else{
37346             newSize = this.dragSpecs.startSize + 
37347                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37348                     endPoint[1] - this.dragSpecs.startPoint[1] :
37349                     this.dragSpecs.startPoint[1] - endPoint[1]
37350                 );
37351         }
37352         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37353         if(newSize != this.dragSpecs.startSize){
37354             if(this.fireEvent('beforeapply', this, newSize) !== false){
37355                 this.adapter.setElementSize(this, newSize);
37356                 this.fireEvent("moved", this, newSize);
37357                 this.fireEvent("resize", this, newSize);
37358             }
37359         }
37360     },
37361     
37362     /**
37363      * Get the adapter this SplitBar uses
37364      * @return The adapter object
37365      */
37366     getAdapter : function(){
37367         return this.adapter;
37368     },
37369     
37370     /**
37371      * Set the adapter this SplitBar uses
37372      * @param {Object} adapter A SplitBar adapter object
37373      */
37374     setAdapter : function(adapter){
37375         this.adapter = adapter;
37376         this.adapter.init(this);
37377     },
37378     
37379     /**
37380      * Gets the minimum size for the resizing element
37381      * @return {Number} The minimum size
37382      */
37383     getMinimumSize : function(){
37384         return this.minSize;
37385     },
37386     
37387     /**
37388      * Sets the minimum size for the resizing element
37389      * @param {Number} minSize The minimum size
37390      */
37391     setMinimumSize : function(minSize){
37392         this.minSize = minSize;
37393     },
37394     
37395     /**
37396      * Gets the maximum size for the resizing element
37397      * @return {Number} The maximum size
37398      */
37399     getMaximumSize : function(){
37400         return this.maxSize;
37401     },
37402     
37403     /**
37404      * Sets the maximum size for the resizing element
37405      * @param {Number} maxSize The maximum size
37406      */
37407     setMaximumSize : function(maxSize){
37408         this.maxSize = maxSize;
37409     },
37410     
37411     /**
37412      * Sets the initialize size for the resizing element
37413      * @param {Number} size The initial size
37414      */
37415     setCurrentSize : function(size){
37416         var oldAnimate = this.animate;
37417         this.animate = false;
37418         this.adapter.setElementSize(this, size);
37419         this.animate = oldAnimate;
37420     },
37421     
37422     /**
37423      * Destroy this splitbar. 
37424      * @param {Boolean} removeEl True to remove the element
37425      */
37426     destroy : function(removeEl){
37427         if(this.shim){
37428             this.shim.remove();
37429         }
37430         this.dd.unreg();
37431         this.proxy.parentNode.removeChild(this.proxy);
37432         if(removeEl){
37433             this.el.remove();
37434         }
37435     }
37436 });
37437
37438 /**
37439  * @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.
37440  */
37441 Roo.bootstrap.SplitBar.createProxy = function(dir){
37442     var proxy = new Roo.Element(document.createElement("div"));
37443     proxy.unselectable();
37444     var cls = 'roo-splitbar-proxy';
37445     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37446     document.body.appendChild(proxy.dom);
37447     return proxy.dom;
37448 };
37449
37450 /** 
37451  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37452  * Default Adapter. It assumes the splitter and resizing element are not positioned
37453  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37454  */
37455 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37456 };
37457
37458 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37459     // do nothing for now
37460     init : function(s){
37461     
37462     },
37463     /**
37464      * Called before drag operations to get the current size of the resizing element. 
37465      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37466      */
37467      getElementSize : function(s){
37468         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37469             return s.resizingEl.getWidth();
37470         }else{
37471             return s.resizingEl.getHeight();
37472         }
37473     },
37474     
37475     /**
37476      * Called after drag operations to set the size of the resizing element.
37477      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37478      * @param {Number} newSize The new size to set
37479      * @param {Function} onComplete A function to be invoked when resizing is complete
37480      */
37481     setElementSize : function(s, newSize, onComplete){
37482         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37483             if(!s.animate){
37484                 s.resizingEl.setWidth(newSize);
37485                 if(onComplete){
37486                     onComplete(s, newSize);
37487                 }
37488             }else{
37489                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37490             }
37491         }else{
37492             
37493             if(!s.animate){
37494                 s.resizingEl.setHeight(newSize);
37495                 if(onComplete){
37496                     onComplete(s, newSize);
37497                 }
37498             }else{
37499                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37500             }
37501         }
37502     }
37503 };
37504
37505 /** 
37506  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37507  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37508  * Adapter that  moves the splitter element to align with the resized sizing element. 
37509  * Used with an absolute positioned SplitBar.
37510  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37511  * document.body, make sure you assign an id to the body element.
37512  */
37513 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37514     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37515     this.container = Roo.get(container);
37516 };
37517
37518 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37519     init : function(s){
37520         this.basic.init(s);
37521     },
37522     
37523     getElementSize : function(s){
37524         return this.basic.getElementSize(s);
37525     },
37526     
37527     setElementSize : function(s, newSize, onComplete){
37528         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37529     },
37530     
37531     moveSplitter : function(s){
37532         var yes = Roo.bootstrap.SplitBar;
37533         switch(s.placement){
37534             case yes.LEFT:
37535                 s.el.setX(s.resizingEl.getRight());
37536                 break;
37537             case yes.RIGHT:
37538                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37539                 break;
37540             case yes.TOP:
37541                 s.el.setY(s.resizingEl.getBottom());
37542                 break;
37543             case yes.BOTTOM:
37544                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37545                 break;
37546         }
37547     }
37548 };
37549
37550 /**
37551  * Orientation constant - Create a vertical SplitBar
37552  * @static
37553  * @type Number
37554  */
37555 Roo.bootstrap.SplitBar.VERTICAL = 1;
37556
37557 /**
37558  * Orientation constant - Create a horizontal SplitBar
37559  * @static
37560  * @type Number
37561  */
37562 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37563
37564 /**
37565  * Placement constant - The resizing element is to the left of the splitter element
37566  * @static
37567  * @type Number
37568  */
37569 Roo.bootstrap.SplitBar.LEFT = 1;
37570
37571 /**
37572  * Placement constant - The resizing element is to the right of the splitter element
37573  * @static
37574  * @type Number
37575  */
37576 Roo.bootstrap.SplitBar.RIGHT = 2;
37577
37578 /**
37579  * Placement constant - The resizing element is positioned above the splitter element
37580  * @static
37581  * @type Number
37582  */
37583 Roo.bootstrap.SplitBar.TOP = 3;
37584
37585 /**
37586  * Placement constant - The resizing element is positioned under splitter element
37587  * @static
37588  * @type Number
37589  */
37590 Roo.bootstrap.SplitBar.BOTTOM = 4;
37591 Roo.namespace("Roo.bootstrap.layout");/*
37592  * Based on:
37593  * Ext JS Library 1.1.1
37594  * Copyright(c) 2006-2007, Ext JS, LLC.
37595  *
37596  * Originally Released Under LGPL - original licence link has changed is not relivant.
37597  *
37598  * Fork - LGPL
37599  * <script type="text/javascript">
37600  */
37601
37602 /**
37603  * @class Roo.bootstrap.layout.Manager
37604  * @extends Roo.bootstrap.Component
37605  * Base class for layout managers.
37606  */
37607 Roo.bootstrap.layout.Manager = function(config)
37608 {
37609     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37610
37611
37612
37613
37614
37615     /** false to disable window resize monitoring @type Boolean */
37616     this.monitorWindowResize = true;
37617     this.regions = {};
37618     this.addEvents({
37619         /**
37620          * @event layout
37621          * Fires when a layout is performed.
37622          * @param {Roo.LayoutManager} this
37623          */
37624         "layout" : true,
37625         /**
37626          * @event regionresized
37627          * Fires when the user resizes a region.
37628          * @param {Roo.LayoutRegion} region The resized region
37629          * @param {Number} newSize The new size (width for east/west, height for north/south)
37630          */
37631         "regionresized" : true,
37632         /**
37633          * @event regioncollapsed
37634          * Fires when a region is collapsed.
37635          * @param {Roo.LayoutRegion} region The collapsed region
37636          */
37637         "regioncollapsed" : true,
37638         /**
37639          * @event regionexpanded
37640          * Fires when a region is expanded.
37641          * @param {Roo.LayoutRegion} region The expanded region
37642          */
37643         "regionexpanded" : true
37644     });
37645     this.updating = false;
37646
37647     if (config.el) {
37648         this.el = Roo.get(config.el);
37649         this.initEvents();
37650     }
37651
37652 };
37653
37654 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37655
37656
37657     regions : null,
37658
37659     monitorWindowResize : true,
37660
37661
37662     updating : false,
37663
37664
37665     onRender : function(ct, position)
37666     {
37667         if(!this.el){
37668             this.el = Roo.get(ct);
37669             this.initEvents();
37670         }
37671         //this.fireEvent('render',this);
37672     },
37673
37674
37675     initEvents: function()
37676     {
37677
37678
37679         // ie scrollbar fix
37680         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37681             document.body.scroll = "no";
37682         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37683             this.el.position('relative');
37684         }
37685         this.id = this.el.id;
37686         this.el.addClass("roo-layout-container");
37687         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37688         if(this.el.dom != document.body ) {
37689             this.el.on('resize', this.layout,this);
37690             this.el.on('show', this.layout,this);
37691         }
37692
37693     },
37694
37695     /**
37696      * Returns true if this layout is currently being updated
37697      * @return {Boolean}
37698      */
37699     isUpdating : function(){
37700         return this.updating;
37701     },
37702
37703     /**
37704      * Suspend the LayoutManager from doing auto-layouts while
37705      * making multiple add or remove calls
37706      */
37707     beginUpdate : function(){
37708         this.updating = true;
37709     },
37710
37711     /**
37712      * Restore auto-layouts and optionally disable the manager from performing a layout
37713      * @param {Boolean} noLayout true to disable a layout update
37714      */
37715     endUpdate : function(noLayout){
37716         this.updating = false;
37717         if(!noLayout){
37718             this.layout();
37719         }
37720     },
37721
37722     layout: function(){
37723         // abstract...
37724     },
37725
37726     onRegionResized : function(region, newSize){
37727         this.fireEvent("regionresized", region, newSize);
37728         this.layout();
37729     },
37730
37731     onRegionCollapsed : function(region){
37732         this.fireEvent("regioncollapsed", region);
37733     },
37734
37735     onRegionExpanded : function(region){
37736         this.fireEvent("regionexpanded", region);
37737     },
37738
37739     /**
37740      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37741      * performs box-model adjustments.
37742      * @return {Object} The size as an object {width: (the width), height: (the height)}
37743      */
37744     getViewSize : function()
37745     {
37746         var size;
37747         if(this.el.dom != document.body){
37748             size = this.el.getSize();
37749         }else{
37750             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37751         }
37752         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37753         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37754         return size;
37755     },
37756
37757     /**
37758      * Returns the Element this layout is bound to.
37759      * @return {Roo.Element}
37760      */
37761     getEl : function(){
37762         return this.el;
37763     },
37764
37765     /**
37766      * Returns the specified region.
37767      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37768      * @return {Roo.LayoutRegion}
37769      */
37770     getRegion : function(target){
37771         return this.regions[target.toLowerCase()];
37772     },
37773
37774     onWindowResize : function(){
37775         if(this.monitorWindowResize){
37776             this.layout();
37777         }
37778     }
37779 });
37780 /*
37781  * Based on:
37782  * Ext JS Library 1.1.1
37783  * Copyright(c) 2006-2007, Ext JS, LLC.
37784  *
37785  * Originally Released Under LGPL - original licence link has changed is not relivant.
37786  *
37787  * Fork - LGPL
37788  * <script type="text/javascript">
37789  */
37790 /**
37791  * @class Roo.bootstrap.layout.Border
37792  * @extends Roo.bootstrap.layout.Manager
37793  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37794  * please see: examples/bootstrap/nested.html<br><br>
37795  
37796 <b>The container the layout is rendered into can be either the body element or any other element.
37797 If it is not the body element, the container needs to either be an absolute positioned element,
37798 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37799 the container size if it is not the body element.</b>
37800
37801 * @constructor
37802 * Create a new Border
37803 * @param {Object} config Configuration options
37804  */
37805 Roo.bootstrap.layout.Border = function(config){
37806     config = config || {};
37807     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37808     
37809     
37810     
37811     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37812         if(config[region]){
37813             config[region].region = region;
37814             this.addRegion(config[region]);
37815         }
37816     },this);
37817     
37818 };
37819
37820 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37821
37822 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37823     
37824     parent : false, // this might point to a 'nest' or a ???
37825     
37826     /**
37827      * Creates and adds a new region if it doesn't already exist.
37828      * @param {String} target The target region key (north, south, east, west or center).
37829      * @param {Object} config The regions config object
37830      * @return {BorderLayoutRegion} The new region
37831      */
37832     addRegion : function(config)
37833     {
37834         if(!this.regions[config.region]){
37835             var r = this.factory(config);
37836             this.bindRegion(r);
37837         }
37838         return this.regions[config.region];
37839     },
37840
37841     // private (kinda)
37842     bindRegion : function(r){
37843         this.regions[r.config.region] = r;
37844         
37845         r.on("visibilitychange",    this.layout, this);
37846         r.on("paneladded",          this.layout, this);
37847         r.on("panelremoved",        this.layout, this);
37848         r.on("invalidated",         this.layout, this);
37849         r.on("resized",             this.onRegionResized, this);
37850         r.on("collapsed",           this.onRegionCollapsed, this);
37851         r.on("expanded",            this.onRegionExpanded, this);
37852     },
37853
37854     /**
37855      * Performs a layout update.
37856      */
37857     layout : function()
37858     {
37859         if(this.updating) {
37860             return;
37861         }
37862         
37863         // render all the rebions if they have not been done alreayd?
37864         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37865             if(this.regions[region] && !this.regions[region].bodyEl){
37866                 this.regions[region].onRender(this.el)
37867             }
37868         },this);
37869         
37870         var size = this.getViewSize();
37871         var w = size.width;
37872         var h = size.height;
37873         var centerW = w;
37874         var centerH = h;
37875         var centerY = 0;
37876         var centerX = 0;
37877         //var x = 0, y = 0;
37878
37879         var rs = this.regions;
37880         var north = rs["north"];
37881         var south = rs["south"]; 
37882         var west = rs["west"];
37883         var east = rs["east"];
37884         var center = rs["center"];
37885         //if(this.hideOnLayout){ // not supported anymore
37886             //c.el.setStyle("display", "none");
37887         //}
37888         if(north && north.isVisible()){
37889             var b = north.getBox();
37890             var m = north.getMargins();
37891             b.width = w - (m.left+m.right);
37892             b.x = m.left;
37893             b.y = m.top;
37894             centerY = b.height + b.y + m.bottom;
37895             centerH -= centerY;
37896             north.updateBox(this.safeBox(b));
37897         }
37898         if(south && south.isVisible()){
37899             var b = south.getBox();
37900             var m = south.getMargins();
37901             b.width = w - (m.left+m.right);
37902             b.x = m.left;
37903             var totalHeight = (b.height + m.top + m.bottom);
37904             b.y = h - totalHeight + m.top;
37905             centerH -= totalHeight;
37906             south.updateBox(this.safeBox(b));
37907         }
37908         if(west && west.isVisible()){
37909             var b = west.getBox();
37910             var m = west.getMargins();
37911             b.height = centerH - (m.top+m.bottom);
37912             b.x = m.left;
37913             b.y = centerY + m.top;
37914             var totalWidth = (b.width + m.left + m.right);
37915             centerX += totalWidth;
37916             centerW -= totalWidth;
37917             west.updateBox(this.safeBox(b));
37918         }
37919         if(east && east.isVisible()){
37920             var b = east.getBox();
37921             var m = east.getMargins();
37922             b.height = centerH - (m.top+m.bottom);
37923             var totalWidth = (b.width + m.left + m.right);
37924             b.x = w - totalWidth + m.left;
37925             b.y = centerY + m.top;
37926             centerW -= totalWidth;
37927             east.updateBox(this.safeBox(b));
37928         }
37929         if(center){
37930             var m = center.getMargins();
37931             var centerBox = {
37932                 x: centerX + m.left,
37933                 y: centerY + m.top,
37934                 width: centerW - (m.left+m.right),
37935                 height: centerH - (m.top+m.bottom)
37936             };
37937             //if(this.hideOnLayout){
37938                 //center.el.setStyle("display", "block");
37939             //}
37940             center.updateBox(this.safeBox(centerBox));
37941         }
37942         this.el.repaint();
37943         this.fireEvent("layout", this);
37944     },
37945
37946     // private
37947     safeBox : function(box){
37948         box.width = Math.max(0, box.width);
37949         box.height = Math.max(0, box.height);
37950         return box;
37951     },
37952
37953     /**
37954      * Adds a ContentPanel (or subclass) to this layout.
37955      * @param {String} target The target region key (north, south, east, west or center).
37956      * @param {Roo.ContentPanel} panel The panel to add
37957      * @return {Roo.ContentPanel} The added panel
37958      */
37959     add : function(target, panel){
37960          
37961         target = target.toLowerCase();
37962         return this.regions[target].add(panel);
37963     },
37964
37965     /**
37966      * Remove a ContentPanel (or subclass) to this layout.
37967      * @param {String} target The target region key (north, south, east, west or center).
37968      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37969      * @return {Roo.ContentPanel} The removed panel
37970      */
37971     remove : function(target, panel){
37972         target = target.toLowerCase();
37973         return this.regions[target].remove(panel);
37974     },
37975
37976     /**
37977      * Searches all regions for a panel with the specified id
37978      * @param {String} panelId
37979      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37980      */
37981     findPanel : function(panelId){
37982         var rs = this.regions;
37983         for(var target in rs){
37984             if(typeof rs[target] != "function"){
37985                 var p = rs[target].getPanel(panelId);
37986                 if(p){
37987                     return p;
37988                 }
37989             }
37990         }
37991         return null;
37992     },
37993
37994     /**
37995      * Searches all regions for a panel with the specified id and activates (shows) it.
37996      * @param {String/ContentPanel} panelId The panels id or the panel itself
37997      * @return {Roo.ContentPanel} The shown panel or null
37998      */
37999     showPanel : function(panelId) {
38000       var rs = this.regions;
38001       for(var target in rs){
38002          var r = rs[target];
38003          if(typeof r != "function"){
38004             if(r.hasPanel(panelId)){
38005                return r.showPanel(panelId);
38006             }
38007          }
38008       }
38009       return null;
38010    },
38011
38012    /**
38013      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38014      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38015      */
38016    /*
38017     restoreState : function(provider){
38018         if(!provider){
38019             provider = Roo.state.Manager;
38020         }
38021         var sm = new Roo.LayoutStateManager();
38022         sm.init(this, provider);
38023     },
38024 */
38025  
38026  
38027     /**
38028      * Adds a xtype elements to the layout.
38029      * <pre><code>
38030
38031 layout.addxtype({
38032        xtype : 'ContentPanel',
38033        region: 'west',
38034        items: [ .... ]
38035    }
38036 );
38037
38038 layout.addxtype({
38039         xtype : 'NestedLayoutPanel',
38040         region: 'west',
38041         layout: {
38042            center: { },
38043            west: { }   
38044         },
38045         items : [ ... list of content panels or nested layout panels.. ]
38046    }
38047 );
38048 </code></pre>
38049      * @param {Object} cfg Xtype definition of item to add.
38050      */
38051     addxtype : function(cfg)
38052     {
38053         // basically accepts a pannel...
38054         // can accept a layout region..!?!?
38055         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38056         
38057         
38058         // theory?  children can only be panels??
38059         
38060         //if (!cfg.xtype.match(/Panel$/)) {
38061         //    return false;
38062         //}
38063         var ret = false;
38064         
38065         if (typeof(cfg.region) == 'undefined') {
38066             Roo.log("Failed to add Panel, region was not set");
38067             Roo.log(cfg);
38068             return false;
38069         }
38070         var region = cfg.region;
38071         delete cfg.region;
38072         
38073           
38074         var xitems = [];
38075         if (cfg.items) {
38076             xitems = cfg.items;
38077             delete cfg.items;
38078         }
38079         var nb = false;
38080         
38081         if ( region == 'center') {
38082             Roo.log("Center: " + cfg.title);
38083         }
38084         
38085         
38086         switch(cfg.xtype) 
38087         {
38088             case 'Content':  // ContentPanel (el, cfg)
38089             case 'Scroll':  // ContentPanel (el, cfg)
38090             case 'View': 
38091                 cfg.autoCreate = cfg.autoCreate || true;
38092                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38093                 //} else {
38094                 //    var el = this.el.createChild();
38095                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38096                 //}
38097                 
38098                 this.add(region, ret);
38099                 break;
38100             
38101             /*
38102             case 'TreePanel': // our new panel!
38103                 cfg.el = this.el.createChild();
38104                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38105                 this.add(region, ret);
38106                 break;
38107             */
38108             
38109             case 'Nest': 
38110                 // create a new Layout (which is  a Border Layout...
38111                 
38112                 var clayout = cfg.layout;
38113                 clayout.el  = this.el.createChild();
38114                 clayout.items   = clayout.items  || [];
38115                 
38116                 delete cfg.layout;
38117                 
38118                 // replace this exitems with the clayout ones..
38119                 xitems = clayout.items;
38120                  
38121                 // force background off if it's in center...
38122                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38123                     cfg.background = false;
38124                 }
38125                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38126                 
38127                 
38128                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38129                 //console.log('adding nested layout panel '  + cfg.toSource());
38130                 this.add(region, ret);
38131                 nb = {}; /// find first...
38132                 break;
38133             
38134             case 'Grid':
38135                 
38136                 // needs grid and region
38137                 
38138                 //var el = this.getRegion(region).el.createChild();
38139                 /*
38140                  *var el = this.el.createChild();
38141                 // create the grid first...
38142                 cfg.grid.container = el;
38143                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38144                 */
38145                 
38146                 if (region == 'center' && this.active ) {
38147                     cfg.background = false;
38148                 }
38149                 
38150                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38151                 
38152                 this.add(region, ret);
38153                 /*
38154                 if (cfg.background) {
38155                     // render grid on panel activation (if panel background)
38156                     ret.on('activate', function(gp) {
38157                         if (!gp.grid.rendered) {
38158                     //        gp.grid.render(el);
38159                         }
38160                     });
38161                 } else {
38162                   //  cfg.grid.render(el);
38163                 }
38164                 */
38165                 break;
38166            
38167            
38168             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38169                 // it was the old xcomponent building that caused this before.
38170                 // espeically if border is the top element in the tree.
38171                 ret = this;
38172                 break; 
38173                 
38174                     
38175                 
38176                 
38177                 
38178             default:
38179                 /*
38180                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38181                     
38182                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38183                     this.add(region, ret);
38184                 } else {
38185                 */
38186                     Roo.log(cfg);
38187                     throw "Can not add '" + cfg.xtype + "' to Border";
38188                     return null;
38189              
38190                                 
38191              
38192         }
38193         this.beginUpdate();
38194         // add children..
38195         var region = '';
38196         var abn = {};
38197         Roo.each(xitems, function(i)  {
38198             region = nb && i.region ? i.region : false;
38199             
38200             var add = ret.addxtype(i);
38201            
38202             if (region) {
38203                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38204                 if (!i.background) {
38205                     abn[region] = nb[region] ;
38206                 }
38207             }
38208             
38209         });
38210         this.endUpdate();
38211
38212         // make the last non-background panel active..
38213         //if (nb) { Roo.log(abn); }
38214         if (nb) {
38215             
38216             for(var r in abn) {
38217                 region = this.getRegion(r);
38218                 if (region) {
38219                     // tried using nb[r], but it does not work..
38220                      
38221                     region.showPanel(abn[r]);
38222                    
38223                 }
38224             }
38225         }
38226         return ret;
38227         
38228     },
38229     
38230     
38231 // private
38232     factory : function(cfg)
38233     {
38234         
38235         var validRegions = Roo.bootstrap.layout.Border.regions;
38236
38237         var target = cfg.region;
38238         cfg.mgr = this;
38239         
38240         var r = Roo.bootstrap.layout;
38241         Roo.log(target);
38242         switch(target){
38243             case "north":
38244                 return new r.North(cfg);
38245             case "south":
38246                 return new r.South(cfg);
38247             case "east":
38248                 return new r.East(cfg);
38249             case "west":
38250                 return new r.West(cfg);
38251             case "center":
38252                 return new r.Center(cfg);
38253         }
38254         throw 'Layout region "'+target+'" not supported.';
38255     }
38256     
38257     
38258 });
38259  /*
38260  * Based on:
38261  * Ext JS Library 1.1.1
38262  * Copyright(c) 2006-2007, Ext JS, LLC.
38263  *
38264  * Originally Released Under LGPL - original licence link has changed is not relivant.
38265  *
38266  * Fork - LGPL
38267  * <script type="text/javascript">
38268  */
38269  
38270 /**
38271  * @class Roo.bootstrap.layout.Basic
38272  * @extends Roo.util.Observable
38273  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38274  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38275  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38276  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38277  * @cfg {string}   region  the region that it inhabits..
38278  * @cfg {bool}   skipConfig skip config?
38279  * 
38280
38281  */
38282 Roo.bootstrap.layout.Basic = function(config){
38283     
38284     this.mgr = config.mgr;
38285     
38286     this.position = config.region;
38287     
38288     var skipConfig = config.skipConfig;
38289     
38290     this.events = {
38291         /**
38292          * @scope Roo.BasicLayoutRegion
38293          */
38294         
38295         /**
38296          * @event beforeremove
38297          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38298          * @param {Roo.LayoutRegion} this
38299          * @param {Roo.ContentPanel} panel The panel
38300          * @param {Object} e The cancel event object
38301          */
38302         "beforeremove" : true,
38303         /**
38304          * @event invalidated
38305          * Fires when the layout for this region is changed.
38306          * @param {Roo.LayoutRegion} this
38307          */
38308         "invalidated" : true,
38309         /**
38310          * @event visibilitychange
38311          * Fires when this region is shown or hidden 
38312          * @param {Roo.LayoutRegion} this
38313          * @param {Boolean} visibility true or false
38314          */
38315         "visibilitychange" : true,
38316         /**
38317          * @event paneladded
38318          * Fires when a panel is added. 
38319          * @param {Roo.LayoutRegion} this
38320          * @param {Roo.ContentPanel} panel The panel
38321          */
38322         "paneladded" : true,
38323         /**
38324          * @event panelremoved
38325          * Fires when a panel is removed. 
38326          * @param {Roo.LayoutRegion} this
38327          * @param {Roo.ContentPanel} panel The panel
38328          */
38329         "panelremoved" : true,
38330         /**
38331          * @event beforecollapse
38332          * Fires when this region before collapse.
38333          * @param {Roo.LayoutRegion} this
38334          */
38335         "beforecollapse" : true,
38336         /**
38337          * @event collapsed
38338          * Fires when this region is collapsed.
38339          * @param {Roo.LayoutRegion} this
38340          */
38341         "collapsed" : true,
38342         /**
38343          * @event expanded
38344          * Fires when this region is expanded.
38345          * @param {Roo.LayoutRegion} this
38346          */
38347         "expanded" : true,
38348         /**
38349          * @event slideshow
38350          * Fires when this region is slid into view.
38351          * @param {Roo.LayoutRegion} this
38352          */
38353         "slideshow" : true,
38354         /**
38355          * @event slidehide
38356          * Fires when this region slides out of view. 
38357          * @param {Roo.LayoutRegion} this
38358          */
38359         "slidehide" : true,
38360         /**
38361          * @event panelactivated
38362          * Fires when a panel is activated. 
38363          * @param {Roo.LayoutRegion} this
38364          * @param {Roo.ContentPanel} panel The activated panel
38365          */
38366         "panelactivated" : true,
38367         /**
38368          * @event resized
38369          * Fires when the user resizes this region. 
38370          * @param {Roo.LayoutRegion} this
38371          * @param {Number} newSize The new size (width for east/west, height for north/south)
38372          */
38373         "resized" : true
38374     };
38375     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38376     this.panels = new Roo.util.MixedCollection();
38377     this.panels.getKey = this.getPanelId.createDelegate(this);
38378     this.box = null;
38379     this.activePanel = null;
38380     // ensure listeners are added...
38381     
38382     if (config.listeners || config.events) {
38383         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38384             listeners : config.listeners || {},
38385             events : config.events || {}
38386         });
38387     }
38388     
38389     if(skipConfig !== true){
38390         this.applyConfig(config);
38391     }
38392 };
38393
38394 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38395 {
38396     getPanelId : function(p){
38397         return p.getId();
38398     },
38399     
38400     applyConfig : function(config){
38401         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38402         this.config = config;
38403         
38404     },
38405     
38406     /**
38407      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38408      * the width, for horizontal (north, south) the height.
38409      * @param {Number} newSize The new width or height
38410      */
38411     resizeTo : function(newSize){
38412         var el = this.el ? this.el :
38413                  (this.activePanel ? this.activePanel.getEl() : null);
38414         if(el){
38415             switch(this.position){
38416                 case "east":
38417                 case "west":
38418                     el.setWidth(newSize);
38419                     this.fireEvent("resized", this, newSize);
38420                 break;
38421                 case "north":
38422                 case "south":
38423                     el.setHeight(newSize);
38424                     this.fireEvent("resized", this, newSize);
38425                 break;                
38426             }
38427         }
38428     },
38429     
38430     getBox : function(){
38431         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38432     },
38433     
38434     getMargins : function(){
38435         return this.margins;
38436     },
38437     
38438     updateBox : function(box){
38439         this.box = box;
38440         var el = this.activePanel.getEl();
38441         el.dom.style.left = box.x + "px";
38442         el.dom.style.top = box.y + "px";
38443         this.activePanel.setSize(box.width, box.height);
38444     },
38445     
38446     /**
38447      * Returns the container element for this region.
38448      * @return {Roo.Element}
38449      */
38450     getEl : function(){
38451         return this.activePanel;
38452     },
38453     
38454     /**
38455      * Returns true if this region is currently visible.
38456      * @return {Boolean}
38457      */
38458     isVisible : function(){
38459         return this.activePanel ? true : false;
38460     },
38461     
38462     setActivePanel : function(panel){
38463         panel = this.getPanel(panel);
38464         if(this.activePanel && this.activePanel != panel){
38465             this.activePanel.setActiveState(false);
38466             this.activePanel.getEl().setLeftTop(-10000,-10000);
38467         }
38468         this.activePanel = panel;
38469         panel.setActiveState(true);
38470         if(this.box){
38471             panel.setSize(this.box.width, this.box.height);
38472         }
38473         this.fireEvent("panelactivated", this, panel);
38474         this.fireEvent("invalidated");
38475     },
38476     
38477     /**
38478      * Show the specified panel.
38479      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38480      * @return {Roo.ContentPanel} The shown panel or null
38481      */
38482     showPanel : function(panel){
38483         panel = this.getPanel(panel);
38484         if(panel){
38485             this.setActivePanel(panel);
38486         }
38487         return panel;
38488     },
38489     
38490     /**
38491      * Get the active panel for this region.
38492      * @return {Roo.ContentPanel} The active panel or null
38493      */
38494     getActivePanel : function(){
38495         return this.activePanel;
38496     },
38497     
38498     /**
38499      * Add the passed ContentPanel(s)
38500      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38501      * @return {Roo.ContentPanel} The panel added (if only one was added)
38502      */
38503     add : function(panel){
38504         if(arguments.length > 1){
38505             for(var i = 0, len = arguments.length; i < len; i++) {
38506                 this.add(arguments[i]);
38507             }
38508             return null;
38509         }
38510         if(this.hasPanel(panel)){
38511             this.showPanel(panel);
38512             return panel;
38513         }
38514         var el = panel.getEl();
38515         if(el.dom.parentNode != this.mgr.el.dom){
38516             this.mgr.el.dom.appendChild(el.dom);
38517         }
38518         if(panel.setRegion){
38519             panel.setRegion(this);
38520         }
38521         this.panels.add(panel);
38522         el.setStyle("position", "absolute");
38523         if(!panel.background){
38524             this.setActivePanel(panel);
38525             if(this.config.initialSize && this.panels.getCount()==1){
38526                 this.resizeTo(this.config.initialSize);
38527             }
38528         }
38529         this.fireEvent("paneladded", this, panel);
38530         return panel;
38531     },
38532     
38533     /**
38534      * Returns true if the panel is in this region.
38535      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38536      * @return {Boolean}
38537      */
38538     hasPanel : function(panel){
38539         if(typeof panel == "object"){ // must be panel obj
38540             panel = panel.getId();
38541         }
38542         return this.getPanel(panel) ? true : false;
38543     },
38544     
38545     /**
38546      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38547      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38548      * @param {Boolean} preservePanel Overrides the config preservePanel option
38549      * @return {Roo.ContentPanel} The panel that was removed
38550      */
38551     remove : function(panel, preservePanel){
38552         panel = this.getPanel(panel);
38553         if(!panel){
38554             return null;
38555         }
38556         var e = {};
38557         this.fireEvent("beforeremove", this, panel, e);
38558         if(e.cancel === true){
38559             return null;
38560         }
38561         var panelId = panel.getId();
38562         this.panels.removeKey(panelId);
38563         return panel;
38564     },
38565     
38566     /**
38567      * Returns the panel specified or null if it's not in this region.
38568      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38569      * @return {Roo.ContentPanel}
38570      */
38571     getPanel : function(id){
38572         if(typeof id == "object"){ // must be panel obj
38573             return id;
38574         }
38575         return this.panels.get(id);
38576     },
38577     
38578     /**
38579      * Returns this regions position (north/south/east/west/center).
38580      * @return {String} 
38581      */
38582     getPosition: function(){
38583         return this.position;    
38584     }
38585 });/*
38586  * Based on:
38587  * Ext JS Library 1.1.1
38588  * Copyright(c) 2006-2007, Ext JS, LLC.
38589  *
38590  * Originally Released Under LGPL - original licence link has changed is not relivant.
38591  *
38592  * Fork - LGPL
38593  * <script type="text/javascript">
38594  */
38595  
38596 /**
38597  * @class Roo.bootstrap.layout.Region
38598  * @extends Roo.bootstrap.layout.Basic
38599  * This class represents a region in a layout manager.
38600  
38601  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38602  * @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})
38603  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38604  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38605  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38606  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38607  * @cfg {String}    title           The title for the region (overrides panel titles)
38608  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38609  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38610  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38611  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38612  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38613  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38614  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38615  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38616  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38617  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38618
38619  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38620  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38621  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38622  * @cfg {Number}    width           For East/West panels
38623  * @cfg {Number}    height          For North/South panels
38624  * @cfg {Boolean}   split           To show the splitter
38625  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38626  * 
38627  * @cfg {string}   cls             Extra CSS classes to add to region
38628  * 
38629  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38630  * @cfg {string}   region  the region that it inhabits..
38631  *
38632
38633  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38634  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38635
38636  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38637  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38638  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38639  */
38640 Roo.bootstrap.layout.Region = function(config)
38641 {
38642     this.applyConfig(config);
38643
38644     var mgr = config.mgr;
38645     var pos = config.region;
38646     config.skipConfig = true;
38647     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38648     
38649     if (mgr.el) {
38650         this.onRender(mgr.el);   
38651     }
38652      
38653     this.visible = true;
38654     this.collapsed = false;
38655     this.unrendered_panels = [];
38656 };
38657
38658 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38659
38660     position: '', // set by wrapper (eg. north/south etc..)
38661     unrendered_panels : null,  // unrendered panels.
38662     
38663     tabPosition : false,
38664     
38665     mgr: false, // points to 'Border'
38666     
38667     
38668     createBody : function(){
38669         /** This region's body element 
38670         * @type Roo.Element */
38671         this.bodyEl = this.el.createChild({
38672                 tag: "div",
38673                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38674         });
38675     },
38676
38677     onRender: function(ctr, pos)
38678     {
38679         var dh = Roo.DomHelper;
38680         /** This region's container element 
38681         * @type Roo.Element */
38682         this.el = dh.append(ctr.dom, {
38683                 tag: "div",
38684                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38685             }, true);
38686         /** This region's title element 
38687         * @type Roo.Element */
38688     
38689         this.titleEl = dh.append(this.el.dom,  {
38690                 tag: "div",
38691                 unselectable: "on",
38692                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38693                 children:[
38694                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38695                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38696                 ]
38697             }, true);
38698         
38699         this.titleEl.enableDisplayMode();
38700         /** This region's title text element 
38701         * @type HTMLElement */
38702         this.titleTextEl = this.titleEl.dom.firstChild;
38703         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38704         /*
38705         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38706         this.closeBtn.enableDisplayMode();
38707         this.closeBtn.on("click", this.closeClicked, this);
38708         this.closeBtn.hide();
38709     */
38710         this.createBody(this.config);
38711         if(this.config.hideWhenEmpty){
38712             this.hide();
38713             this.on("paneladded", this.validateVisibility, this);
38714             this.on("panelremoved", this.validateVisibility, this);
38715         }
38716         if(this.autoScroll){
38717             this.bodyEl.setStyle("overflow", "auto");
38718         }else{
38719             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38720         }
38721         //if(c.titlebar !== false){
38722             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38723                 this.titleEl.hide();
38724             }else{
38725                 this.titleEl.show();
38726                 if(this.config.title){
38727                     this.titleTextEl.innerHTML = this.config.title;
38728                 }
38729             }
38730         //}
38731         if(this.config.collapsed){
38732             this.collapse(true);
38733         }
38734         if(this.config.hidden){
38735             this.hide();
38736         }
38737         
38738         if (this.unrendered_panels && this.unrendered_panels.length) {
38739             for (var i =0;i< this.unrendered_panels.length; i++) {
38740                 this.add(this.unrendered_panels[i]);
38741             }
38742             this.unrendered_panels = null;
38743             
38744         }
38745         
38746     },
38747     
38748     applyConfig : function(c)
38749     {
38750         /*
38751          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38752             var dh = Roo.DomHelper;
38753             if(c.titlebar !== false){
38754                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38755                 this.collapseBtn.on("click", this.collapse, this);
38756                 this.collapseBtn.enableDisplayMode();
38757                 /*
38758                 if(c.showPin === true || this.showPin){
38759                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38760                     this.stickBtn.enableDisplayMode();
38761                     this.stickBtn.on("click", this.expand, this);
38762                     this.stickBtn.hide();
38763                 }
38764                 
38765             }
38766             */
38767             /** This region's collapsed element
38768             * @type Roo.Element */
38769             /*
38770              *
38771             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38772                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38773             ]}, true);
38774             
38775             if(c.floatable !== false){
38776                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38777                this.collapsedEl.on("click", this.collapseClick, this);
38778             }
38779
38780             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38781                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38782                    id: "message", unselectable: "on", style:{"float":"left"}});
38783                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38784              }
38785             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38786             this.expandBtn.on("click", this.expand, this);
38787             
38788         }
38789         
38790         if(this.collapseBtn){
38791             this.collapseBtn.setVisible(c.collapsible == true);
38792         }
38793         
38794         this.cmargins = c.cmargins || this.cmargins ||
38795                          (this.position == "west" || this.position == "east" ?
38796                              {top: 0, left: 2, right:2, bottom: 0} :
38797                              {top: 2, left: 0, right:0, bottom: 2});
38798         */
38799         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38800         
38801         
38802         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38803         
38804         this.autoScroll = c.autoScroll || false;
38805         
38806         
38807        
38808         
38809         this.duration = c.duration || .30;
38810         this.slideDuration = c.slideDuration || .45;
38811         this.config = c;
38812        
38813     },
38814     /**
38815      * Returns true if this region is currently visible.
38816      * @return {Boolean}
38817      */
38818     isVisible : function(){
38819         return this.visible;
38820     },
38821
38822     /**
38823      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38824      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38825      */
38826     //setCollapsedTitle : function(title){
38827     //    title = title || "&#160;";
38828      //   if(this.collapsedTitleTextEl){
38829       //      this.collapsedTitleTextEl.innerHTML = title;
38830        // }
38831     //},
38832
38833     getBox : function(){
38834         var b;
38835       //  if(!this.collapsed){
38836             b = this.el.getBox(false, true);
38837        // }else{
38838           //  b = this.collapsedEl.getBox(false, true);
38839         //}
38840         return b;
38841     },
38842
38843     getMargins : function(){
38844         return this.margins;
38845         //return this.collapsed ? this.cmargins : this.margins;
38846     },
38847 /*
38848     highlight : function(){
38849         this.el.addClass("x-layout-panel-dragover");
38850     },
38851
38852     unhighlight : function(){
38853         this.el.removeClass("x-layout-panel-dragover");
38854     },
38855 */
38856     updateBox : function(box)
38857     {
38858         if (!this.bodyEl) {
38859             return; // not rendered yet..
38860         }
38861         
38862         this.box = box;
38863         if(!this.collapsed){
38864             this.el.dom.style.left = box.x + "px";
38865             this.el.dom.style.top = box.y + "px";
38866             this.updateBody(box.width, box.height);
38867         }else{
38868             this.collapsedEl.dom.style.left = box.x + "px";
38869             this.collapsedEl.dom.style.top = box.y + "px";
38870             this.collapsedEl.setSize(box.width, box.height);
38871         }
38872         if(this.tabs){
38873             this.tabs.autoSizeTabs();
38874         }
38875     },
38876
38877     updateBody : function(w, h)
38878     {
38879         if(w !== null){
38880             this.el.setWidth(w);
38881             w -= this.el.getBorderWidth("rl");
38882             if(this.config.adjustments){
38883                 w += this.config.adjustments[0];
38884             }
38885         }
38886         if(h !== null && h > 0){
38887             this.el.setHeight(h);
38888             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38889             h -= this.el.getBorderWidth("tb");
38890             if(this.config.adjustments){
38891                 h += this.config.adjustments[1];
38892             }
38893             this.bodyEl.setHeight(h);
38894             if(this.tabs){
38895                 h = this.tabs.syncHeight(h);
38896             }
38897         }
38898         if(this.panelSize){
38899             w = w !== null ? w : this.panelSize.width;
38900             h = h !== null ? h : this.panelSize.height;
38901         }
38902         if(this.activePanel){
38903             var el = this.activePanel.getEl();
38904             w = w !== null ? w : el.getWidth();
38905             h = h !== null ? h : el.getHeight();
38906             this.panelSize = {width: w, height: h};
38907             this.activePanel.setSize(w, h);
38908         }
38909         if(Roo.isIE && this.tabs){
38910             this.tabs.el.repaint();
38911         }
38912     },
38913
38914     /**
38915      * Returns the container element for this region.
38916      * @return {Roo.Element}
38917      */
38918     getEl : function(){
38919         return this.el;
38920     },
38921
38922     /**
38923      * Hides this region.
38924      */
38925     hide : function(){
38926         //if(!this.collapsed){
38927             this.el.dom.style.left = "-2000px";
38928             this.el.hide();
38929         //}else{
38930          //   this.collapsedEl.dom.style.left = "-2000px";
38931          //   this.collapsedEl.hide();
38932        // }
38933         this.visible = false;
38934         this.fireEvent("visibilitychange", this, false);
38935     },
38936
38937     /**
38938      * Shows this region if it was previously hidden.
38939      */
38940     show : function(){
38941         //if(!this.collapsed){
38942             this.el.show();
38943         //}else{
38944         //    this.collapsedEl.show();
38945        // }
38946         this.visible = true;
38947         this.fireEvent("visibilitychange", this, true);
38948     },
38949 /*
38950     closeClicked : function(){
38951         if(this.activePanel){
38952             this.remove(this.activePanel);
38953         }
38954     },
38955
38956     collapseClick : function(e){
38957         if(this.isSlid){
38958            e.stopPropagation();
38959            this.slideIn();
38960         }else{
38961            e.stopPropagation();
38962            this.slideOut();
38963         }
38964     },
38965 */
38966     /**
38967      * Collapses this region.
38968      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38969      */
38970     /*
38971     collapse : function(skipAnim, skipCheck = false){
38972         if(this.collapsed) {
38973             return;
38974         }
38975         
38976         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38977             
38978             this.collapsed = true;
38979             if(this.split){
38980                 this.split.el.hide();
38981             }
38982             if(this.config.animate && skipAnim !== true){
38983                 this.fireEvent("invalidated", this);
38984                 this.animateCollapse();
38985             }else{
38986                 this.el.setLocation(-20000,-20000);
38987                 this.el.hide();
38988                 this.collapsedEl.show();
38989                 this.fireEvent("collapsed", this);
38990                 this.fireEvent("invalidated", this);
38991             }
38992         }
38993         
38994     },
38995 */
38996     animateCollapse : function(){
38997         // overridden
38998     },
38999
39000     /**
39001      * Expands this region if it was previously collapsed.
39002      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39003      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39004      */
39005     /*
39006     expand : function(e, skipAnim){
39007         if(e) {
39008             e.stopPropagation();
39009         }
39010         if(!this.collapsed || this.el.hasActiveFx()) {
39011             return;
39012         }
39013         if(this.isSlid){
39014             this.afterSlideIn();
39015             skipAnim = true;
39016         }
39017         this.collapsed = false;
39018         if(this.config.animate && skipAnim !== true){
39019             this.animateExpand();
39020         }else{
39021             this.el.show();
39022             if(this.split){
39023                 this.split.el.show();
39024             }
39025             this.collapsedEl.setLocation(-2000,-2000);
39026             this.collapsedEl.hide();
39027             this.fireEvent("invalidated", this);
39028             this.fireEvent("expanded", this);
39029         }
39030     },
39031 */
39032     animateExpand : function(){
39033         // overridden
39034     },
39035
39036     initTabs : function()
39037     {
39038         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39039         
39040         var ts = new Roo.bootstrap.panel.Tabs({
39041             el: this.bodyEl.dom,
39042             region : this,
39043             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39044             disableTooltips: this.config.disableTabTips,
39045             toolbar : this.config.toolbar
39046         });
39047         
39048         if(this.config.hideTabs){
39049             ts.stripWrap.setDisplayed(false);
39050         }
39051         this.tabs = ts;
39052         ts.resizeTabs = this.config.resizeTabs === true;
39053         ts.minTabWidth = this.config.minTabWidth || 40;
39054         ts.maxTabWidth = this.config.maxTabWidth || 250;
39055         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39056         ts.monitorResize = false;
39057         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39058         ts.bodyEl.addClass('roo-layout-tabs-body');
39059         this.panels.each(this.initPanelAsTab, this);
39060     },
39061
39062     initPanelAsTab : function(panel){
39063         var ti = this.tabs.addTab(
39064             panel.getEl().id,
39065             panel.getTitle(),
39066             null,
39067             this.config.closeOnTab && panel.isClosable(),
39068             panel.tpl
39069         );
39070         if(panel.tabTip !== undefined){
39071             ti.setTooltip(panel.tabTip);
39072         }
39073         ti.on("activate", function(){
39074               this.setActivePanel(panel);
39075         }, this);
39076         
39077         if(this.config.closeOnTab){
39078             ti.on("beforeclose", function(t, e){
39079                 e.cancel = true;
39080                 this.remove(panel);
39081             }, this);
39082         }
39083         
39084         panel.tabItem = ti;
39085         
39086         return ti;
39087     },
39088
39089     updatePanelTitle : function(panel, title)
39090     {
39091         if(this.activePanel == panel){
39092             this.updateTitle(title);
39093         }
39094         if(this.tabs){
39095             var ti = this.tabs.getTab(panel.getEl().id);
39096             ti.setText(title);
39097             if(panel.tabTip !== undefined){
39098                 ti.setTooltip(panel.tabTip);
39099             }
39100         }
39101     },
39102
39103     updateTitle : function(title){
39104         if(this.titleTextEl && !this.config.title){
39105             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39106         }
39107     },
39108
39109     setActivePanel : function(panel)
39110     {
39111         panel = this.getPanel(panel);
39112         if(this.activePanel && this.activePanel != panel){
39113             if(this.activePanel.setActiveState(false) === false){
39114                 return;
39115             }
39116         }
39117         this.activePanel = panel;
39118         panel.setActiveState(true);
39119         if(this.panelSize){
39120             panel.setSize(this.panelSize.width, this.panelSize.height);
39121         }
39122         if(this.closeBtn){
39123             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39124         }
39125         this.updateTitle(panel.getTitle());
39126         if(this.tabs){
39127             this.fireEvent("invalidated", this);
39128         }
39129         this.fireEvent("panelactivated", this, panel);
39130     },
39131
39132     /**
39133      * Shows the specified panel.
39134      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39135      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39136      */
39137     showPanel : function(panel)
39138     {
39139         panel = this.getPanel(panel);
39140         if(panel){
39141             if(this.tabs){
39142                 var tab = this.tabs.getTab(panel.getEl().id);
39143                 if(tab.isHidden()){
39144                     this.tabs.unhideTab(tab.id);
39145                 }
39146                 tab.activate();
39147             }else{
39148                 this.setActivePanel(panel);
39149             }
39150         }
39151         return panel;
39152     },
39153
39154     /**
39155      * Get the active panel for this region.
39156      * @return {Roo.ContentPanel} The active panel or null
39157      */
39158     getActivePanel : function(){
39159         return this.activePanel;
39160     },
39161
39162     validateVisibility : function(){
39163         if(this.panels.getCount() < 1){
39164             this.updateTitle("&#160;");
39165             this.closeBtn.hide();
39166             this.hide();
39167         }else{
39168             if(!this.isVisible()){
39169                 this.show();
39170             }
39171         }
39172     },
39173
39174     /**
39175      * Adds the passed ContentPanel(s) to this region.
39176      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39177      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39178      */
39179     add : function(panel)
39180     {
39181         if(arguments.length > 1){
39182             for(var i = 0, len = arguments.length; i < len; i++) {
39183                 this.add(arguments[i]);
39184             }
39185             return null;
39186         }
39187         
39188         // if we have not been rendered yet, then we can not really do much of this..
39189         if (!this.bodyEl) {
39190             this.unrendered_panels.push(panel);
39191             return panel;
39192         }
39193         
39194         
39195         
39196         
39197         if(this.hasPanel(panel)){
39198             this.showPanel(panel);
39199             return panel;
39200         }
39201         panel.setRegion(this);
39202         this.panels.add(panel);
39203        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39204             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39205             // and hide them... ???
39206             this.bodyEl.dom.appendChild(panel.getEl().dom);
39207             if(panel.background !== true){
39208                 this.setActivePanel(panel);
39209             }
39210             this.fireEvent("paneladded", this, panel);
39211             return panel;
39212         }
39213         */
39214         if(!this.tabs){
39215             this.initTabs();
39216         }else{
39217             this.initPanelAsTab(panel);
39218         }
39219         
39220         
39221         if(panel.background !== true){
39222             this.tabs.activate(panel.getEl().id);
39223         }
39224         this.fireEvent("paneladded", this, panel);
39225         return panel;
39226     },
39227
39228     /**
39229      * Hides the tab for the specified panel.
39230      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39231      */
39232     hidePanel : function(panel){
39233         if(this.tabs && (panel = this.getPanel(panel))){
39234             this.tabs.hideTab(panel.getEl().id);
39235         }
39236     },
39237
39238     /**
39239      * Unhides the tab for a previously hidden panel.
39240      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39241      */
39242     unhidePanel : function(panel){
39243         if(this.tabs && (panel = this.getPanel(panel))){
39244             this.tabs.unhideTab(panel.getEl().id);
39245         }
39246     },
39247
39248     clearPanels : function(){
39249         while(this.panels.getCount() > 0){
39250              this.remove(this.panels.first());
39251         }
39252     },
39253
39254     /**
39255      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39256      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39257      * @param {Boolean} preservePanel Overrides the config preservePanel option
39258      * @return {Roo.ContentPanel} The panel that was removed
39259      */
39260     remove : function(panel, preservePanel)
39261     {
39262         panel = this.getPanel(panel);
39263         if(!panel){
39264             return null;
39265         }
39266         var e = {};
39267         this.fireEvent("beforeremove", this, panel, e);
39268         if(e.cancel === true){
39269             return null;
39270         }
39271         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39272         var panelId = panel.getId();
39273         this.panels.removeKey(panelId);
39274         if(preservePanel){
39275             document.body.appendChild(panel.getEl().dom);
39276         }
39277         if(this.tabs){
39278             this.tabs.removeTab(panel.getEl().id);
39279         }else if (!preservePanel){
39280             this.bodyEl.dom.removeChild(panel.getEl().dom);
39281         }
39282         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39283             var p = this.panels.first();
39284             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39285             tempEl.appendChild(p.getEl().dom);
39286             this.bodyEl.update("");
39287             this.bodyEl.dom.appendChild(p.getEl().dom);
39288             tempEl = null;
39289             this.updateTitle(p.getTitle());
39290             this.tabs = null;
39291             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39292             this.setActivePanel(p);
39293         }
39294         panel.setRegion(null);
39295         if(this.activePanel == panel){
39296             this.activePanel = null;
39297         }
39298         if(this.config.autoDestroy !== false && preservePanel !== true){
39299             try{panel.destroy();}catch(e){}
39300         }
39301         this.fireEvent("panelremoved", this, panel);
39302         return panel;
39303     },
39304
39305     /**
39306      * Returns the TabPanel component used by this region
39307      * @return {Roo.TabPanel}
39308      */
39309     getTabs : function(){
39310         return this.tabs;
39311     },
39312
39313     createTool : function(parentEl, className){
39314         var btn = Roo.DomHelper.append(parentEl, {
39315             tag: "div",
39316             cls: "x-layout-tools-button",
39317             children: [ {
39318                 tag: "div",
39319                 cls: "roo-layout-tools-button-inner " + className,
39320                 html: "&#160;"
39321             }]
39322         }, true);
39323         btn.addClassOnOver("roo-layout-tools-button-over");
39324         return btn;
39325     }
39326 });/*
39327  * Based on:
39328  * Ext JS Library 1.1.1
39329  * Copyright(c) 2006-2007, Ext JS, LLC.
39330  *
39331  * Originally Released Under LGPL - original licence link has changed is not relivant.
39332  *
39333  * Fork - LGPL
39334  * <script type="text/javascript">
39335  */
39336  
39337
39338
39339 /**
39340  * @class Roo.SplitLayoutRegion
39341  * @extends Roo.LayoutRegion
39342  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39343  */
39344 Roo.bootstrap.layout.Split = function(config){
39345     this.cursor = config.cursor;
39346     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39347 };
39348
39349 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39350 {
39351     splitTip : "Drag to resize.",
39352     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39353     useSplitTips : false,
39354
39355     applyConfig : function(config){
39356         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39357     },
39358     
39359     onRender : function(ctr,pos) {
39360         
39361         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39362         if(!this.config.split){
39363             return;
39364         }
39365         if(!this.split){
39366             
39367             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39368                             tag: "div",
39369                             id: this.el.id + "-split",
39370                             cls: "roo-layout-split roo-layout-split-"+this.position,
39371                             html: "&#160;"
39372             });
39373             /** The SplitBar for this region 
39374             * @type Roo.SplitBar */
39375             // does not exist yet...
39376             Roo.log([this.position, this.orientation]);
39377             
39378             this.split = new Roo.bootstrap.SplitBar({
39379                 dragElement : splitEl,
39380                 resizingElement: this.el,
39381                 orientation : this.orientation
39382             });
39383             
39384             this.split.on("moved", this.onSplitMove, this);
39385             this.split.useShim = this.config.useShim === true;
39386             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39387             if(this.useSplitTips){
39388                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39389             }
39390             //if(config.collapsible){
39391             //    this.split.el.on("dblclick", this.collapse,  this);
39392             //}
39393         }
39394         if(typeof this.config.minSize != "undefined"){
39395             this.split.minSize = this.config.minSize;
39396         }
39397         if(typeof this.config.maxSize != "undefined"){
39398             this.split.maxSize = this.config.maxSize;
39399         }
39400         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39401             this.hideSplitter();
39402         }
39403         
39404     },
39405
39406     getHMaxSize : function(){
39407          var cmax = this.config.maxSize || 10000;
39408          var center = this.mgr.getRegion("center");
39409          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39410     },
39411
39412     getVMaxSize : function(){
39413          var cmax = this.config.maxSize || 10000;
39414          var center = this.mgr.getRegion("center");
39415          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39416     },
39417
39418     onSplitMove : function(split, newSize){
39419         this.fireEvent("resized", this, newSize);
39420     },
39421     
39422     /** 
39423      * Returns the {@link Roo.SplitBar} for this region.
39424      * @return {Roo.SplitBar}
39425      */
39426     getSplitBar : function(){
39427         return this.split;
39428     },
39429     
39430     hide : function(){
39431         this.hideSplitter();
39432         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39433     },
39434
39435     hideSplitter : function(){
39436         if(this.split){
39437             this.split.el.setLocation(-2000,-2000);
39438             this.split.el.hide();
39439         }
39440     },
39441
39442     show : function(){
39443         if(this.split){
39444             this.split.el.show();
39445         }
39446         Roo.bootstrap.layout.Split.superclass.show.call(this);
39447     },
39448     
39449     beforeSlide: function(){
39450         if(Roo.isGecko){// firefox overflow auto bug workaround
39451             this.bodyEl.clip();
39452             if(this.tabs) {
39453                 this.tabs.bodyEl.clip();
39454             }
39455             if(this.activePanel){
39456                 this.activePanel.getEl().clip();
39457                 
39458                 if(this.activePanel.beforeSlide){
39459                     this.activePanel.beforeSlide();
39460                 }
39461             }
39462         }
39463     },
39464     
39465     afterSlide : function(){
39466         if(Roo.isGecko){// firefox overflow auto bug workaround
39467             this.bodyEl.unclip();
39468             if(this.tabs) {
39469                 this.tabs.bodyEl.unclip();
39470             }
39471             if(this.activePanel){
39472                 this.activePanel.getEl().unclip();
39473                 if(this.activePanel.afterSlide){
39474                     this.activePanel.afterSlide();
39475                 }
39476             }
39477         }
39478     },
39479
39480     initAutoHide : function(){
39481         if(this.autoHide !== false){
39482             if(!this.autoHideHd){
39483                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39484                 this.autoHideHd = {
39485                     "mouseout": function(e){
39486                         if(!e.within(this.el, true)){
39487                             st.delay(500);
39488                         }
39489                     },
39490                     "mouseover" : function(e){
39491                         st.cancel();
39492                     },
39493                     scope : this
39494                 };
39495             }
39496             this.el.on(this.autoHideHd);
39497         }
39498     },
39499
39500     clearAutoHide : function(){
39501         if(this.autoHide !== false){
39502             this.el.un("mouseout", this.autoHideHd.mouseout);
39503             this.el.un("mouseover", this.autoHideHd.mouseover);
39504         }
39505     },
39506
39507     clearMonitor : function(){
39508         Roo.get(document).un("click", this.slideInIf, this);
39509     },
39510
39511     // these names are backwards but not changed for compat
39512     slideOut : function(){
39513         if(this.isSlid || this.el.hasActiveFx()){
39514             return;
39515         }
39516         this.isSlid = true;
39517         if(this.collapseBtn){
39518             this.collapseBtn.hide();
39519         }
39520         this.closeBtnState = this.closeBtn.getStyle('display');
39521         this.closeBtn.hide();
39522         if(this.stickBtn){
39523             this.stickBtn.show();
39524         }
39525         this.el.show();
39526         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39527         this.beforeSlide();
39528         this.el.setStyle("z-index", 10001);
39529         this.el.slideIn(this.getSlideAnchor(), {
39530             callback: function(){
39531                 this.afterSlide();
39532                 this.initAutoHide();
39533                 Roo.get(document).on("click", this.slideInIf, this);
39534                 this.fireEvent("slideshow", this);
39535             },
39536             scope: this,
39537             block: true
39538         });
39539     },
39540
39541     afterSlideIn : function(){
39542         this.clearAutoHide();
39543         this.isSlid = false;
39544         this.clearMonitor();
39545         this.el.setStyle("z-index", "");
39546         if(this.collapseBtn){
39547             this.collapseBtn.show();
39548         }
39549         this.closeBtn.setStyle('display', this.closeBtnState);
39550         if(this.stickBtn){
39551             this.stickBtn.hide();
39552         }
39553         this.fireEvent("slidehide", this);
39554     },
39555
39556     slideIn : function(cb){
39557         if(!this.isSlid || this.el.hasActiveFx()){
39558             Roo.callback(cb);
39559             return;
39560         }
39561         this.isSlid = false;
39562         this.beforeSlide();
39563         this.el.slideOut(this.getSlideAnchor(), {
39564             callback: function(){
39565                 this.el.setLeftTop(-10000, -10000);
39566                 this.afterSlide();
39567                 this.afterSlideIn();
39568                 Roo.callback(cb);
39569             },
39570             scope: this,
39571             block: true
39572         });
39573     },
39574     
39575     slideInIf : function(e){
39576         if(!e.within(this.el)){
39577             this.slideIn();
39578         }
39579     },
39580
39581     animateCollapse : function(){
39582         this.beforeSlide();
39583         this.el.setStyle("z-index", 20000);
39584         var anchor = this.getSlideAnchor();
39585         this.el.slideOut(anchor, {
39586             callback : function(){
39587                 this.el.setStyle("z-index", "");
39588                 this.collapsedEl.slideIn(anchor, {duration:.3});
39589                 this.afterSlide();
39590                 this.el.setLocation(-10000,-10000);
39591                 this.el.hide();
39592                 this.fireEvent("collapsed", this);
39593             },
39594             scope: this,
39595             block: true
39596         });
39597     },
39598
39599     animateExpand : function(){
39600         this.beforeSlide();
39601         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39602         this.el.setStyle("z-index", 20000);
39603         this.collapsedEl.hide({
39604             duration:.1
39605         });
39606         this.el.slideIn(this.getSlideAnchor(), {
39607             callback : function(){
39608                 this.el.setStyle("z-index", "");
39609                 this.afterSlide();
39610                 if(this.split){
39611                     this.split.el.show();
39612                 }
39613                 this.fireEvent("invalidated", this);
39614                 this.fireEvent("expanded", this);
39615             },
39616             scope: this,
39617             block: true
39618         });
39619     },
39620
39621     anchors : {
39622         "west" : "left",
39623         "east" : "right",
39624         "north" : "top",
39625         "south" : "bottom"
39626     },
39627
39628     sanchors : {
39629         "west" : "l",
39630         "east" : "r",
39631         "north" : "t",
39632         "south" : "b"
39633     },
39634
39635     canchors : {
39636         "west" : "tl-tr",
39637         "east" : "tr-tl",
39638         "north" : "tl-bl",
39639         "south" : "bl-tl"
39640     },
39641
39642     getAnchor : function(){
39643         return this.anchors[this.position];
39644     },
39645
39646     getCollapseAnchor : function(){
39647         return this.canchors[this.position];
39648     },
39649
39650     getSlideAnchor : function(){
39651         return this.sanchors[this.position];
39652     },
39653
39654     getAlignAdj : function(){
39655         var cm = this.cmargins;
39656         switch(this.position){
39657             case "west":
39658                 return [0, 0];
39659             break;
39660             case "east":
39661                 return [0, 0];
39662             break;
39663             case "north":
39664                 return [0, 0];
39665             break;
39666             case "south":
39667                 return [0, 0];
39668             break;
39669         }
39670     },
39671
39672     getExpandAdj : function(){
39673         var c = this.collapsedEl, cm = this.cmargins;
39674         switch(this.position){
39675             case "west":
39676                 return [-(cm.right+c.getWidth()+cm.left), 0];
39677             break;
39678             case "east":
39679                 return [cm.right+c.getWidth()+cm.left, 0];
39680             break;
39681             case "north":
39682                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39683             break;
39684             case "south":
39685                 return [0, cm.top+cm.bottom+c.getHeight()];
39686             break;
39687         }
39688     }
39689 });/*
39690  * Based on:
39691  * Ext JS Library 1.1.1
39692  * Copyright(c) 2006-2007, Ext JS, LLC.
39693  *
39694  * Originally Released Under LGPL - original licence link has changed is not relivant.
39695  *
39696  * Fork - LGPL
39697  * <script type="text/javascript">
39698  */
39699 /*
39700  * These classes are private internal classes
39701  */
39702 Roo.bootstrap.layout.Center = function(config){
39703     config.region = "center";
39704     Roo.bootstrap.layout.Region.call(this, config);
39705     this.visible = true;
39706     this.minWidth = config.minWidth || 20;
39707     this.minHeight = config.minHeight || 20;
39708 };
39709
39710 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39711     hide : function(){
39712         // center panel can't be hidden
39713     },
39714     
39715     show : function(){
39716         // center panel can't be hidden
39717     },
39718     
39719     getMinWidth: function(){
39720         return this.minWidth;
39721     },
39722     
39723     getMinHeight: function(){
39724         return this.minHeight;
39725     }
39726 });
39727
39728
39729
39730
39731  
39732
39733
39734
39735
39736
39737
39738 Roo.bootstrap.layout.North = function(config)
39739 {
39740     config.region = 'north';
39741     config.cursor = 'n-resize';
39742     
39743     Roo.bootstrap.layout.Split.call(this, config);
39744     
39745     
39746     if(this.split){
39747         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39748         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39749         this.split.el.addClass("roo-layout-split-v");
39750     }
39751     //var size = config.initialSize || config.height;
39752     //if(this.el && typeof size != "undefined"){
39753     //    this.el.setHeight(size);
39754     //}
39755 };
39756 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39757 {
39758     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39759      
39760      
39761     onRender : function(ctr, pos)
39762     {
39763         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39764         var size = this.config.initialSize || this.config.height;
39765         if(this.el && typeof size != "undefined"){
39766             this.el.setHeight(size);
39767         }
39768     
39769     },
39770     
39771     getBox : function(){
39772         if(this.collapsed){
39773             return this.collapsedEl.getBox();
39774         }
39775         var box = this.el.getBox();
39776         if(this.split){
39777             box.height += this.split.el.getHeight();
39778         }
39779         return box;
39780     },
39781     
39782     updateBox : function(box){
39783         if(this.split && !this.collapsed){
39784             box.height -= this.split.el.getHeight();
39785             this.split.el.setLeft(box.x);
39786             this.split.el.setTop(box.y+box.height);
39787             this.split.el.setWidth(box.width);
39788         }
39789         if(this.collapsed){
39790             this.updateBody(box.width, null);
39791         }
39792         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39793     }
39794 });
39795
39796
39797
39798
39799
39800 Roo.bootstrap.layout.South = function(config){
39801     config.region = 'south';
39802     config.cursor = 's-resize';
39803     Roo.bootstrap.layout.Split.call(this, config);
39804     if(this.split){
39805         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39806         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39807         this.split.el.addClass("roo-layout-split-v");
39808     }
39809     
39810 };
39811
39812 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39813     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39814     
39815     onRender : function(ctr, pos)
39816     {
39817         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39818         var size = this.config.initialSize || this.config.height;
39819         if(this.el && typeof size != "undefined"){
39820             this.el.setHeight(size);
39821         }
39822     
39823     },
39824     
39825     getBox : function(){
39826         if(this.collapsed){
39827             return this.collapsedEl.getBox();
39828         }
39829         var box = this.el.getBox();
39830         if(this.split){
39831             var sh = this.split.el.getHeight();
39832             box.height += sh;
39833             box.y -= sh;
39834         }
39835         return box;
39836     },
39837     
39838     updateBox : function(box){
39839         if(this.split && !this.collapsed){
39840             var sh = this.split.el.getHeight();
39841             box.height -= sh;
39842             box.y += sh;
39843             this.split.el.setLeft(box.x);
39844             this.split.el.setTop(box.y-sh);
39845             this.split.el.setWidth(box.width);
39846         }
39847         if(this.collapsed){
39848             this.updateBody(box.width, null);
39849         }
39850         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39851     }
39852 });
39853
39854 Roo.bootstrap.layout.East = function(config){
39855     config.region = "east";
39856     config.cursor = "e-resize";
39857     Roo.bootstrap.layout.Split.call(this, config);
39858     if(this.split){
39859         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39860         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39861         this.split.el.addClass("roo-layout-split-h");
39862     }
39863     
39864 };
39865 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39866     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39867     
39868     onRender : function(ctr, pos)
39869     {
39870         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39871         var size = this.config.initialSize || this.config.width;
39872         if(this.el && typeof size != "undefined"){
39873             this.el.setWidth(size);
39874         }
39875     
39876     },
39877     
39878     getBox : function(){
39879         if(this.collapsed){
39880             return this.collapsedEl.getBox();
39881         }
39882         var box = this.el.getBox();
39883         if(this.split){
39884             var sw = this.split.el.getWidth();
39885             box.width += sw;
39886             box.x -= sw;
39887         }
39888         return box;
39889     },
39890
39891     updateBox : function(box){
39892         if(this.split && !this.collapsed){
39893             var sw = this.split.el.getWidth();
39894             box.width -= sw;
39895             this.split.el.setLeft(box.x);
39896             this.split.el.setTop(box.y);
39897             this.split.el.setHeight(box.height);
39898             box.x += sw;
39899         }
39900         if(this.collapsed){
39901             this.updateBody(null, box.height);
39902         }
39903         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39904     }
39905 });
39906
39907 Roo.bootstrap.layout.West = function(config){
39908     config.region = "west";
39909     config.cursor = "w-resize";
39910     
39911     Roo.bootstrap.layout.Split.call(this, config);
39912     if(this.split){
39913         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39914         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39915         this.split.el.addClass("roo-layout-split-h");
39916     }
39917     
39918 };
39919 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39920     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39921     
39922     onRender: function(ctr, pos)
39923     {
39924         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39925         var size = this.config.initialSize || this.config.width;
39926         if(typeof size != "undefined"){
39927             this.el.setWidth(size);
39928         }
39929     },
39930     
39931     getBox : function(){
39932         if(this.collapsed){
39933             return this.collapsedEl.getBox();
39934         }
39935         var box = this.el.getBox();
39936         if (box.width == 0) {
39937             box.width = this.config.width; // kludge?
39938         }
39939         if(this.split){
39940             box.width += this.split.el.getWidth();
39941         }
39942         return box;
39943     },
39944     
39945     updateBox : function(box){
39946         if(this.split && !this.collapsed){
39947             var sw = this.split.el.getWidth();
39948             box.width -= sw;
39949             this.split.el.setLeft(box.x+box.width);
39950             this.split.el.setTop(box.y);
39951             this.split.el.setHeight(box.height);
39952         }
39953         if(this.collapsed){
39954             this.updateBody(null, box.height);
39955         }
39956         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39957     }
39958 });Roo.namespace("Roo.bootstrap.panel");/*
39959  * Based on:
39960  * Ext JS Library 1.1.1
39961  * Copyright(c) 2006-2007, Ext JS, LLC.
39962  *
39963  * Originally Released Under LGPL - original licence link has changed is not relivant.
39964  *
39965  * Fork - LGPL
39966  * <script type="text/javascript">
39967  */
39968 /**
39969  * @class Roo.ContentPanel
39970  * @extends Roo.util.Observable
39971  * A basic ContentPanel element.
39972  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39973  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39974  * @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
39975  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39976  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39977  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39978  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39979  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39980  * @cfg {String} title          The title for this panel
39981  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39982  * @cfg {String} url            Calls {@link #setUrl} with this value
39983  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39984  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39985  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39986  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39987  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39988  * @cfg {Boolean} badges render the badges
39989  * @cfg {String} cls  extra classes to use  
39990  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39991
39992  * @constructor
39993  * Create a new ContentPanel.
39994  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39995  * @param {String/Object} config A string to set only the title or a config object
39996  * @param {String} content (optional) Set the HTML content for this panel
39997  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39998  */
39999 Roo.bootstrap.panel.Content = function( config){
40000     
40001     this.tpl = config.tpl || false;
40002     
40003     var el = config.el;
40004     var content = config.content;
40005
40006     if(config.autoCreate){ // xtype is available if this is called from factory
40007         el = Roo.id();
40008     }
40009     this.el = Roo.get(el);
40010     if(!this.el && config && config.autoCreate){
40011         if(typeof config.autoCreate == "object"){
40012             if(!config.autoCreate.id){
40013                 config.autoCreate.id = config.id||el;
40014             }
40015             this.el = Roo.DomHelper.append(document.body,
40016                         config.autoCreate, true);
40017         }else{
40018             var elcfg =  {
40019                 tag: "div",
40020                 cls: (config.cls || '') +
40021                     (config.background ? ' bg-' + config.background : '') +
40022                     " roo-layout-inactive-content",
40023                 id: config.id||el
40024             };
40025             if (config.iframe) {
40026                 elcfg.cn = [
40027                     {
40028                         tag : 'iframe',
40029                         style : 'border: 0px',
40030                         src : 'about:blank'
40031                     }
40032                 ];
40033             }
40034               
40035             if (config.html) {
40036                 elcfg.html = config.html;
40037                 
40038             }
40039                         
40040             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40041             if (config.iframe) {
40042                 this.iframeEl = this.el.select('iframe',true).first();
40043             }
40044             
40045         }
40046     } 
40047     this.closable = false;
40048     this.loaded = false;
40049     this.active = false;
40050    
40051       
40052     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40053         
40054         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40055         
40056         this.wrapEl = this.el; //this.el.wrap();
40057         var ti = [];
40058         if (config.toolbar.items) {
40059             ti = config.toolbar.items ;
40060             delete config.toolbar.items ;
40061         }
40062         
40063         var nitems = [];
40064         this.toolbar.render(this.wrapEl, 'before');
40065         for(var i =0;i < ti.length;i++) {
40066           //  Roo.log(['add child', items[i]]);
40067             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40068         }
40069         this.toolbar.items = nitems;
40070         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40071         delete config.toolbar;
40072         
40073     }
40074     /*
40075     // xtype created footer. - not sure if will work as we normally have to render first..
40076     if (this.footer && !this.footer.el && this.footer.xtype) {
40077         if (!this.wrapEl) {
40078             this.wrapEl = this.el.wrap();
40079         }
40080     
40081         this.footer.container = this.wrapEl.createChild();
40082          
40083         this.footer = Roo.factory(this.footer, Roo);
40084         
40085     }
40086     */
40087     
40088      if(typeof config == "string"){
40089         this.title = config;
40090     }else{
40091         Roo.apply(this, config);
40092     }
40093     
40094     if(this.resizeEl){
40095         this.resizeEl = Roo.get(this.resizeEl, true);
40096     }else{
40097         this.resizeEl = this.el;
40098     }
40099     // handle view.xtype
40100     
40101  
40102     
40103     
40104     this.addEvents({
40105         /**
40106          * @event activate
40107          * Fires when this panel is activated. 
40108          * @param {Roo.ContentPanel} this
40109          */
40110         "activate" : true,
40111         /**
40112          * @event deactivate
40113          * Fires when this panel is activated. 
40114          * @param {Roo.ContentPanel} this
40115          */
40116         "deactivate" : true,
40117
40118         /**
40119          * @event resize
40120          * Fires when this panel is resized if fitToFrame is true.
40121          * @param {Roo.ContentPanel} this
40122          * @param {Number} width The width after any component adjustments
40123          * @param {Number} height The height after any component adjustments
40124          */
40125         "resize" : true,
40126         
40127          /**
40128          * @event render
40129          * Fires when this tab is created
40130          * @param {Roo.ContentPanel} this
40131          */
40132         "render" : true
40133         
40134         
40135         
40136     });
40137     
40138
40139     
40140     
40141     if(this.autoScroll && !this.iframe){
40142         this.resizeEl.setStyle("overflow", "auto");
40143     } else {
40144         // fix randome scrolling
40145         //this.el.on('scroll', function() {
40146         //    Roo.log('fix random scolling');
40147         //    this.scrollTo('top',0); 
40148         //});
40149     }
40150     content = content || this.content;
40151     if(content){
40152         this.setContent(content);
40153     }
40154     if(config && config.url){
40155         this.setUrl(this.url, this.params, this.loadOnce);
40156     }
40157     
40158     
40159     
40160     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40161     
40162     if (this.view && typeof(this.view.xtype) != 'undefined') {
40163         this.view.el = this.el.appendChild(document.createElement("div"));
40164         this.view = Roo.factory(this.view); 
40165         this.view.render  &&  this.view.render(false, '');  
40166     }
40167     
40168     
40169     this.fireEvent('render', this);
40170 };
40171
40172 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40173     
40174     cls : '',
40175     background : '',
40176     
40177     tabTip : '',
40178     
40179     iframe : false,
40180     iframeEl : false,
40181     
40182     setRegion : function(region){
40183         this.region = region;
40184         this.setActiveClass(region && !this.background);
40185     },
40186     
40187     
40188     setActiveClass: function(state)
40189     {
40190         if(state){
40191            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40192            this.el.setStyle('position','relative');
40193         }else{
40194            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40195            this.el.setStyle('position', 'absolute');
40196         } 
40197     },
40198     
40199     /**
40200      * Returns the toolbar for this Panel if one was configured. 
40201      * @return {Roo.Toolbar} 
40202      */
40203     getToolbar : function(){
40204         return this.toolbar;
40205     },
40206     
40207     setActiveState : function(active)
40208     {
40209         this.active = active;
40210         this.setActiveClass(active);
40211         if(!active){
40212             if(this.fireEvent("deactivate", this) === false){
40213                 return false;
40214             }
40215             return true;
40216         }
40217         this.fireEvent("activate", this);
40218         return true;
40219     },
40220     /**
40221      * Updates this panel's element (not for iframe)
40222      * @param {String} content The new content
40223      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40224     */
40225     setContent : function(content, loadScripts){
40226         if (this.iframe) {
40227             return;
40228         }
40229         
40230         this.el.update(content, loadScripts);
40231     },
40232
40233     ignoreResize : function(w, h){
40234         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40235             return true;
40236         }else{
40237             this.lastSize = {width: w, height: h};
40238             return false;
40239         }
40240     },
40241     /**
40242      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40243      * @return {Roo.UpdateManager} The UpdateManager
40244      */
40245     getUpdateManager : function(){
40246         if (this.iframe) {
40247             return false;
40248         }
40249         return this.el.getUpdateManager();
40250     },
40251      /**
40252      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40253      * Does not work with IFRAME contents
40254      * @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:
40255 <pre><code>
40256 panel.load({
40257     url: "your-url.php",
40258     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40259     callback: yourFunction,
40260     scope: yourObject, //(optional scope)
40261     discardUrl: false,
40262     nocache: false,
40263     text: "Loading...",
40264     timeout: 30,
40265     scripts: false
40266 });
40267 </code></pre>
40268      
40269      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40270      * 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.
40271      * @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}
40272      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40273      * @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.
40274      * @return {Roo.ContentPanel} this
40275      */
40276     load : function(){
40277         
40278         if (this.iframe) {
40279             return this;
40280         }
40281         
40282         var um = this.el.getUpdateManager();
40283         um.update.apply(um, arguments);
40284         return this;
40285     },
40286
40287
40288     /**
40289      * 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.
40290      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40291      * @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)
40292      * @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)
40293      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40294      */
40295     setUrl : function(url, params, loadOnce){
40296         if (this.iframe) {
40297             this.iframeEl.dom.src = url;
40298             return false;
40299         }
40300         
40301         if(this.refreshDelegate){
40302             this.removeListener("activate", this.refreshDelegate);
40303         }
40304         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40305         this.on("activate", this.refreshDelegate);
40306         return this.el.getUpdateManager();
40307     },
40308     
40309     _handleRefresh : function(url, params, loadOnce){
40310         if(!loadOnce || !this.loaded){
40311             var updater = this.el.getUpdateManager();
40312             updater.update(url, params, this._setLoaded.createDelegate(this));
40313         }
40314     },
40315     
40316     _setLoaded : function(){
40317         this.loaded = true;
40318     }, 
40319     
40320     /**
40321      * Returns this panel's id
40322      * @return {String} 
40323      */
40324     getId : function(){
40325         return this.el.id;
40326     },
40327     
40328     /** 
40329      * Returns this panel's element - used by regiosn to add.
40330      * @return {Roo.Element} 
40331      */
40332     getEl : function(){
40333         return this.wrapEl || this.el;
40334     },
40335     
40336    
40337     
40338     adjustForComponents : function(width, height)
40339     {
40340         //Roo.log('adjustForComponents ');
40341         if(this.resizeEl != this.el){
40342             width -= this.el.getFrameWidth('lr');
40343             height -= this.el.getFrameWidth('tb');
40344         }
40345         if(this.toolbar){
40346             var te = this.toolbar.getEl();
40347             te.setWidth(width);
40348             height -= te.getHeight();
40349         }
40350         if(this.footer){
40351             var te = this.footer.getEl();
40352             te.setWidth(width);
40353             height -= te.getHeight();
40354         }
40355         
40356         
40357         if(this.adjustments){
40358             width += this.adjustments[0];
40359             height += this.adjustments[1];
40360         }
40361         return {"width": width, "height": height};
40362     },
40363     
40364     setSize : function(width, height){
40365         if(this.fitToFrame && !this.ignoreResize(width, height)){
40366             if(this.fitContainer && this.resizeEl != this.el){
40367                 this.el.setSize(width, height);
40368             }
40369             var size = this.adjustForComponents(width, height);
40370             if (this.iframe) {
40371                 this.iframeEl.setSize(width,height);
40372             }
40373             
40374             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40375             this.fireEvent('resize', this, size.width, size.height);
40376             
40377             
40378         }
40379     },
40380     
40381     /**
40382      * Returns this panel's title
40383      * @return {String} 
40384      */
40385     getTitle : function(){
40386         
40387         if (typeof(this.title) != 'object') {
40388             return this.title;
40389         }
40390         
40391         var t = '';
40392         for (var k in this.title) {
40393             if (!this.title.hasOwnProperty(k)) {
40394                 continue;
40395             }
40396             
40397             if (k.indexOf('-') >= 0) {
40398                 var s = k.split('-');
40399                 for (var i = 0; i<s.length; i++) {
40400                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40401                 }
40402             } else {
40403                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40404             }
40405         }
40406         return t;
40407     },
40408     
40409     /**
40410      * Set this panel's title
40411      * @param {String} title
40412      */
40413     setTitle : function(title){
40414         this.title = title;
40415         if(this.region){
40416             this.region.updatePanelTitle(this, title);
40417         }
40418     },
40419     
40420     /**
40421      * Returns true is this panel was configured to be closable
40422      * @return {Boolean} 
40423      */
40424     isClosable : function(){
40425         return this.closable;
40426     },
40427     
40428     beforeSlide : function(){
40429         this.el.clip();
40430         this.resizeEl.clip();
40431     },
40432     
40433     afterSlide : function(){
40434         this.el.unclip();
40435         this.resizeEl.unclip();
40436     },
40437     
40438     /**
40439      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40440      *   Will fail silently if the {@link #setUrl} method has not been called.
40441      *   This does not activate the panel, just updates its content.
40442      */
40443     refresh : function(){
40444         if(this.refreshDelegate){
40445            this.loaded = false;
40446            this.refreshDelegate();
40447         }
40448     },
40449     
40450     /**
40451      * Destroys this panel
40452      */
40453     destroy : function(){
40454         this.el.removeAllListeners();
40455         var tempEl = document.createElement("span");
40456         tempEl.appendChild(this.el.dom);
40457         tempEl.innerHTML = "";
40458         this.el.remove();
40459         this.el = null;
40460     },
40461     
40462     /**
40463      * form - if the content panel contains a form - this is a reference to it.
40464      * @type {Roo.form.Form}
40465      */
40466     form : false,
40467     /**
40468      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40469      *    This contains a reference to it.
40470      * @type {Roo.View}
40471      */
40472     view : false,
40473     
40474       /**
40475      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40476      * <pre><code>
40477
40478 layout.addxtype({
40479        xtype : 'Form',
40480        items: [ .... ]
40481    }
40482 );
40483
40484 </code></pre>
40485      * @param {Object} cfg Xtype definition of item to add.
40486      */
40487     
40488     
40489     getChildContainer: function () {
40490         return this.getEl();
40491     }
40492     
40493     
40494     /*
40495         var  ret = new Roo.factory(cfg);
40496         return ret;
40497         
40498         
40499         // add form..
40500         if (cfg.xtype.match(/^Form$/)) {
40501             
40502             var el;
40503             //if (this.footer) {
40504             //    el = this.footer.container.insertSibling(false, 'before');
40505             //} else {
40506                 el = this.el.createChild();
40507             //}
40508
40509             this.form = new  Roo.form.Form(cfg);
40510             
40511             
40512             if ( this.form.allItems.length) {
40513                 this.form.render(el.dom);
40514             }
40515             return this.form;
40516         }
40517         // should only have one of theses..
40518         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40519             // views.. should not be just added - used named prop 'view''
40520             
40521             cfg.el = this.el.appendChild(document.createElement("div"));
40522             // factory?
40523             
40524             var ret = new Roo.factory(cfg);
40525              
40526              ret.render && ret.render(false, ''); // render blank..
40527             this.view = ret;
40528             return ret;
40529         }
40530         return false;
40531     }
40532     \*/
40533 });
40534  
40535 /**
40536  * @class Roo.bootstrap.panel.Grid
40537  * @extends Roo.bootstrap.panel.Content
40538  * @constructor
40539  * Create a new GridPanel.
40540  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40541  * @param {Object} config A the config object
40542   
40543  */
40544
40545
40546
40547 Roo.bootstrap.panel.Grid = function(config)
40548 {
40549     
40550       
40551     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40552         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40553
40554     config.el = this.wrapper;
40555     //this.el = this.wrapper;
40556     
40557       if (config.container) {
40558         // ctor'ed from a Border/panel.grid
40559         
40560         
40561         this.wrapper.setStyle("overflow", "hidden");
40562         this.wrapper.addClass('roo-grid-container');
40563
40564     }
40565     
40566     
40567     if(config.toolbar){
40568         var tool_el = this.wrapper.createChild();    
40569         this.toolbar = Roo.factory(config.toolbar);
40570         var ti = [];
40571         if (config.toolbar.items) {
40572             ti = config.toolbar.items ;
40573             delete config.toolbar.items ;
40574         }
40575         
40576         var nitems = [];
40577         this.toolbar.render(tool_el);
40578         for(var i =0;i < ti.length;i++) {
40579           //  Roo.log(['add child', items[i]]);
40580             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40581         }
40582         this.toolbar.items = nitems;
40583         
40584         delete config.toolbar;
40585     }
40586     
40587     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40588     config.grid.scrollBody = true;;
40589     config.grid.monitorWindowResize = false; // turn off autosizing
40590     config.grid.autoHeight = false;
40591     config.grid.autoWidth = false;
40592     
40593     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40594     
40595     if (config.background) {
40596         // render grid on panel activation (if panel background)
40597         this.on('activate', function(gp) {
40598             if (!gp.grid.rendered) {
40599                 gp.grid.render(this.wrapper);
40600                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40601             }
40602         });
40603             
40604     } else {
40605         this.grid.render(this.wrapper);
40606         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40607
40608     }
40609     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40610     // ??? needed ??? config.el = this.wrapper;
40611     
40612     
40613     
40614   
40615     // xtype created footer. - not sure if will work as we normally have to render first..
40616     if (this.footer && !this.footer.el && this.footer.xtype) {
40617         
40618         var ctr = this.grid.getView().getFooterPanel(true);
40619         this.footer.dataSource = this.grid.dataSource;
40620         this.footer = Roo.factory(this.footer, Roo);
40621         this.footer.render(ctr);
40622         
40623     }
40624     
40625     
40626     
40627     
40628      
40629 };
40630
40631 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40632     getId : function(){
40633         return this.grid.id;
40634     },
40635     
40636     /**
40637      * Returns the grid for this panel
40638      * @return {Roo.bootstrap.Table} 
40639      */
40640     getGrid : function(){
40641         return this.grid;    
40642     },
40643     
40644     setSize : function(width, height){
40645         if(!this.ignoreResize(width, height)){
40646             var grid = this.grid;
40647             var size = this.adjustForComponents(width, height);
40648             // tfoot is not a footer?
40649           
40650             
40651             var gridel = grid.getGridEl();
40652             gridel.setSize(size.width, size.height);
40653             
40654             var tbd = grid.getGridEl().select('tbody', true).first();
40655             var thd = grid.getGridEl().select('thead',true).first();
40656             var tbf= grid.getGridEl().select('tfoot', true).first();
40657
40658             if (tbf) {
40659                 size.height -= tbf.getHeight();
40660             }
40661             if (thd) {
40662                 size.height -= thd.getHeight();
40663             }
40664             
40665             tbd.setSize(size.width, size.height );
40666             // this is for the account management tab -seems to work there.
40667             var thd = grid.getGridEl().select('thead',true).first();
40668             //if (tbd) {
40669             //    tbd.setSize(size.width, size.height - thd.getHeight());
40670             //}
40671              
40672             grid.autoSize();
40673         }
40674     },
40675      
40676     
40677     
40678     beforeSlide : function(){
40679         this.grid.getView().scroller.clip();
40680     },
40681     
40682     afterSlide : function(){
40683         this.grid.getView().scroller.unclip();
40684     },
40685     
40686     destroy : function(){
40687         this.grid.destroy();
40688         delete this.grid;
40689         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40690     }
40691 });
40692
40693 /**
40694  * @class Roo.bootstrap.panel.Nest
40695  * @extends Roo.bootstrap.panel.Content
40696  * @constructor
40697  * Create a new Panel, that can contain a layout.Border.
40698  * 
40699  * 
40700  * @param {Roo.BorderLayout} layout The layout for this panel
40701  * @param {String/Object} config A string to set only the title or a config object
40702  */
40703 Roo.bootstrap.panel.Nest = function(config)
40704 {
40705     // construct with only one argument..
40706     /* FIXME - implement nicer consturctors
40707     if (layout.layout) {
40708         config = layout;
40709         layout = config.layout;
40710         delete config.layout;
40711     }
40712     if (layout.xtype && !layout.getEl) {
40713         // then layout needs constructing..
40714         layout = Roo.factory(layout, Roo);
40715     }
40716     */
40717     
40718     config.el =  config.layout.getEl();
40719     
40720     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40721     
40722     config.layout.monitorWindowResize = false; // turn off autosizing
40723     this.layout = config.layout;
40724     this.layout.getEl().addClass("roo-layout-nested-layout");
40725     this.layout.parent = this;
40726     
40727     
40728     
40729     
40730 };
40731
40732 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40733
40734     setSize : function(width, height){
40735         if(!this.ignoreResize(width, height)){
40736             var size = this.adjustForComponents(width, height);
40737             var el = this.layout.getEl();
40738             if (size.height < 1) {
40739                 el.setWidth(size.width);   
40740             } else {
40741                 el.setSize(size.width, size.height);
40742             }
40743             var touch = el.dom.offsetWidth;
40744             this.layout.layout();
40745             // ie requires a double layout on the first pass
40746             if(Roo.isIE && !this.initialized){
40747                 this.initialized = true;
40748                 this.layout.layout();
40749             }
40750         }
40751     },
40752     
40753     // activate all subpanels if not currently active..
40754     
40755     setActiveState : function(active){
40756         this.active = active;
40757         this.setActiveClass(active);
40758         
40759         if(!active){
40760             this.fireEvent("deactivate", this);
40761             return;
40762         }
40763         
40764         this.fireEvent("activate", this);
40765         // not sure if this should happen before or after..
40766         if (!this.layout) {
40767             return; // should not happen..
40768         }
40769         var reg = false;
40770         for (var r in this.layout.regions) {
40771             reg = this.layout.getRegion(r);
40772             if (reg.getActivePanel()) {
40773                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40774                 reg.setActivePanel(reg.getActivePanel());
40775                 continue;
40776             }
40777             if (!reg.panels.length) {
40778                 continue;
40779             }
40780             reg.showPanel(reg.getPanel(0));
40781         }
40782         
40783         
40784         
40785         
40786     },
40787     
40788     /**
40789      * Returns the nested BorderLayout for this panel
40790      * @return {Roo.BorderLayout} 
40791      */
40792     getLayout : function(){
40793         return this.layout;
40794     },
40795     
40796      /**
40797      * Adds a xtype elements to the layout of the nested panel
40798      * <pre><code>
40799
40800 panel.addxtype({
40801        xtype : 'ContentPanel',
40802        region: 'west',
40803        items: [ .... ]
40804    }
40805 );
40806
40807 panel.addxtype({
40808         xtype : 'NestedLayoutPanel',
40809         region: 'west',
40810         layout: {
40811            center: { },
40812            west: { }   
40813         },
40814         items : [ ... list of content panels or nested layout panels.. ]
40815    }
40816 );
40817 </code></pre>
40818      * @param {Object} cfg Xtype definition of item to add.
40819      */
40820     addxtype : function(cfg) {
40821         return this.layout.addxtype(cfg);
40822     
40823     }
40824 });/*
40825  * Based on:
40826  * Ext JS Library 1.1.1
40827  * Copyright(c) 2006-2007, Ext JS, LLC.
40828  *
40829  * Originally Released Under LGPL - original licence link has changed is not relivant.
40830  *
40831  * Fork - LGPL
40832  * <script type="text/javascript">
40833  */
40834 /**
40835  * @class Roo.TabPanel
40836  * @extends Roo.util.Observable
40837  * A lightweight tab container.
40838  * <br><br>
40839  * Usage:
40840  * <pre><code>
40841 // basic tabs 1, built from existing content
40842 var tabs = new Roo.TabPanel("tabs1");
40843 tabs.addTab("script", "View Script");
40844 tabs.addTab("markup", "View Markup");
40845 tabs.activate("script");
40846
40847 // more advanced tabs, built from javascript
40848 var jtabs = new Roo.TabPanel("jtabs");
40849 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40850
40851 // set up the UpdateManager
40852 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40853 var updater = tab2.getUpdateManager();
40854 updater.setDefaultUrl("ajax1.htm");
40855 tab2.on('activate', updater.refresh, updater, true);
40856
40857 // Use setUrl for Ajax loading
40858 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40859 tab3.setUrl("ajax2.htm", null, true);
40860
40861 // Disabled tab
40862 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40863 tab4.disable();
40864
40865 jtabs.activate("jtabs-1");
40866  * </code></pre>
40867  * @constructor
40868  * Create a new TabPanel.
40869  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40870  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40871  */
40872 Roo.bootstrap.panel.Tabs = function(config){
40873     /**
40874     * The container element for this TabPanel.
40875     * @type Roo.Element
40876     */
40877     this.el = Roo.get(config.el);
40878     delete config.el;
40879     if(config){
40880         if(typeof config == "boolean"){
40881             this.tabPosition = config ? "bottom" : "top";
40882         }else{
40883             Roo.apply(this, config);
40884         }
40885     }
40886     
40887     if(this.tabPosition == "bottom"){
40888         // if tabs are at the bottom = create the body first.
40889         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40890         this.el.addClass("roo-tabs-bottom");
40891     }
40892     // next create the tabs holders
40893     
40894     if (this.tabPosition == "west"){
40895         
40896         var reg = this.region; // fake it..
40897         while (reg) {
40898             if (!reg.mgr.parent) {
40899                 break;
40900             }
40901             reg = reg.mgr.parent.region;
40902         }
40903         Roo.log("got nest?");
40904         Roo.log(reg);
40905         if (reg.mgr.getRegion('west')) {
40906             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40907             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40908             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40909             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40910             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40911         
40912             
40913         }
40914         
40915         
40916     } else {
40917      
40918         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40919         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40920         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40921         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40922     }
40923     
40924     
40925     if(Roo.isIE){
40926         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40927     }
40928     
40929     // finally - if tabs are at the top, then create the body last..
40930     if(this.tabPosition != "bottom"){
40931         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40932          * @type Roo.Element
40933          */
40934         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40935         this.el.addClass("roo-tabs-top");
40936     }
40937     this.items = [];
40938
40939     this.bodyEl.setStyle("position", "relative");
40940
40941     this.active = null;
40942     this.activateDelegate = this.activate.createDelegate(this);
40943
40944     this.addEvents({
40945         /**
40946          * @event tabchange
40947          * Fires when the active tab changes
40948          * @param {Roo.TabPanel} this
40949          * @param {Roo.TabPanelItem} activePanel The new active tab
40950          */
40951         "tabchange": true,
40952         /**
40953          * @event beforetabchange
40954          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40955          * @param {Roo.TabPanel} this
40956          * @param {Object} e Set cancel to true on this object to cancel the tab change
40957          * @param {Roo.TabPanelItem} tab The tab being changed to
40958          */
40959         "beforetabchange" : true
40960     });
40961
40962     Roo.EventManager.onWindowResize(this.onResize, this);
40963     this.cpad = this.el.getPadding("lr");
40964     this.hiddenCount = 0;
40965
40966
40967     // toolbar on the tabbar support...
40968     if (this.toolbar) {
40969         alert("no toolbar support yet");
40970         this.toolbar  = false;
40971         /*
40972         var tcfg = this.toolbar;
40973         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40974         this.toolbar = new Roo.Toolbar(tcfg);
40975         if (Roo.isSafari) {
40976             var tbl = tcfg.container.child('table', true);
40977             tbl.setAttribute('width', '100%');
40978         }
40979         */
40980         
40981     }
40982    
40983
40984
40985     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40986 };
40987
40988 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40989     /*
40990      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40991      */
40992     tabPosition : "top",
40993     /*
40994      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40995      */
40996     currentTabWidth : 0,
40997     /*
40998      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40999      */
41000     minTabWidth : 40,
41001     /*
41002      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41003      */
41004     maxTabWidth : 250,
41005     /*
41006      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41007      */
41008     preferredTabWidth : 175,
41009     /*
41010      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41011      */
41012     resizeTabs : false,
41013     /*
41014      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41015      */
41016     monitorResize : true,
41017     /*
41018      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41019      */
41020     toolbar : false,  // set by caller..
41021     
41022     region : false, /// set by caller
41023     
41024     disableTooltips : true, // not used yet...
41025
41026     /**
41027      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41028      * @param {String} id The id of the div to use <b>or create</b>
41029      * @param {String} text The text for the tab
41030      * @param {String} content (optional) Content to put in the TabPanelItem body
41031      * @param {Boolean} closable (optional) True to create a close icon on the tab
41032      * @return {Roo.TabPanelItem} The created TabPanelItem
41033      */
41034     addTab : function(id, text, content, closable, tpl)
41035     {
41036         var item = new Roo.bootstrap.panel.TabItem({
41037             panel: this,
41038             id : id,
41039             text : text,
41040             closable : closable,
41041             tpl : tpl
41042         });
41043         this.addTabItem(item);
41044         if(content){
41045             item.setContent(content);
41046         }
41047         return item;
41048     },
41049
41050     /**
41051      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41052      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41053      * @return {Roo.TabPanelItem}
41054      */
41055     getTab : function(id){
41056         return this.items[id];
41057     },
41058
41059     /**
41060      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41061      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41062      */
41063     hideTab : function(id){
41064         var t = this.items[id];
41065         if(!t.isHidden()){
41066            t.setHidden(true);
41067            this.hiddenCount++;
41068            this.autoSizeTabs();
41069         }
41070     },
41071
41072     /**
41073      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41074      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41075      */
41076     unhideTab : function(id){
41077         var t = this.items[id];
41078         if(t.isHidden()){
41079            t.setHidden(false);
41080            this.hiddenCount--;
41081            this.autoSizeTabs();
41082         }
41083     },
41084
41085     /**
41086      * Adds an existing {@link Roo.TabPanelItem}.
41087      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41088      */
41089     addTabItem : function(item)
41090     {
41091         this.items[item.id] = item;
41092         this.items.push(item);
41093         this.autoSizeTabs();
41094       //  if(this.resizeTabs){
41095     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41096   //         this.autoSizeTabs();
41097 //        }else{
41098 //            item.autoSize();
41099        // }
41100     },
41101
41102     /**
41103      * Removes a {@link Roo.TabPanelItem}.
41104      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41105      */
41106     removeTab : function(id){
41107         var items = this.items;
41108         var tab = items[id];
41109         if(!tab) { return; }
41110         var index = items.indexOf(tab);
41111         if(this.active == tab && items.length > 1){
41112             var newTab = this.getNextAvailable(index);
41113             if(newTab) {
41114                 newTab.activate();
41115             }
41116         }
41117         this.stripEl.dom.removeChild(tab.pnode.dom);
41118         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41119             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41120         }
41121         items.splice(index, 1);
41122         delete this.items[tab.id];
41123         tab.fireEvent("close", tab);
41124         tab.purgeListeners();
41125         this.autoSizeTabs();
41126     },
41127
41128     getNextAvailable : function(start){
41129         var items = this.items;
41130         var index = start;
41131         // look for a next tab that will slide over to
41132         // replace the one being removed
41133         while(index < items.length){
41134             var item = items[++index];
41135             if(item && !item.isHidden()){
41136                 return item;
41137             }
41138         }
41139         // if one isn't found select the previous tab (on the left)
41140         index = start;
41141         while(index >= 0){
41142             var item = items[--index];
41143             if(item && !item.isHidden()){
41144                 return item;
41145             }
41146         }
41147         return null;
41148     },
41149
41150     /**
41151      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41152      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41153      */
41154     disableTab : function(id){
41155         var tab = this.items[id];
41156         if(tab && this.active != tab){
41157             tab.disable();
41158         }
41159     },
41160
41161     /**
41162      * Enables a {@link Roo.TabPanelItem} that is disabled.
41163      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41164      */
41165     enableTab : function(id){
41166         var tab = this.items[id];
41167         tab.enable();
41168     },
41169
41170     /**
41171      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41172      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41173      * @return {Roo.TabPanelItem} The TabPanelItem.
41174      */
41175     activate : function(id)
41176     {
41177         //Roo.log('activite:'  + id);
41178         
41179         var tab = this.items[id];
41180         if(!tab){
41181             return null;
41182         }
41183         if(tab == this.active || tab.disabled){
41184             return tab;
41185         }
41186         var e = {};
41187         this.fireEvent("beforetabchange", this, e, tab);
41188         if(e.cancel !== true && !tab.disabled){
41189             if(this.active){
41190                 this.active.hide();
41191             }
41192             this.active = this.items[id];
41193             this.active.show();
41194             this.fireEvent("tabchange", this, this.active);
41195         }
41196         return tab;
41197     },
41198
41199     /**
41200      * Gets the active {@link Roo.TabPanelItem}.
41201      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41202      */
41203     getActiveTab : function(){
41204         return this.active;
41205     },
41206
41207     /**
41208      * Updates the tab body element to fit the height of the container element
41209      * for overflow scrolling
41210      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41211      */
41212     syncHeight : function(targetHeight){
41213         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41214         var bm = this.bodyEl.getMargins();
41215         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41216         this.bodyEl.setHeight(newHeight);
41217         return newHeight;
41218     },
41219
41220     onResize : function(){
41221         if(this.monitorResize){
41222             this.autoSizeTabs();
41223         }
41224     },
41225
41226     /**
41227      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41228      */
41229     beginUpdate : function(){
41230         this.updating = true;
41231     },
41232
41233     /**
41234      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41235      */
41236     endUpdate : function(){
41237         this.updating = false;
41238         this.autoSizeTabs();
41239     },
41240
41241     /**
41242      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41243      */
41244     autoSizeTabs : function()
41245     {
41246         var count = this.items.length;
41247         var vcount = count - this.hiddenCount;
41248         
41249         if (vcount < 2) {
41250             this.stripEl.hide();
41251         } else {
41252             this.stripEl.show();
41253         }
41254         
41255         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41256             return;
41257         }
41258         
41259         
41260         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41261         var availWidth = Math.floor(w / vcount);
41262         var b = this.stripBody;
41263         if(b.getWidth() > w){
41264             var tabs = this.items;
41265             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41266             if(availWidth < this.minTabWidth){
41267                 /*if(!this.sleft){    // incomplete scrolling code
41268                     this.createScrollButtons();
41269                 }
41270                 this.showScroll();
41271                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41272             }
41273         }else{
41274             if(this.currentTabWidth < this.preferredTabWidth){
41275                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41276             }
41277         }
41278     },
41279
41280     /**
41281      * Returns the number of tabs in this TabPanel.
41282      * @return {Number}
41283      */
41284      getCount : function(){
41285          return this.items.length;
41286      },
41287
41288     /**
41289      * Resizes all the tabs to the passed width
41290      * @param {Number} The new width
41291      */
41292     setTabWidth : function(width){
41293         this.currentTabWidth = width;
41294         for(var i = 0, len = this.items.length; i < len; i++) {
41295                 if(!this.items[i].isHidden()) {
41296                 this.items[i].setWidth(width);
41297             }
41298         }
41299     },
41300
41301     /**
41302      * Destroys this TabPanel
41303      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41304      */
41305     destroy : function(removeEl){
41306         Roo.EventManager.removeResizeListener(this.onResize, this);
41307         for(var i = 0, len = this.items.length; i < len; i++){
41308             this.items[i].purgeListeners();
41309         }
41310         if(removeEl === true){
41311             this.el.update("");
41312             this.el.remove();
41313         }
41314     },
41315     
41316     createStrip : function(container)
41317     {
41318         var strip = document.createElement("nav");
41319         strip.className = Roo.bootstrap.version == 4 ?
41320             "navbar-light bg-light" : 
41321             "navbar navbar-default"; //"x-tabs-wrap";
41322         container.appendChild(strip);
41323         return strip;
41324     },
41325     
41326     createStripList : function(strip)
41327     {
41328         // div wrapper for retard IE
41329         // returns the "tr" element.
41330         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41331         //'<div class="x-tabs-strip-wrap">'+
41332           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41333           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41334         return strip.firstChild; //.firstChild.firstChild.firstChild;
41335     },
41336     createBody : function(container)
41337     {
41338         var body = document.createElement("div");
41339         Roo.id(body, "tab-body");
41340         //Roo.fly(body).addClass("x-tabs-body");
41341         Roo.fly(body).addClass("tab-content");
41342         container.appendChild(body);
41343         return body;
41344     },
41345     createItemBody :function(bodyEl, id){
41346         var body = Roo.getDom(id);
41347         if(!body){
41348             body = document.createElement("div");
41349             body.id = id;
41350         }
41351         //Roo.fly(body).addClass("x-tabs-item-body");
41352         Roo.fly(body).addClass("tab-pane");
41353          bodyEl.insertBefore(body, bodyEl.firstChild);
41354         return body;
41355     },
41356     /** @private */
41357     createStripElements :  function(stripEl, text, closable, tpl)
41358     {
41359         var td = document.createElement("li"); // was td..
41360         td.className = 'nav-item';
41361         
41362         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41363         
41364         
41365         stripEl.appendChild(td);
41366         /*if(closable){
41367             td.className = "x-tabs-closable";
41368             if(!this.closeTpl){
41369                 this.closeTpl = new Roo.Template(
41370                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41371                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41372                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41373                 );
41374             }
41375             var el = this.closeTpl.overwrite(td, {"text": text});
41376             var close = el.getElementsByTagName("div")[0];
41377             var inner = el.getElementsByTagName("em")[0];
41378             return {"el": el, "close": close, "inner": inner};
41379         } else {
41380         */
41381         // not sure what this is..
41382 //            if(!this.tabTpl){
41383                 //this.tabTpl = new Roo.Template(
41384                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41385                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41386                 //);
41387 //                this.tabTpl = new Roo.Template(
41388 //                   '<a href="#">' +
41389 //                   '<span unselectable="on"' +
41390 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41391 //                            ' >{text}</span></a>'
41392 //                );
41393 //                
41394 //            }
41395
41396
41397             var template = tpl || this.tabTpl || false;
41398             
41399             if(!template){
41400                 template =  new Roo.Template(
41401                         Roo.bootstrap.version == 4 ? 
41402                             (
41403                                 '<a class="nav-link" href="#" unselectable="on"' +
41404                                      (this.disableTooltips ? '' : ' title="{text}"') +
41405                                      ' >{text}</a>'
41406                             ) : (
41407                                 '<a class="nav-link" href="#">' +
41408                                 '<span unselectable="on"' +
41409                                          (this.disableTooltips ? '' : ' title="{text}"') +
41410                                     ' >{text}</span></a>'
41411                             )
41412                 );
41413             }
41414             
41415             switch (typeof(template)) {
41416                 case 'object' :
41417                     break;
41418                 case 'string' :
41419                     template = new Roo.Template(template);
41420                     break;
41421                 default :
41422                     break;
41423             }
41424             
41425             var el = template.overwrite(td, {"text": text});
41426             
41427             var inner = el.getElementsByTagName("span")[0];
41428             
41429             return {"el": el, "inner": inner};
41430             
41431     }
41432         
41433     
41434 });
41435
41436 /**
41437  * @class Roo.TabPanelItem
41438  * @extends Roo.util.Observable
41439  * Represents an individual item (tab plus body) in a TabPanel.
41440  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41441  * @param {String} id The id of this TabPanelItem
41442  * @param {String} text The text for the tab of this TabPanelItem
41443  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41444  */
41445 Roo.bootstrap.panel.TabItem = function(config){
41446     /**
41447      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41448      * @type Roo.TabPanel
41449      */
41450     this.tabPanel = config.panel;
41451     /**
41452      * The id for this TabPanelItem
41453      * @type String
41454      */
41455     this.id = config.id;
41456     /** @private */
41457     this.disabled = false;
41458     /** @private */
41459     this.text = config.text;
41460     /** @private */
41461     this.loaded = false;
41462     this.closable = config.closable;
41463
41464     /**
41465      * The body element for this TabPanelItem.
41466      * @type Roo.Element
41467      */
41468     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41469     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41470     this.bodyEl.setStyle("display", "block");
41471     this.bodyEl.setStyle("zoom", "1");
41472     //this.hideAction();
41473
41474     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41475     /** @private */
41476     this.el = Roo.get(els.el);
41477     this.inner = Roo.get(els.inner, true);
41478      this.textEl = Roo.bootstrap.version == 4 ?
41479         this.el : Roo.get(this.el.dom.firstChild, true);
41480
41481     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41482     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41483
41484     
41485 //    this.el.on("mousedown", this.onTabMouseDown, this);
41486     this.el.on("click", this.onTabClick, this);
41487     /** @private */
41488     if(config.closable){
41489         var c = Roo.get(els.close, true);
41490         c.dom.title = this.closeText;
41491         c.addClassOnOver("close-over");
41492         c.on("click", this.closeClick, this);
41493      }
41494
41495     this.addEvents({
41496          /**
41497          * @event activate
41498          * Fires when this tab becomes the active tab.
41499          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41500          * @param {Roo.TabPanelItem} this
41501          */
41502         "activate": true,
41503         /**
41504          * @event beforeclose
41505          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41506          * @param {Roo.TabPanelItem} this
41507          * @param {Object} e Set cancel to true on this object to cancel the close.
41508          */
41509         "beforeclose": true,
41510         /**
41511          * @event close
41512          * Fires when this tab is closed.
41513          * @param {Roo.TabPanelItem} this
41514          */
41515          "close": true,
41516         /**
41517          * @event deactivate
41518          * Fires when this tab is no longer the active tab.
41519          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41520          * @param {Roo.TabPanelItem} this
41521          */
41522          "deactivate" : true
41523     });
41524     this.hidden = false;
41525
41526     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41527 };
41528
41529 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41530            {
41531     purgeListeners : function(){
41532        Roo.util.Observable.prototype.purgeListeners.call(this);
41533        this.el.removeAllListeners();
41534     },
41535     /**
41536      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41537      */
41538     show : function(){
41539         this.status_node.addClass("active");
41540         this.showAction();
41541         if(Roo.isOpera){
41542             this.tabPanel.stripWrap.repaint();
41543         }
41544         this.fireEvent("activate", this.tabPanel, this);
41545     },
41546
41547     /**
41548      * Returns true if this tab is the active tab.
41549      * @return {Boolean}
41550      */
41551     isActive : function(){
41552         return this.tabPanel.getActiveTab() == this;
41553     },
41554
41555     /**
41556      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41557      */
41558     hide : function(){
41559         this.status_node.removeClass("active");
41560         this.hideAction();
41561         this.fireEvent("deactivate", this.tabPanel, this);
41562     },
41563
41564     hideAction : function(){
41565         this.bodyEl.hide();
41566         this.bodyEl.setStyle("position", "absolute");
41567         this.bodyEl.setLeft("-20000px");
41568         this.bodyEl.setTop("-20000px");
41569     },
41570
41571     showAction : function(){
41572         this.bodyEl.setStyle("position", "relative");
41573         this.bodyEl.setTop("");
41574         this.bodyEl.setLeft("");
41575         this.bodyEl.show();
41576     },
41577
41578     /**
41579      * Set the tooltip for the tab.
41580      * @param {String} tooltip The tab's tooltip
41581      */
41582     setTooltip : function(text){
41583         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41584             this.textEl.dom.qtip = text;
41585             this.textEl.dom.removeAttribute('title');
41586         }else{
41587             this.textEl.dom.title = text;
41588         }
41589     },
41590
41591     onTabClick : function(e){
41592         e.preventDefault();
41593         this.tabPanel.activate(this.id);
41594     },
41595
41596     onTabMouseDown : function(e){
41597         e.preventDefault();
41598         this.tabPanel.activate(this.id);
41599     },
41600 /*
41601     getWidth : function(){
41602         return this.inner.getWidth();
41603     },
41604
41605     setWidth : function(width){
41606         var iwidth = width - this.linode.getPadding("lr");
41607         this.inner.setWidth(iwidth);
41608         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41609         this.linode.setWidth(width);
41610     },
41611 */
41612     /**
41613      * Show or hide the tab
41614      * @param {Boolean} hidden True to hide or false to show.
41615      */
41616     setHidden : function(hidden){
41617         this.hidden = hidden;
41618         this.linode.setStyle("display", hidden ? "none" : "");
41619     },
41620
41621     /**
41622      * Returns true if this tab is "hidden"
41623      * @return {Boolean}
41624      */
41625     isHidden : function(){
41626         return this.hidden;
41627     },
41628
41629     /**
41630      * Returns the text for this tab
41631      * @return {String}
41632      */
41633     getText : function(){
41634         return this.text;
41635     },
41636     /*
41637     autoSize : function(){
41638         //this.el.beginMeasure();
41639         this.textEl.setWidth(1);
41640         /*
41641          *  #2804 [new] Tabs in Roojs
41642          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41643          */
41644         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41645         //this.el.endMeasure();
41646     //},
41647
41648     /**
41649      * Sets the text for the tab (Note: this also sets the tooltip text)
41650      * @param {String} text The tab's text and tooltip
41651      */
41652     setText : function(text){
41653         this.text = text;
41654         this.textEl.update(text);
41655         this.setTooltip(text);
41656         //if(!this.tabPanel.resizeTabs){
41657         //    this.autoSize();
41658         //}
41659     },
41660     /**
41661      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41662      */
41663     activate : function(){
41664         this.tabPanel.activate(this.id);
41665     },
41666
41667     /**
41668      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41669      */
41670     disable : function(){
41671         if(this.tabPanel.active != this){
41672             this.disabled = true;
41673             this.status_node.addClass("disabled");
41674         }
41675     },
41676
41677     /**
41678      * Enables this TabPanelItem if it was previously disabled.
41679      */
41680     enable : function(){
41681         this.disabled = false;
41682         this.status_node.removeClass("disabled");
41683     },
41684
41685     /**
41686      * Sets the content for this TabPanelItem.
41687      * @param {String} content The content
41688      * @param {Boolean} loadScripts true to look for and load scripts
41689      */
41690     setContent : function(content, loadScripts){
41691         this.bodyEl.update(content, loadScripts);
41692     },
41693
41694     /**
41695      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41696      * @return {Roo.UpdateManager} The UpdateManager
41697      */
41698     getUpdateManager : function(){
41699         return this.bodyEl.getUpdateManager();
41700     },
41701
41702     /**
41703      * Set a URL to be used to load the content for this TabPanelItem.
41704      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41705      * @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)
41706      * @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)
41707      * @return {Roo.UpdateManager} The UpdateManager
41708      */
41709     setUrl : function(url, params, loadOnce){
41710         if(this.refreshDelegate){
41711             this.un('activate', this.refreshDelegate);
41712         }
41713         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41714         this.on("activate", this.refreshDelegate);
41715         return this.bodyEl.getUpdateManager();
41716     },
41717
41718     /** @private */
41719     _handleRefresh : function(url, params, loadOnce){
41720         if(!loadOnce || !this.loaded){
41721             var updater = this.bodyEl.getUpdateManager();
41722             updater.update(url, params, this._setLoaded.createDelegate(this));
41723         }
41724     },
41725
41726     /**
41727      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41728      *   Will fail silently if the setUrl method has not been called.
41729      *   This does not activate the panel, just updates its content.
41730      */
41731     refresh : function(){
41732         if(this.refreshDelegate){
41733            this.loaded = false;
41734            this.refreshDelegate();
41735         }
41736     },
41737
41738     /** @private */
41739     _setLoaded : function(){
41740         this.loaded = true;
41741     },
41742
41743     /** @private */
41744     closeClick : function(e){
41745         var o = {};
41746         e.stopEvent();
41747         this.fireEvent("beforeclose", this, o);
41748         if(o.cancel !== true){
41749             this.tabPanel.removeTab(this.id);
41750         }
41751     },
41752     /**
41753      * The text displayed in the tooltip for the close icon.
41754      * @type String
41755      */
41756     closeText : "Close this tab"
41757 });
41758 /**
41759 *    This script refer to:
41760 *    Title: International Telephone Input
41761 *    Author: Jack O'Connor
41762 *    Code version:  v12.1.12
41763 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41764 **/
41765
41766 Roo.bootstrap.PhoneInputData = function() {
41767     var d = [
41768       [
41769         "Afghanistan (‫افغانستان‬‎)",
41770         "af",
41771         "93"
41772       ],
41773       [
41774         "Albania (Shqipëri)",
41775         "al",
41776         "355"
41777       ],
41778       [
41779         "Algeria (‫الجزائر‬‎)",
41780         "dz",
41781         "213"
41782       ],
41783       [
41784         "American Samoa",
41785         "as",
41786         "1684"
41787       ],
41788       [
41789         "Andorra",
41790         "ad",
41791         "376"
41792       ],
41793       [
41794         "Angola",
41795         "ao",
41796         "244"
41797       ],
41798       [
41799         "Anguilla",
41800         "ai",
41801         "1264"
41802       ],
41803       [
41804         "Antigua and Barbuda",
41805         "ag",
41806         "1268"
41807       ],
41808       [
41809         "Argentina",
41810         "ar",
41811         "54"
41812       ],
41813       [
41814         "Armenia (Հայաստան)",
41815         "am",
41816         "374"
41817       ],
41818       [
41819         "Aruba",
41820         "aw",
41821         "297"
41822       ],
41823       [
41824         "Australia",
41825         "au",
41826         "61",
41827         0
41828       ],
41829       [
41830         "Austria (Österreich)",
41831         "at",
41832         "43"
41833       ],
41834       [
41835         "Azerbaijan (Azərbaycan)",
41836         "az",
41837         "994"
41838       ],
41839       [
41840         "Bahamas",
41841         "bs",
41842         "1242"
41843       ],
41844       [
41845         "Bahrain (‫البحرين‬‎)",
41846         "bh",
41847         "973"
41848       ],
41849       [
41850         "Bangladesh (বাংলাদেশ)",
41851         "bd",
41852         "880"
41853       ],
41854       [
41855         "Barbados",
41856         "bb",
41857         "1246"
41858       ],
41859       [
41860         "Belarus (Беларусь)",
41861         "by",
41862         "375"
41863       ],
41864       [
41865         "Belgium (België)",
41866         "be",
41867         "32"
41868       ],
41869       [
41870         "Belize",
41871         "bz",
41872         "501"
41873       ],
41874       [
41875         "Benin (Bénin)",
41876         "bj",
41877         "229"
41878       ],
41879       [
41880         "Bermuda",
41881         "bm",
41882         "1441"
41883       ],
41884       [
41885         "Bhutan (འབྲུག)",
41886         "bt",
41887         "975"
41888       ],
41889       [
41890         "Bolivia",
41891         "bo",
41892         "591"
41893       ],
41894       [
41895         "Bosnia and Herzegovina (Босна и Херцеговина)",
41896         "ba",
41897         "387"
41898       ],
41899       [
41900         "Botswana",
41901         "bw",
41902         "267"
41903       ],
41904       [
41905         "Brazil (Brasil)",
41906         "br",
41907         "55"
41908       ],
41909       [
41910         "British Indian Ocean Territory",
41911         "io",
41912         "246"
41913       ],
41914       [
41915         "British Virgin Islands",
41916         "vg",
41917         "1284"
41918       ],
41919       [
41920         "Brunei",
41921         "bn",
41922         "673"
41923       ],
41924       [
41925         "Bulgaria (България)",
41926         "bg",
41927         "359"
41928       ],
41929       [
41930         "Burkina Faso",
41931         "bf",
41932         "226"
41933       ],
41934       [
41935         "Burundi (Uburundi)",
41936         "bi",
41937         "257"
41938       ],
41939       [
41940         "Cambodia (កម្ពុជា)",
41941         "kh",
41942         "855"
41943       ],
41944       [
41945         "Cameroon (Cameroun)",
41946         "cm",
41947         "237"
41948       ],
41949       [
41950         "Canada",
41951         "ca",
41952         "1",
41953         1,
41954         ["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"]
41955       ],
41956       [
41957         "Cape Verde (Kabu Verdi)",
41958         "cv",
41959         "238"
41960       ],
41961       [
41962         "Caribbean Netherlands",
41963         "bq",
41964         "599",
41965         1
41966       ],
41967       [
41968         "Cayman Islands",
41969         "ky",
41970         "1345"
41971       ],
41972       [
41973         "Central African Republic (République centrafricaine)",
41974         "cf",
41975         "236"
41976       ],
41977       [
41978         "Chad (Tchad)",
41979         "td",
41980         "235"
41981       ],
41982       [
41983         "Chile",
41984         "cl",
41985         "56"
41986       ],
41987       [
41988         "China (中国)",
41989         "cn",
41990         "86"
41991       ],
41992       [
41993         "Christmas Island",
41994         "cx",
41995         "61",
41996         2
41997       ],
41998       [
41999         "Cocos (Keeling) Islands",
42000         "cc",
42001         "61",
42002         1
42003       ],
42004       [
42005         "Colombia",
42006         "co",
42007         "57"
42008       ],
42009       [
42010         "Comoros (‫جزر القمر‬‎)",
42011         "km",
42012         "269"
42013       ],
42014       [
42015         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42016         "cd",
42017         "243"
42018       ],
42019       [
42020         "Congo (Republic) (Congo-Brazzaville)",
42021         "cg",
42022         "242"
42023       ],
42024       [
42025         "Cook Islands",
42026         "ck",
42027         "682"
42028       ],
42029       [
42030         "Costa Rica",
42031         "cr",
42032         "506"
42033       ],
42034       [
42035         "Côte d’Ivoire",
42036         "ci",
42037         "225"
42038       ],
42039       [
42040         "Croatia (Hrvatska)",
42041         "hr",
42042         "385"
42043       ],
42044       [
42045         "Cuba",
42046         "cu",
42047         "53"
42048       ],
42049       [
42050         "Curaçao",
42051         "cw",
42052         "599",
42053         0
42054       ],
42055       [
42056         "Cyprus (Κύπρος)",
42057         "cy",
42058         "357"
42059       ],
42060       [
42061         "Czech Republic (Česká republika)",
42062         "cz",
42063         "420"
42064       ],
42065       [
42066         "Denmark (Danmark)",
42067         "dk",
42068         "45"
42069       ],
42070       [
42071         "Djibouti",
42072         "dj",
42073         "253"
42074       ],
42075       [
42076         "Dominica",
42077         "dm",
42078         "1767"
42079       ],
42080       [
42081         "Dominican Republic (República Dominicana)",
42082         "do",
42083         "1",
42084         2,
42085         ["809", "829", "849"]
42086       ],
42087       [
42088         "Ecuador",
42089         "ec",
42090         "593"
42091       ],
42092       [
42093         "Egypt (‫مصر‬‎)",
42094         "eg",
42095         "20"
42096       ],
42097       [
42098         "El Salvador",
42099         "sv",
42100         "503"
42101       ],
42102       [
42103         "Equatorial Guinea (Guinea Ecuatorial)",
42104         "gq",
42105         "240"
42106       ],
42107       [
42108         "Eritrea",
42109         "er",
42110         "291"
42111       ],
42112       [
42113         "Estonia (Eesti)",
42114         "ee",
42115         "372"
42116       ],
42117       [
42118         "Ethiopia",
42119         "et",
42120         "251"
42121       ],
42122       [
42123         "Falkland Islands (Islas Malvinas)",
42124         "fk",
42125         "500"
42126       ],
42127       [
42128         "Faroe Islands (Føroyar)",
42129         "fo",
42130         "298"
42131       ],
42132       [
42133         "Fiji",
42134         "fj",
42135         "679"
42136       ],
42137       [
42138         "Finland (Suomi)",
42139         "fi",
42140         "358",
42141         0
42142       ],
42143       [
42144         "France",
42145         "fr",
42146         "33"
42147       ],
42148       [
42149         "French Guiana (Guyane française)",
42150         "gf",
42151         "594"
42152       ],
42153       [
42154         "French Polynesia (Polynésie française)",
42155         "pf",
42156         "689"
42157       ],
42158       [
42159         "Gabon",
42160         "ga",
42161         "241"
42162       ],
42163       [
42164         "Gambia",
42165         "gm",
42166         "220"
42167       ],
42168       [
42169         "Georgia (საქართველო)",
42170         "ge",
42171         "995"
42172       ],
42173       [
42174         "Germany (Deutschland)",
42175         "de",
42176         "49"
42177       ],
42178       [
42179         "Ghana (Gaana)",
42180         "gh",
42181         "233"
42182       ],
42183       [
42184         "Gibraltar",
42185         "gi",
42186         "350"
42187       ],
42188       [
42189         "Greece (Ελλάδα)",
42190         "gr",
42191         "30"
42192       ],
42193       [
42194         "Greenland (Kalaallit Nunaat)",
42195         "gl",
42196         "299"
42197       ],
42198       [
42199         "Grenada",
42200         "gd",
42201         "1473"
42202       ],
42203       [
42204         "Guadeloupe",
42205         "gp",
42206         "590",
42207         0
42208       ],
42209       [
42210         "Guam",
42211         "gu",
42212         "1671"
42213       ],
42214       [
42215         "Guatemala",
42216         "gt",
42217         "502"
42218       ],
42219       [
42220         "Guernsey",
42221         "gg",
42222         "44",
42223         1
42224       ],
42225       [
42226         "Guinea (Guinée)",
42227         "gn",
42228         "224"
42229       ],
42230       [
42231         "Guinea-Bissau (Guiné Bissau)",
42232         "gw",
42233         "245"
42234       ],
42235       [
42236         "Guyana",
42237         "gy",
42238         "592"
42239       ],
42240       [
42241         "Haiti",
42242         "ht",
42243         "509"
42244       ],
42245       [
42246         "Honduras",
42247         "hn",
42248         "504"
42249       ],
42250       [
42251         "Hong Kong (香港)",
42252         "hk",
42253         "852"
42254       ],
42255       [
42256         "Hungary (Magyarország)",
42257         "hu",
42258         "36"
42259       ],
42260       [
42261         "Iceland (Ísland)",
42262         "is",
42263         "354"
42264       ],
42265       [
42266         "India (भारत)",
42267         "in",
42268         "91"
42269       ],
42270       [
42271         "Indonesia",
42272         "id",
42273         "62"
42274       ],
42275       [
42276         "Iran (‫ایران‬‎)",
42277         "ir",
42278         "98"
42279       ],
42280       [
42281         "Iraq (‫العراق‬‎)",
42282         "iq",
42283         "964"
42284       ],
42285       [
42286         "Ireland",
42287         "ie",
42288         "353"
42289       ],
42290       [
42291         "Isle of Man",
42292         "im",
42293         "44",
42294         2
42295       ],
42296       [
42297         "Israel (‫ישראל‬‎)",
42298         "il",
42299         "972"
42300       ],
42301       [
42302         "Italy (Italia)",
42303         "it",
42304         "39",
42305         0
42306       ],
42307       [
42308         "Jamaica",
42309         "jm",
42310         "1876"
42311       ],
42312       [
42313         "Japan (日本)",
42314         "jp",
42315         "81"
42316       ],
42317       [
42318         "Jersey",
42319         "je",
42320         "44",
42321         3
42322       ],
42323       [
42324         "Jordan (‫الأردن‬‎)",
42325         "jo",
42326         "962"
42327       ],
42328       [
42329         "Kazakhstan (Казахстан)",
42330         "kz",
42331         "7",
42332         1
42333       ],
42334       [
42335         "Kenya",
42336         "ke",
42337         "254"
42338       ],
42339       [
42340         "Kiribati",
42341         "ki",
42342         "686"
42343       ],
42344       [
42345         "Kosovo",
42346         "xk",
42347         "383"
42348       ],
42349       [
42350         "Kuwait (‫الكويت‬‎)",
42351         "kw",
42352         "965"
42353       ],
42354       [
42355         "Kyrgyzstan (Кыргызстан)",
42356         "kg",
42357         "996"
42358       ],
42359       [
42360         "Laos (ລາວ)",
42361         "la",
42362         "856"
42363       ],
42364       [
42365         "Latvia (Latvija)",
42366         "lv",
42367         "371"
42368       ],
42369       [
42370         "Lebanon (‫لبنان‬‎)",
42371         "lb",
42372         "961"
42373       ],
42374       [
42375         "Lesotho",
42376         "ls",
42377         "266"
42378       ],
42379       [
42380         "Liberia",
42381         "lr",
42382         "231"
42383       ],
42384       [
42385         "Libya (‫ليبيا‬‎)",
42386         "ly",
42387         "218"
42388       ],
42389       [
42390         "Liechtenstein",
42391         "li",
42392         "423"
42393       ],
42394       [
42395         "Lithuania (Lietuva)",
42396         "lt",
42397         "370"
42398       ],
42399       [
42400         "Luxembourg",
42401         "lu",
42402         "352"
42403       ],
42404       [
42405         "Macau (澳門)",
42406         "mo",
42407         "853"
42408       ],
42409       [
42410         "Macedonia (FYROM) (Македонија)",
42411         "mk",
42412         "389"
42413       ],
42414       [
42415         "Madagascar (Madagasikara)",
42416         "mg",
42417         "261"
42418       ],
42419       [
42420         "Malawi",
42421         "mw",
42422         "265"
42423       ],
42424       [
42425         "Malaysia",
42426         "my",
42427         "60"
42428       ],
42429       [
42430         "Maldives",
42431         "mv",
42432         "960"
42433       ],
42434       [
42435         "Mali",
42436         "ml",
42437         "223"
42438       ],
42439       [
42440         "Malta",
42441         "mt",
42442         "356"
42443       ],
42444       [
42445         "Marshall Islands",
42446         "mh",
42447         "692"
42448       ],
42449       [
42450         "Martinique",
42451         "mq",
42452         "596"
42453       ],
42454       [
42455         "Mauritania (‫موريتانيا‬‎)",
42456         "mr",
42457         "222"
42458       ],
42459       [
42460         "Mauritius (Moris)",
42461         "mu",
42462         "230"
42463       ],
42464       [
42465         "Mayotte",
42466         "yt",
42467         "262",
42468         1
42469       ],
42470       [
42471         "Mexico (México)",
42472         "mx",
42473         "52"
42474       ],
42475       [
42476         "Micronesia",
42477         "fm",
42478         "691"
42479       ],
42480       [
42481         "Moldova (Republica Moldova)",
42482         "md",
42483         "373"
42484       ],
42485       [
42486         "Monaco",
42487         "mc",
42488         "377"
42489       ],
42490       [
42491         "Mongolia (Монгол)",
42492         "mn",
42493         "976"
42494       ],
42495       [
42496         "Montenegro (Crna Gora)",
42497         "me",
42498         "382"
42499       ],
42500       [
42501         "Montserrat",
42502         "ms",
42503         "1664"
42504       ],
42505       [
42506         "Morocco (‫المغرب‬‎)",
42507         "ma",
42508         "212",
42509         0
42510       ],
42511       [
42512         "Mozambique (Moçambique)",
42513         "mz",
42514         "258"
42515       ],
42516       [
42517         "Myanmar (Burma) (မြန်မာ)",
42518         "mm",
42519         "95"
42520       ],
42521       [
42522         "Namibia (Namibië)",
42523         "na",
42524         "264"
42525       ],
42526       [
42527         "Nauru",
42528         "nr",
42529         "674"
42530       ],
42531       [
42532         "Nepal (नेपाल)",
42533         "np",
42534         "977"
42535       ],
42536       [
42537         "Netherlands (Nederland)",
42538         "nl",
42539         "31"
42540       ],
42541       [
42542         "New Caledonia (Nouvelle-Calédonie)",
42543         "nc",
42544         "687"
42545       ],
42546       [
42547         "New Zealand",
42548         "nz",
42549         "64"
42550       ],
42551       [
42552         "Nicaragua",
42553         "ni",
42554         "505"
42555       ],
42556       [
42557         "Niger (Nijar)",
42558         "ne",
42559         "227"
42560       ],
42561       [
42562         "Nigeria",
42563         "ng",
42564         "234"
42565       ],
42566       [
42567         "Niue",
42568         "nu",
42569         "683"
42570       ],
42571       [
42572         "Norfolk Island",
42573         "nf",
42574         "672"
42575       ],
42576       [
42577         "North Korea (조선 민주주의 인민 공화국)",
42578         "kp",
42579         "850"
42580       ],
42581       [
42582         "Northern Mariana Islands",
42583         "mp",
42584         "1670"
42585       ],
42586       [
42587         "Norway (Norge)",
42588         "no",
42589         "47",
42590         0
42591       ],
42592       [
42593         "Oman (‫عُمان‬‎)",
42594         "om",
42595         "968"
42596       ],
42597       [
42598         "Pakistan (‫پاکستان‬‎)",
42599         "pk",
42600         "92"
42601       ],
42602       [
42603         "Palau",
42604         "pw",
42605         "680"
42606       ],
42607       [
42608         "Palestine (‫فلسطين‬‎)",
42609         "ps",
42610         "970"
42611       ],
42612       [
42613         "Panama (Panamá)",
42614         "pa",
42615         "507"
42616       ],
42617       [
42618         "Papua New Guinea",
42619         "pg",
42620         "675"
42621       ],
42622       [
42623         "Paraguay",
42624         "py",
42625         "595"
42626       ],
42627       [
42628         "Peru (Perú)",
42629         "pe",
42630         "51"
42631       ],
42632       [
42633         "Philippines",
42634         "ph",
42635         "63"
42636       ],
42637       [
42638         "Poland (Polska)",
42639         "pl",
42640         "48"
42641       ],
42642       [
42643         "Portugal",
42644         "pt",
42645         "351"
42646       ],
42647       [
42648         "Puerto Rico",
42649         "pr",
42650         "1",
42651         3,
42652         ["787", "939"]
42653       ],
42654       [
42655         "Qatar (‫قطر‬‎)",
42656         "qa",
42657         "974"
42658       ],
42659       [
42660         "Réunion (La Réunion)",
42661         "re",
42662         "262",
42663         0
42664       ],
42665       [
42666         "Romania (România)",
42667         "ro",
42668         "40"
42669       ],
42670       [
42671         "Russia (Россия)",
42672         "ru",
42673         "7",
42674         0
42675       ],
42676       [
42677         "Rwanda",
42678         "rw",
42679         "250"
42680       ],
42681       [
42682         "Saint Barthélemy",
42683         "bl",
42684         "590",
42685         1
42686       ],
42687       [
42688         "Saint Helena",
42689         "sh",
42690         "290"
42691       ],
42692       [
42693         "Saint Kitts and Nevis",
42694         "kn",
42695         "1869"
42696       ],
42697       [
42698         "Saint Lucia",
42699         "lc",
42700         "1758"
42701       ],
42702       [
42703         "Saint Martin (Saint-Martin (partie française))",
42704         "mf",
42705         "590",
42706         2
42707       ],
42708       [
42709         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42710         "pm",
42711         "508"
42712       ],
42713       [
42714         "Saint Vincent and the Grenadines",
42715         "vc",
42716         "1784"
42717       ],
42718       [
42719         "Samoa",
42720         "ws",
42721         "685"
42722       ],
42723       [
42724         "San Marino",
42725         "sm",
42726         "378"
42727       ],
42728       [
42729         "São Tomé and Príncipe (São Tomé e Príncipe)",
42730         "st",
42731         "239"
42732       ],
42733       [
42734         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42735         "sa",
42736         "966"
42737       ],
42738       [
42739         "Senegal (Sénégal)",
42740         "sn",
42741         "221"
42742       ],
42743       [
42744         "Serbia (Србија)",
42745         "rs",
42746         "381"
42747       ],
42748       [
42749         "Seychelles",
42750         "sc",
42751         "248"
42752       ],
42753       [
42754         "Sierra Leone",
42755         "sl",
42756         "232"
42757       ],
42758       [
42759         "Singapore",
42760         "sg",
42761         "65"
42762       ],
42763       [
42764         "Sint Maarten",
42765         "sx",
42766         "1721"
42767       ],
42768       [
42769         "Slovakia (Slovensko)",
42770         "sk",
42771         "421"
42772       ],
42773       [
42774         "Slovenia (Slovenija)",
42775         "si",
42776         "386"
42777       ],
42778       [
42779         "Solomon Islands",
42780         "sb",
42781         "677"
42782       ],
42783       [
42784         "Somalia (Soomaaliya)",
42785         "so",
42786         "252"
42787       ],
42788       [
42789         "South Africa",
42790         "za",
42791         "27"
42792       ],
42793       [
42794         "South Korea (대한민국)",
42795         "kr",
42796         "82"
42797       ],
42798       [
42799         "South Sudan (‫جنوب السودان‬‎)",
42800         "ss",
42801         "211"
42802       ],
42803       [
42804         "Spain (España)",
42805         "es",
42806         "34"
42807       ],
42808       [
42809         "Sri Lanka (ශ්‍රී ලංකාව)",
42810         "lk",
42811         "94"
42812       ],
42813       [
42814         "Sudan (‫السودان‬‎)",
42815         "sd",
42816         "249"
42817       ],
42818       [
42819         "Suriname",
42820         "sr",
42821         "597"
42822       ],
42823       [
42824         "Svalbard and Jan Mayen",
42825         "sj",
42826         "47",
42827         1
42828       ],
42829       [
42830         "Swaziland",
42831         "sz",
42832         "268"
42833       ],
42834       [
42835         "Sweden (Sverige)",
42836         "se",
42837         "46"
42838       ],
42839       [
42840         "Switzerland (Schweiz)",
42841         "ch",
42842         "41"
42843       ],
42844       [
42845         "Syria (‫سوريا‬‎)",
42846         "sy",
42847         "963"
42848       ],
42849       [
42850         "Taiwan (台灣)",
42851         "tw",
42852         "886"
42853       ],
42854       [
42855         "Tajikistan",
42856         "tj",
42857         "992"
42858       ],
42859       [
42860         "Tanzania",
42861         "tz",
42862         "255"
42863       ],
42864       [
42865         "Thailand (ไทย)",
42866         "th",
42867         "66"
42868       ],
42869       [
42870         "Timor-Leste",
42871         "tl",
42872         "670"
42873       ],
42874       [
42875         "Togo",
42876         "tg",
42877         "228"
42878       ],
42879       [
42880         "Tokelau",
42881         "tk",
42882         "690"
42883       ],
42884       [
42885         "Tonga",
42886         "to",
42887         "676"
42888       ],
42889       [
42890         "Trinidad and Tobago",
42891         "tt",
42892         "1868"
42893       ],
42894       [
42895         "Tunisia (‫تونس‬‎)",
42896         "tn",
42897         "216"
42898       ],
42899       [
42900         "Turkey (Türkiye)",
42901         "tr",
42902         "90"
42903       ],
42904       [
42905         "Turkmenistan",
42906         "tm",
42907         "993"
42908       ],
42909       [
42910         "Turks and Caicos Islands",
42911         "tc",
42912         "1649"
42913       ],
42914       [
42915         "Tuvalu",
42916         "tv",
42917         "688"
42918       ],
42919       [
42920         "U.S. Virgin Islands",
42921         "vi",
42922         "1340"
42923       ],
42924       [
42925         "Uganda",
42926         "ug",
42927         "256"
42928       ],
42929       [
42930         "Ukraine (Україна)",
42931         "ua",
42932         "380"
42933       ],
42934       [
42935         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42936         "ae",
42937         "971"
42938       ],
42939       [
42940         "United Kingdom",
42941         "gb",
42942         "44",
42943         0
42944       ],
42945       [
42946         "United States",
42947         "us",
42948         "1",
42949         0
42950       ],
42951       [
42952         "Uruguay",
42953         "uy",
42954         "598"
42955       ],
42956       [
42957         "Uzbekistan (Oʻzbekiston)",
42958         "uz",
42959         "998"
42960       ],
42961       [
42962         "Vanuatu",
42963         "vu",
42964         "678"
42965       ],
42966       [
42967         "Vatican City (Città del Vaticano)",
42968         "va",
42969         "39",
42970         1
42971       ],
42972       [
42973         "Venezuela",
42974         "ve",
42975         "58"
42976       ],
42977       [
42978         "Vietnam (Việt Nam)",
42979         "vn",
42980         "84"
42981       ],
42982       [
42983         "Wallis and Futuna (Wallis-et-Futuna)",
42984         "wf",
42985         "681"
42986       ],
42987       [
42988         "Western Sahara (‫الصحراء الغربية‬‎)",
42989         "eh",
42990         "212",
42991         1
42992       ],
42993       [
42994         "Yemen (‫اليمن‬‎)",
42995         "ye",
42996         "967"
42997       ],
42998       [
42999         "Zambia",
43000         "zm",
43001         "260"
43002       ],
43003       [
43004         "Zimbabwe",
43005         "zw",
43006         "263"
43007       ],
43008       [
43009         "Åland Islands",
43010         "ax",
43011         "358",
43012         1
43013       ]
43014   ];
43015   
43016   return d;
43017 }/**
43018 *    This script refer to:
43019 *    Title: International Telephone Input
43020 *    Author: Jack O'Connor
43021 *    Code version:  v12.1.12
43022 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43023 **/
43024
43025 /**
43026  * @class Roo.bootstrap.PhoneInput
43027  * @extends Roo.bootstrap.TriggerField
43028  * An input with International dial-code selection
43029  
43030  * @cfg {String} defaultDialCode default '+852'
43031  * @cfg {Array} preferedCountries default []
43032   
43033  * @constructor
43034  * Create a new PhoneInput.
43035  * @param {Object} config Configuration options
43036  */
43037
43038 Roo.bootstrap.PhoneInput = function(config) {
43039     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43040 };
43041
43042 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43043         
43044         listWidth: undefined,
43045         
43046         selectedClass: 'active',
43047         
43048         invalidClass : "has-warning",
43049         
43050         validClass: 'has-success',
43051         
43052         allowed: '0123456789',
43053         
43054         max_length: 15,
43055         
43056         /**
43057          * @cfg {String} defaultDialCode The default dial code when initializing the input
43058          */
43059         defaultDialCode: '+852',
43060         
43061         /**
43062          * @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
43063          */
43064         preferedCountries: false,
43065         
43066         getAutoCreate : function()
43067         {
43068             var data = Roo.bootstrap.PhoneInputData();
43069             var align = this.labelAlign || this.parentLabelAlign();
43070             var id = Roo.id();
43071             
43072             this.allCountries = [];
43073             this.dialCodeMapping = [];
43074             
43075             for (var i = 0; i < data.length; i++) {
43076               var c = data[i];
43077               this.allCountries[i] = {
43078                 name: c[0],
43079                 iso2: c[1],
43080                 dialCode: c[2],
43081                 priority: c[3] || 0,
43082                 areaCodes: c[4] || null
43083               };
43084               this.dialCodeMapping[c[2]] = {
43085                   name: c[0],
43086                   iso2: c[1],
43087                   priority: c[3] || 0,
43088                   areaCodes: c[4] || null
43089               };
43090             }
43091             
43092             var cfg = {
43093                 cls: 'form-group',
43094                 cn: []
43095             };
43096             
43097             var input =  {
43098                 tag: 'input',
43099                 id : id,
43100                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43101                 maxlength: this.max_length,
43102                 cls : 'form-control tel-input',
43103                 autocomplete: 'new-password'
43104             };
43105             
43106             var hiddenInput = {
43107                 tag: 'input',
43108                 type: 'hidden',
43109                 cls: 'hidden-tel-input'
43110             };
43111             
43112             if (this.name) {
43113                 hiddenInput.name = this.name;
43114             }
43115             
43116             if (this.disabled) {
43117                 input.disabled = true;
43118             }
43119             
43120             var flag_container = {
43121                 tag: 'div',
43122                 cls: 'flag-box',
43123                 cn: [
43124                     {
43125                         tag: 'div',
43126                         cls: 'flag'
43127                     },
43128                     {
43129                         tag: 'div',
43130                         cls: 'caret'
43131                     }
43132                 ]
43133             };
43134             
43135             var box = {
43136                 tag: 'div',
43137                 cls: this.hasFeedback ? 'has-feedback' : '',
43138                 cn: [
43139                     hiddenInput,
43140                     input,
43141                     {
43142                         tag: 'input',
43143                         cls: 'dial-code-holder',
43144                         disabled: true
43145                     }
43146                 ]
43147             };
43148             
43149             var container = {
43150                 cls: 'roo-select2-container input-group',
43151                 cn: [
43152                     flag_container,
43153                     box
43154                 ]
43155             };
43156             
43157             if (this.fieldLabel.length) {
43158                 var indicator = {
43159                     tag: 'i',
43160                     tooltip: 'This field is required'
43161                 };
43162                 
43163                 var label = {
43164                     tag: 'label',
43165                     'for':  id,
43166                     cls: 'control-label',
43167                     cn: []
43168                 };
43169                 
43170                 var label_text = {
43171                     tag: 'span',
43172                     html: this.fieldLabel
43173                 };
43174                 
43175                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43176                 label.cn = [
43177                     indicator,
43178                     label_text
43179                 ];
43180                 
43181                 if(this.indicatorpos == 'right') {
43182                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43183                     label.cn = [
43184                         label_text,
43185                         indicator
43186                     ];
43187                 }
43188                 
43189                 if(align == 'left') {
43190                     container = {
43191                         tag: 'div',
43192                         cn: [
43193                             container
43194                         ]
43195                     };
43196                     
43197                     if(this.labelWidth > 12){
43198                         label.style = "width: " + this.labelWidth + 'px';
43199                     }
43200                     if(this.labelWidth < 13 && this.labelmd == 0){
43201                         this.labelmd = this.labelWidth;
43202                     }
43203                     if(this.labellg > 0){
43204                         label.cls += ' col-lg-' + this.labellg;
43205                         input.cls += ' col-lg-' + (12 - this.labellg);
43206                     }
43207                     if(this.labelmd > 0){
43208                         label.cls += ' col-md-' + this.labelmd;
43209                         container.cls += ' col-md-' + (12 - this.labelmd);
43210                     }
43211                     if(this.labelsm > 0){
43212                         label.cls += ' col-sm-' + this.labelsm;
43213                         container.cls += ' col-sm-' + (12 - this.labelsm);
43214                     }
43215                     if(this.labelxs > 0){
43216                         label.cls += ' col-xs-' + this.labelxs;
43217                         container.cls += ' col-xs-' + (12 - this.labelxs);
43218                     }
43219                 }
43220             }
43221             
43222             cfg.cn = [
43223                 label,
43224                 container
43225             ];
43226             
43227             var settings = this;
43228             
43229             ['xs','sm','md','lg'].map(function(size){
43230                 if (settings[size]) {
43231                     cfg.cls += ' col-' + size + '-' + settings[size];
43232                 }
43233             });
43234             
43235             this.store = new Roo.data.Store({
43236                 proxy : new Roo.data.MemoryProxy({}),
43237                 reader : new Roo.data.JsonReader({
43238                     fields : [
43239                         {
43240                             'name' : 'name',
43241                             'type' : 'string'
43242                         },
43243                         {
43244                             'name' : 'iso2',
43245                             'type' : 'string'
43246                         },
43247                         {
43248                             'name' : 'dialCode',
43249                             'type' : 'string'
43250                         },
43251                         {
43252                             'name' : 'priority',
43253                             'type' : 'string'
43254                         },
43255                         {
43256                             'name' : 'areaCodes',
43257                             'type' : 'string'
43258                         }
43259                     ]
43260                 })
43261             });
43262             
43263             if(!this.preferedCountries) {
43264                 this.preferedCountries = [
43265                     'hk',
43266                     'gb',
43267                     'us'
43268                 ];
43269             }
43270             
43271             var p = this.preferedCountries.reverse();
43272             
43273             if(p) {
43274                 for (var i = 0; i < p.length; i++) {
43275                     for (var j = 0; j < this.allCountries.length; j++) {
43276                         if(this.allCountries[j].iso2 == p[i]) {
43277                             var t = this.allCountries[j];
43278                             this.allCountries.splice(j,1);
43279                             this.allCountries.unshift(t);
43280                         }
43281                     } 
43282                 }
43283             }
43284             
43285             this.store.proxy.data = {
43286                 success: true,
43287                 data: this.allCountries
43288             };
43289             
43290             return cfg;
43291         },
43292         
43293         initEvents : function()
43294         {
43295             this.createList();
43296             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43297             
43298             this.indicator = this.indicatorEl();
43299             this.flag = this.flagEl();
43300             this.dialCodeHolder = this.dialCodeHolderEl();
43301             
43302             this.trigger = this.el.select('div.flag-box',true).first();
43303             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43304             
43305             var _this = this;
43306             
43307             (function(){
43308                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43309                 _this.list.setWidth(lw);
43310             }).defer(100);
43311             
43312             this.list.on('mouseover', this.onViewOver, this);
43313             this.list.on('mousemove', this.onViewMove, this);
43314             this.inputEl().on("keyup", this.onKeyUp, this);
43315             this.inputEl().on("keypress", this.onKeyPress, this);
43316             
43317             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43318
43319             this.view = new Roo.View(this.list, this.tpl, {
43320                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43321             });
43322             
43323             this.view.on('click', this.onViewClick, this);
43324             this.setValue(this.defaultDialCode);
43325         },
43326         
43327         onTriggerClick : function(e)
43328         {
43329             Roo.log('trigger click');
43330             if(this.disabled){
43331                 return;
43332             }
43333             
43334             if(this.isExpanded()){
43335                 this.collapse();
43336                 this.hasFocus = false;
43337             }else {
43338                 this.store.load({});
43339                 this.hasFocus = true;
43340                 this.expand();
43341             }
43342         },
43343         
43344         isExpanded : function()
43345         {
43346             return this.list.isVisible();
43347         },
43348         
43349         collapse : function()
43350         {
43351             if(!this.isExpanded()){
43352                 return;
43353             }
43354             this.list.hide();
43355             Roo.get(document).un('mousedown', this.collapseIf, this);
43356             Roo.get(document).un('mousewheel', this.collapseIf, this);
43357             this.fireEvent('collapse', this);
43358             this.validate();
43359         },
43360         
43361         expand : function()
43362         {
43363             Roo.log('expand');
43364
43365             if(this.isExpanded() || !this.hasFocus){
43366                 return;
43367             }
43368             
43369             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43370             this.list.setWidth(lw);
43371             
43372             this.list.show();
43373             this.restrictHeight();
43374             
43375             Roo.get(document).on('mousedown', this.collapseIf, this);
43376             Roo.get(document).on('mousewheel', this.collapseIf, this);
43377             
43378             this.fireEvent('expand', this);
43379         },
43380         
43381         restrictHeight : function()
43382         {
43383             this.list.alignTo(this.inputEl(), this.listAlign);
43384             this.list.alignTo(this.inputEl(), this.listAlign);
43385         },
43386         
43387         onViewOver : function(e, t)
43388         {
43389             if(this.inKeyMode){
43390                 return;
43391             }
43392             var item = this.view.findItemFromChild(t);
43393             
43394             if(item){
43395                 var index = this.view.indexOf(item);
43396                 this.select(index, false);
43397             }
43398         },
43399
43400         // private
43401         onViewClick : function(view, doFocus, el, e)
43402         {
43403             var index = this.view.getSelectedIndexes()[0];
43404             
43405             var r = this.store.getAt(index);
43406             
43407             if(r){
43408                 this.onSelect(r, index);
43409             }
43410             if(doFocus !== false && !this.blockFocus){
43411                 this.inputEl().focus();
43412             }
43413         },
43414         
43415         onViewMove : function(e, t)
43416         {
43417             this.inKeyMode = false;
43418         },
43419         
43420         select : function(index, scrollIntoView)
43421         {
43422             this.selectedIndex = index;
43423             this.view.select(index);
43424             if(scrollIntoView !== false){
43425                 var el = this.view.getNode(index);
43426                 if(el){
43427                     this.list.scrollChildIntoView(el, false);
43428                 }
43429             }
43430         },
43431         
43432         createList : function()
43433         {
43434             this.list = Roo.get(document.body).createChild({
43435                 tag: 'ul',
43436                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43437                 style: 'display:none'
43438             });
43439             
43440             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43441         },
43442         
43443         collapseIf : function(e)
43444         {
43445             var in_combo  = e.within(this.el);
43446             var in_list =  e.within(this.list);
43447             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43448             
43449             if (in_combo || in_list || is_list) {
43450                 return;
43451             }
43452             this.collapse();
43453         },
43454         
43455         onSelect : function(record, index)
43456         {
43457             if(this.fireEvent('beforeselect', this, record, index) !== false){
43458                 
43459                 this.setFlagClass(record.data.iso2);
43460                 this.setDialCode(record.data.dialCode);
43461                 this.hasFocus = false;
43462                 this.collapse();
43463                 this.fireEvent('select', this, record, index);
43464             }
43465         },
43466         
43467         flagEl : function()
43468         {
43469             var flag = this.el.select('div.flag',true).first();
43470             if(!flag){
43471                 return false;
43472             }
43473             return flag;
43474         },
43475         
43476         dialCodeHolderEl : function()
43477         {
43478             var d = this.el.select('input.dial-code-holder',true).first();
43479             if(!d){
43480                 return false;
43481             }
43482             return d;
43483         },
43484         
43485         setDialCode : function(v)
43486         {
43487             this.dialCodeHolder.dom.value = '+'+v;
43488         },
43489         
43490         setFlagClass : function(n)
43491         {
43492             this.flag.dom.className = 'flag '+n;
43493         },
43494         
43495         getValue : function()
43496         {
43497             var v = this.inputEl().getValue();
43498             if(this.dialCodeHolder) {
43499                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43500             }
43501             return v;
43502         },
43503         
43504         setValue : function(v)
43505         {
43506             var d = this.getDialCode(v);
43507             
43508             //invalid dial code
43509             if(v.length == 0 || !d || d.length == 0) {
43510                 if(this.rendered){
43511                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43512                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43513                 }
43514                 return;
43515             }
43516             
43517             //valid dial code
43518             this.setFlagClass(this.dialCodeMapping[d].iso2);
43519             this.setDialCode(d);
43520             this.inputEl().dom.value = v.replace('+'+d,'');
43521             this.hiddenEl().dom.value = this.getValue();
43522             
43523             this.validate();
43524         },
43525         
43526         getDialCode : function(v)
43527         {
43528             v = v ||  '';
43529             
43530             if (v.length == 0) {
43531                 return this.dialCodeHolder.dom.value;
43532             }
43533             
43534             var dialCode = "";
43535             if (v.charAt(0) != "+") {
43536                 return false;
43537             }
43538             var numericChars = "";
43539             for (var i = 1; i < v.length; i++) {
43540               var c = v.charAt(i);
43541               if (!isNaN(c)) {
43542                 numericChars += c;
43543                 if (this.dialCodeMapping[numericChars]) {
43544                   dialCode = v.substr(1, i);
43545                 }
43546                 if (numericChars.length == 4) {
43547                   break;
43548                 }
43549               }
43550             }
43551             return dialCode;
43552         },
43553         
43554         reset : function()
43555         {
43556             this.setValue(this.defaultDialCode);
43557             this.validate();
43558         },
43559         
43560         hiddenEl : function()
43561         {
43562             return this.el.select('input.hidden-tel-input',true).first();
43563         },
43564         
43565         // after setting val
43566         onKeyUp : function(e){
43567             this.setValue(this.getValue());
43568         },
43569         
43570         onKeyPress : function(e){
43571             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43572                 e.stopEvent();
43573             }
43574         }
43575         
43576 });
43577 /**
43578  * @class Roo.bootstrap.MoneyField
43579  * @extends Roo.bootstrap.ComboBox
43580  * Bootstrap MoneyField class
43581  * 
43582  * @constructor
43583  * Create a new MoneyField.
43584  * @param {Object} config Configuration options
43585  */
43586
43587 Roo.bootstrap.MoneyField = function(config) {
43588     
43589     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43590     
43591 };
43592
43593 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43594     
43595     /**
43596      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43597      */
43598     allowDecimals : true,
43599     /**
43600      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43601      */
43602     decimalSeparator : ".",
43603     /**
43604      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43605      */
43606     decimalPrecision : 0,
43607     /**
43608      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43609      */
43610     allowNegative : true,
43611     /**
43612      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43613      */
43614     allowZero: true,
43615     /**
43616      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43617      */
43618     minValue : Number.NEGATIVE_INFINITY,
43619     /**
43620      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43621      */
43622     maxValue : Number.MAX_VALUE,
43623     /**
43624      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43625      */
43626     minText : "The minimum value for this field is {0}",
43627     /**
43628      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43629      */
43630     maxText : "The maximum value for this field is {0}",
43631     /**
43632      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43633      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43634      */
43635     nanText : "{0} is not a valid number",
43636     /**
43637      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43638      */
43639     castInt : true,
43640     /**
43641      * @cfg {String} defaults currency of the MoneyField
43642      * value should be in lkey
43643      */
43644     defaultCurrency : false,
43645     /**
43646      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43647      */
43648     thousandsDelimiter : false,
43649     /**
43650      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43651      */
43652     max_length: false,
43653     
43654     inputlg : 9,
43655     inputmd : 9,
43656     inputsm : 9,
43657     inputxs : 6,
43658     
43659     store : false,
43660     
43661     getAutoCreate : function()
43662     {
43663         var align = this.labelAlign || this.parentLabelAlign();
43664         
43665         var id = Roo.id();
43666
43667         var cfg = {
43668             cls: 'form-group',
43669             cn: []
43670         };
43671
43672         var input =  {
43673             tag: 'input',
43674             id : id,
43675             cls : 'form-control roo-money-amount-input',
43676             autocomplete: 'new-password'
43677         };
43678         
43679         var hiddenInput = {
43680             tag: 'input',
43681             type: 'hidden',
43682             id: Roo.id(),
43683             cls: 'hidden-number-input'
43684         };
43685         
43686         if(this.max_length) {
43687             input.maxlength = this.max_length; 
43688         }
43689         
43690         if (this.name) {
43691             hiddenInput.name = this.name;
43692         }
43693
43694         if (this.disabled) {
43695             input.disabled = true;
43696         }
43697
43698         var clg = 12 - this.inputlg;
43699         var cmd = 12 - this.inputmd;
43700         var csm = 12 - this.inputsm;
43701         var cxs = 12 - this.inputxs;
43702         
43703         var container = {
43704             tag : 'div',
43705             cls : 'row roo-money-field',
43706             cn : [
43707                 {
43708                     tag : 'div',
43709                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43710                     cn : [
43711                         {
43712                             tag : 'div',
43713                             cls: 'roo-select2-container input-group',
43714                             cn: [
43715                                 {
43716                                     tag : 'input',
43717                                     cls : 'form-control roo-money-currency-input',
43718                                     autocomplete: 'new-password',
43719                                     readOnly : 1,
43720                                     name : this.currencyName
43721                                 },
43722                                 {
43723                                     tag :'span',
43724                                     cls : 'input-group-addon',
43725                                     cn : [
43726                                         {
43727                                             tag: 'span',
43728                                             cls: 'caret'
43729                                         }
43730                                     ]
43731                                 }
43732                             ]
43733                         }
43734                     ]
43735                 },
43736                 {
43737                     tag : 'div',
43738                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43739                     cn : [
43740                         {
43741                             tag: 'div',
43742                             cls: this.hasFeedback ? 'has-feedback' : '',
43743                             cn: [
43744                                 input
43745                             ]
43746                         }
43747                     ]
43748                 }
43749             ]
43750             
43751         };
43752         
43753         if (this.fieldLabel.length) {
43754             var indicator = {
43755                 tag: 'i',
43756                 tooltip: 'This field is required'
43757             };
43758
43759             var label = {
43760                 tag: 'label',
43761                 'for':  id,
43762                 cls: 'control-label',
43763                 cn: []
43764             };
43765
43766             var label_text = {
43767                 tag: 'span',
43768                 html: this.fieldLabel
43769             };
43770
43771             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43772             label.cn = [
43773                 indicator,
43774                 label_text
43775             ];
43776
43777             if(this.indicatorpos == 'right') {
43778                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43779                 label.cn = [
43780                     label_text,
43781                     indicator
43782                 ];
43783             }
43784
43785             if(align == 'left') {
43786                 container = {
43787                     tag: 'div',
43788                     cn: [
43789                         container
43790                     ]
43791                 };
43792
43793                 if(this.labelWidth > 12){
43794                     label.style = "width: " + this.labelWidth + 'px';
43795                 }
43796                 if(this.labelWidth < 13 && this.labelmd == 0){
43797                     this.labelmd = this.labelWidth;
43798                 }
43799                 if(this.labellg > 0){
43800                     label.cls += ' col-lg-' + this.labellg;
43801                     input.cls += ' col-lg-' + (12 - this.labellg);
43802                 }
43803                 if(this.labelmd > 0){
43804                     label.cls += ' col-md-' + this.labelmd;
43805                     container.cls += ' col-md-' + (12 - this.labelmd);
43806                 }
43807                 if(this.labelsm > 0){
43808                     label.cls += ' col-sm-' + this.labelsm;
43809                     container.cls += ' col-sm-' + (12 - this.labelsm);
43810                 }
43811                 if(this.labelxs > 0){
43812                     label.cls += ' col-xs-' + this.labelxs;
43813                     container.cls += ' col-xs-' + (12 - this.labelxs);
43814                 }
43815             }
43816         }
43817
43818         cfg.cn = [
43819             label,
43820             container,
43821             hiddenInput
43822         ];
43823         
43824         var settings = this;
43825
43826         ['xs','sm','md','lg'].map(function(size){
43827             if (settings[size]) {
43828                 cfg.cls += ' col-' + size + '-' + settings[size];
43829             }
43830         });
43831         
43832         return cfg;
43833     },
43834     
43835     initEvents : function()
43836     {
43837         this.indicator = this.indicatorEl();
43838         
43839         this.initCurrencyEvent();
43840         
43841         this.initNumberEvent();
43842     },
43843     
43844     initCurrencyEvent : function()
43845     {
43846         if (!this.store) {
43847             throw "can not find store for combo";
43848         }
43849         
43850         this.store = Roo.factory(this.store, Roo.data);
43851         this.store.parent = this;
43852         
43853         this.createList();
43854         
43855         this.triggerEl = this.el.select('.input-group-addon', true).first();
43856         
43857         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43858         
43859         var _this = this;
43860         
43861         (function(){
43862             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43863             _this.list.setWidth(lw);
43864         }).defer(100);
43865         
43866         this.list.on('mouseover', this.onViewOver, this);
43867         this.list.on('mousemove', this.onViewMove, this);
43868         this.list.on('scroll', this.onViewScroll, this);
43869         
43870         if(!this.tpl){
43871             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43872         }
43873         
43874         this.view = new Roo.View(this.list, this.tpl, {
43875             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43876         });
43877         
43878         this.view.on('click', this.onViewClick, this);
43879         
43880         this.store.on('beforeload', this.onBeforeLoad, this);
43881         this.store.on('load', this.onLoad, this);
43882         this.store.on('loadexception', this.onLoadException, this);
43883         
43884         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43885             "up" : function(e){
43886                 this.inKeyMode = true;
43887                 this.selectPrev();
43888             },
43889
43890             "down" : function(e){
43891                 if(!this.isExpanded()){
43892                     this.onTriggerClick();
43893                 }else{
43894                     this.inKeyMode = true;
43895                     this.selectNext();
43896                 }
43897             },
43898
43899             "enter" : function(e){
43900                 this.collapse();
43901                 
43902                 if(this.fireEvent("specialkey", this, e)){
43903                     this.onViewClick(false);
43904                 }
43905                 
43906                 return true;
43907             },
43908
43909             "esc" : function(e){
43910                 this.collapse();
43911             },
43912
43913             "tab" : function(e){
43914                 this.collapse();
43915                 
43916                 if(this.fireEvent("specialkey", this, e)){
43917                     this.onViewClick(false);
43918                 }
43919                 
43920                 return true;
43921             },
43922
43923             scope : this,
43924
43925             doRelay : function(foo, bar, hname){
43926                 if(hname == 'down' || this.scope.isExpanded()){
43927                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43928                 }
43929                 return true;
43930             },
43931
43932             forceKeyDown: true
43933         });
43934         
43935         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43936         
43937     },
43938     
43939     initNumberEvent : function(e)
43940     {
43941         this.inputEl().on("keydown" , this.fireKey,  this);
43942         this.inputEl().on("focus", this.onFocus,  this);
43943         this.inputEl().on("blur", this.onBlur,  this);
43944         
43945         this.inputEl().relayEvent('keyup', this);
43946         
43947         if(this.indicator){
43948             this.indicator.addClass('invisible');
43949         }
43950  
43951         this.originalValue = this.getValue();
43952         
43953         if(this.validationEvent == 'keyup'){
43954             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43955             this.inputEl().on('keyup', this.filterValidation, this);
43956         }
43957         else if(this.validationEvent !== false){
43958             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43959         }
43960         
43961         if(this.selectOnFocus){
43962             this.on("focus", this.preFocus, this);
43963             
43964         }
43965         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43966             this.inputEl().on("keypress", this.filterKeys, this);
43967         } else {
43968             this.inputEl().relayEvent('keypress', this);
43969         }
43970         
43971         var allowed = "0123456789";
43972         
43973         if(this.allowDecimals){
43974             allowed += this.decimalSeparator;
43975         }
43976         
43977         if(this.allowNegative){
43978             allowed += "-";
43979         }
43980         
43981         if(this.thousandsDelimiter) {
43982             allowed += ",";
43983         }
43984         
43985         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43986         
43987         var keyPress = function(e){
43988             
43989             var k = e.getKey();
43990             
43991             var c = e.getCharCode();
43992             
43993             if(
43994                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43995                     allowed.indexOf(String.fromCharCode(c)) === -1
43996             ){
43997                 e.stopEvent();
43998                 return;
43999             }
44000             
44001             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44002                 return;
44003             }
44004             
44005             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44006                 e.stopEvent();
44007             }
44008         };
44009         
44010         this.inputEl().on("keypress", keyPress, this);
44011         
44012     },
44013     
44014     onTriggerClick : function(e)
44015     {   
44016         if(this.disabled){
44017             return;
44018         }
44019         
44020         this.page = 0;
44021         this.loadNext = false;
44022         
44023         if(this.isExpanded()){
44024             this.collapse();
44025             return;
44026         }
44027         
44028         this.hasFocus = true;
44029         
44030         if(this.triggerAction == 'all') {
44031             this.doQuery(this.allQuery, true);
44032             return;
44033         }
44034         
44035         this.doQuery(this.getRawValue());
44036     },
44037     
44038     getCurrency : function()
44039     {   
44040         var v = this.currencyEl().getValue();
44041         
44042         return v;
44043     },
44044     
44045     restrictHeight : function()
44046     {
44047         this.list.alignTo(this.currencyEl(), this.listAlign);
44048         this.list.alignTo(this.currencyEl(), this.listAlign);
44049     },
44050     
44051     onViewClick : function(view, doFocus, el, e)
44052     {
44053         var index = this.view.getSelectedIndexes()[0];
44054         
44055         var r = this.store.getAt(index);
44056         
44057         if(r){
44058             this.onSelect(r, index);
44059         }
44060     },
44061     
44062     onSelect : function(record, index){
44063         
44064         if(this.fireEvent('beforeselect', this, record, index) !== false){
44065         
44066             this.setFromCurrencyData(index > -1 ? record.data : false);
44067             
44068             this.collapse();
44069             
44070             this.fireEvent('select', this, record, index);
44071         }
44072     },
44073     
44074     setFromCurrencyData : function(o)
44075     {
44076         var currency = '';
44077         
44078         this.lastCurrency = o;
44079         
44080         if (this.currencyField) {
44081             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44082         } else {
44083             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44084         }
44085         
44086         this.lastSelectionText = currency;
44087         
44088         //setting default currency
44089         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44090             this.setCurrency(this.defaultCurrency);
44091             return;
44092         }
44093         
44094         this.setCurrency(currency);
44095     },
44096     
44097     setFromData : function(o)
44098     {
44099         var c = {};
44100         
44101         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44102         
44103         this.setFromCurrencyData(c);
44104         
44105         var value = '';
44106         
44107         if (this.name) {
44108             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44109         } else {
44110             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44111         }
44112         
44113         this.setValue(value);
44114         
44115     },
44116     
44117     setCurrency : function(v)
44118     {   
44119         this.currencyValue = v;
44120         
44121         if(this.rendered){
44122             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44123             this.validate();
44124         }
44125     },
44126     
44127     setValue : function(v)
44128     {
44129         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44130         
44131         this.value = v;
44132         
44133         if(this.rendered){
44134             
44135             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44136             
44137             this.inputEl().dom.value = (v == '') ? '' :
44138                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44139             
44140             if(!this.allowZero && v === '0') {
44141                 this.hiddenEl().dom.value = '';
44142                 this.inputEl().dom.value = '';
44143             }
44144             
44145             this.validate();
44146         }
44147     },
44148     
44149     getRawValue : function()
44150     {
44151         var v = this.inputEl().getValue();
44152         
44153         return v;
44154     },
44155     
44156     getValue : function()
44157     {
44158         return this.fixPrecision(this.parseValue(this.getRawValue()));
44159     },
44160     
44161     parseValue : function(value)
44162     {
44163         if(this.thousandsDelimiter) {
44164             value += "";
44165             r = new RegExp(",", "g");
44166             value = value.replace(r, "");
44167         }
44168         
44169         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44170         return isNaN(value) ? '' : value;
44171         
44172     },
44173     
44174     fixPrecision : function(value)
44175     {
44176         if(this.thousandsDelimiter) {
44177             value += "";
44178             r = new RegExp(",", "g");
44179             value = value.replace(r, "");
44180         }
44181         
44182         var nan = isNaN(value);
44183         
44184         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44185             return nan ? '' : value;
44186         }
44187         return parseFloat(value).toFixed(this.decimalPrecision);
44188     },
44189     
44190     decimalPrecisionFcn : function(v)
44191     {
44192         return Math.floor(v);
44193     },
44194     
44195     validateValue : function(value)
44196     {
44197         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44198             return false;
44199         }
44200         
44201         var num = this.parseValue(value);
44202         
44203         if(isNaN(num)){
44204             this.markInvalid(String.format(this.nanText, value));
44205             return false;
44206         }
44207         
44208         if(num < this.minValue){
44209             this.markInvalid(String.format(this.minText, this.minValue));
44210             return false;
44211         }
44212         
44213         if(num > this.maxValue){
44214             this.markInvalid(String.format(this.maxText, this.maxValue));
44215             return false;
44216         }
44217         
44218         return true;
44219     },
44220     
44221     validate : function()
44222     {
44223         if(this.disabled || this.allowBlank){
44224             this.markValid();
44225             return true;
44226         }
44227         
44228         var currency = this.getCurrency();
44229         
44230         if(this.validateValue(this.getRawValue()) && currency.length){
44231             this.markValid();
44232             return true;
44233         }
44234         
44235         this.markInvalid();
44236         return false;
44237     },
44238     
44239     getName: function()
44240     {
44241         return this.name;
44242     },
44243     
44244     beforeBlur : function()
44245     {
44246         if(!this.castInt){
44247             return;
44248         }
44249         
44250         var v = this.parseValue(this.getRawValue());
44251         
44252         if(v || v == 0){
44253             this.setValue(v);
44254         }
44255     },
44256     
44257     onBlur : function()
44258     {
44259         this.beforeBlur();
44260         
44261         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44262             //this.el.removeClass(this.focusClass);
44263         }
44264         
44265         this.hasFocus = false;
44266         
44267         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44268             this.validate();
44269         }
44270         
44271         var v = this.getValue();
44272         
44273         if(String(v) !== String(this.startValue)){
44274             this.fireEvent('change', this, v, this.startValue);
44275         }
44276         
44277         this.fireEvent("blur", this);
44278     },
44279     
44280     inputEl : function()
44281     {
44282         return this.el.select('.roo-money-amount-input', true).first();
44283     },
44284     
44285     currencyEl : function()
44286     {
44287         return this.el.select('.roo-money-currency-input', true).first();
44288     },
44289     
44290     hiddenEl : function()
44291     {
44292         return this.el.select('input.hidden-number-input',true).first();
44293     }
44294     
44295 });/**
44296  * @class Roo.bootstrap.BezierSignature
44297  * @extends Roo.bootstrap.Component
44298  * Bootstrap BezierSignature class
44299  * This script refer to:
44300  *    Title: Signature Pad
44301  *    Author: szimek
44302  *    Availability: https://github.com/szimek/signature_pad
44303  *
44304  * @constructor
44305  * Create a new BezierSignature
44306  * @param {Object} config The config object
44307  */
44308
44309 Roo.bootstrap.BezierSignature = function(config){
44310     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44311     this.addEvents({
44312         "resize" : true
44313     });
44314 };
44315
44316 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44317 {
44318      
44319     curve_data: [],
44320     
44321     is_empty: true,
44322     
44323     mouse_btn_down: true,
44324     
44325     /**
44326      * @cfg {int} canvas height
44327      */
44328     canvas_height: '200px',
44329     
44330     /**
44331      * @cfg {float|function} Radius of a single dot.
44332      */ 
44333     dot_size: false,
44334     
44335     /**
44336      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44337      */
44338     min_width: 0.5,
44339     
44340     /**
44341      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44342      */
44343     max_width: 2.5,
44344     
44345     /**
44346      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44347      */
44348     throttle: 16,
44349     
44350     /**
44351      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44352      */
44353     min_distance: 5,
44354     
44355     /**
44356      * @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.
44357      */
44358     bg_color: 'rgba(0, 0, 0, 0)',
44359     
44360     /**
44361      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44362      */
44363     dot_color: 'black',
44364     
44365     /**
44366      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44367      */ 
44368     velocity_filter_weight: 0.7,
44369     
44370     /**
44371      * @cfg {function} Callback when stroke begin. 
44372      */
44373     onBegin: false,
44374     
44375     /**
44376      * @cfg {function} Callback when stroke end.
44377      */
44378     onEnd: false,
44379     
44380     getAutoCreate : function()
44381     {
44382         var cls = 'roo-signature column';
44383         
44384         if(this.cls){
44385             cls += ' ' + this.cls;
44386         }
44387         
44388         var col_sizes = [
44389             'lg',
44390             'md',
44391             'sm',
44392             'xs'
44393         ];
44394         
44395         for(var i = 0; i < col_sizes.length; i++) {
44396             if(this[col_sizes[i]]) {
44397                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44398             }
44399         }
44400         
44401         var cfg = {
44402             tag: 'div',
44403             cls: cls,
44404             cn: [
44405                 {
44406                     tag: 'div',
44407                     cls: 'roo-signature-body',
44408                     cn: [
44409                         {
44410                             tag: 'canvas',
44411                             cls: 'roo-signature-body-canvas',
44412                             height: this.canvas_height,
44413                             width: this.canvas_width
44414                         }
44415                     ]
44416                 },
44417                 {
44418                     tag: 'input',
44419                     type: 'file',
44420                     style: 'display: none'
44421                 }
44422             ]
44423         };
44424         
44425         return cfg;
44426     },
44427     
44428     initEvents: function() 
44429     {
44430         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44431         
44432         var canvas = this.canvasEl();
44433         
44434         // mouse && touch event swapping...
44435         canvas.dom.style.touchAction = 'none';
44436         canvas.dom.style.msTouchAction = 'none';
44437         
44438         this.mouse_btn_down = false;
44439         canvas.on('mousedown', this._handleMouseDown, this);
44440         canvas.on('mousemove', this._handleMouseMove, this);
44441         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44442         
44443         if (window.PointerEvent) {
44444             canvas.on('pointerdown', this._handleMouseDown, this);
44445             canvas.on('pointermove', this._handleMouseMove, this);
44446             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44447         }
44448         
44449         if ('ontouchstart' in window) {
44450             canvas.on('touchstart', this._handleTouchStart, this);
44451             canvas.on('touchmove', this._handleTouchMove, this);
44452             canvas.on('touchend', this._handleTouchEnd, this);
44453         }
44454         
44455         Roo.EventManager.onWindowResize(this.resize, this, true);
44456         
44457         // file input event
44458         this.fileEl().on('change', this.uploadImage, this);
44459         
44460         this.clear();
44461         
44462         this.resize();
44463     },
44464     
44465     resize: function(){
44466         
44467         var canvas = this.canvasEl().dom;
44468         var ctx = this.canvasElCtx();
44469         var img_data = false;
44470         
44471         if(canvas.width > 0) {
44472             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44473         }
44474         // setting canvas width will clean img data
44475         canvas.width = 0;
44476         
44477         var style = window.getComputedStyle ? 
44478             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44479             
44480         var padding_left = parseInt(style.paddingLeft) || 0;
44481         var padding_right = parseInt(style.paddingRight) || 0;
44482         
44483         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44484         
44485         if(img_data) {
44486             ctx.putImageData(img_data, 0, 0);
44487         }
44488     },
44489     
44490     _handleMouseDown: function(e)
44491     {
44492         if (e.browserEvent.which === 1) {
44493             this.mouse_btn_down = true;
44494             this.strokeBegin(e);
44495         }
44496     },
44497     
44498     _handleMouseMove: function (e)
44499     {
44500         if (this.mouse_btn_down) {
44501             this.strokeMoveUpdate(e);
44502         }
44503     },
44504     
44505     _handleMouseUp: function (e)
44506     {
44507         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44508             this.mouse_btn_down = false;
44509             this.strokeEnd(e);
44510         }
44511     },
44512     
44513     _handleTouchStart: function (e) {
44514         
44515         e.preventDefault();
44516         if (e.browserEvent.targetTouches.length === 1) {
44517             // var touch = e.browserEvent.changedTouches[0];
44518             // this.strokeBegin(touch);
44519             
44520              this.strokeBegin(e); // assume e catching the correct xy...
44521         }
44522     },
44523     
44524     _handleTouchMove: function (e) {
44525         e.preventDefault();
44526         // var touch = event.targetTouches[0];
44527         // _this._strokeMoveUpdate(touch);
44528         this.strokeMoveUpdate(e);
44529     },
44530     
44531     _handleTouchEnd: function (e) {
44532         var wasCanvasTouched = e.target === this.canvasEl().dom;
44533         if (wasCanvasTouched) {
44534             e.preventDefault();
44535             // var touch = event.changedTouches[0];
44536             // _this._strokeEnd(touch);
44537             this.strokeEnd(e);
44538         }
44539     },
44540     
44541     reset: function () {
44542         this._lastPoints = [];
44543         this._lastVelocity = 0;
44544         this._lastWidth = (this.min_width + this.max_width) / 2;
44545         this.canvasElCtx().fillStyle = this.dot_color;
44546     },
44547     
44548     strokeMoveUpdate: function(e)
44549     {
44550         this.strokeUpdate(e);
44551         
44552         if (this.throttle) {
44553             this.throttleStroke(this.strokeUpdate, this.throttle);
44554         }
44555         else {
44556             this.strokeUpdate(e);
44557         }
44558     },
44559     
44560     strokeBegin: function(e)
44561     {
44562         var newPointGroup = {
44563             color: this.dot_color,
44564             points: []
44565         };
44566         
44567         if (typeof this.onBegin === 'function') {
44568             this.onBegin(e);
44569         }
44570         
44571         this.curve_data.push(newPointGroup);
44572         this.reset();
44573         this.strokeUpdate(e);
44574     },
44575     
44576     strokeUpdate: function(e)
44577     {
44578         var rect = this.canvasEl().dom.getBoundingClientRect();
44579         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44580         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44581         var lastPoints = lastPointGroup.points;
44582         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44583         var isLastPointTooClose = lastPoint
44584             ? point.distanceTo(lastPoint) <= this.min_distance
44585             : false;
44586         var color = lastPointGroup.color;
44587         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44588             var curve = this.addPoint(point);
44589             if (!lastPoint) {
44590                 this.drawDot({color: color, point: point});
44591             }
44592             else if (curve) {
44593                 this.drawCurve({color: color, curve: curve});
44594             }
44595             lastPoints.push({
44596                 time: point.time,
44597                 x: point.x,
44598                 y: point.y
44599             });
44600         }
44601     },
44602     
44603     strokeEnd: function(e)
44604     {
44605         this.strokeUpdate(e);
44606         if (typeof this.onEnd === 'function') {
44607             this.onEnd(e);
44608         }
44609     },
44610     
44611     addPoint:  function (point) {
44612         var _lastPoints = this._lastPoints;
44613         _lastPoints.push(point);
44614         if (_lastPoints.length > 2) {
44615             if (_lastPoints.length === 3) {
44616                 _lastPoints.unshift(_lastPoints[0]);
44617             }
44618             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44619             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44620             _lastPoints.shift();
44621             return curve;
44622         }
44623         return null;
44624     },
44625     
44626     calculateCurveWidths: function (startPoint, endPoint) {
44627         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44628             (1 - this.velocity_filter_weight) * this._lastVelocity;
44629
44630         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44631         var widths = {
44632             end: newWidth,
44633             start: this._lastWidth
44634         };
44635         
44636         this._lastVelocity = velocity;
44637         this._lastWidth = newWidth;
44638         return widths;
44639     },
44640     
44641     drawDot: function (_a) {
44642         var color = _a.color, point = _a.point;
44643         var ctx = this.canvasElCtx();
44644         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44645         ctx.beginPath();
44646         this.drawCurveSegment(point.x, point.y, width);
44647         ctx.closePath();
44648         ctx.fillStyle = color;
44649         ctx.fill();
44650     },
44651     
44652     drawCurve: function (_a) {
44653         var color = _a.color, curve = _a.curve;
44654         var ctx = this.canvasElCtx();
44655         var widthDelta = curve.endWidth - curve.startWidth;
44656         var drawSteps = Math.floor(curve.length()) * 2;
44657         ctx.beginPath();
44658         ctx.fillStyle = color;
44659         for (var i = 0; i < drawSteps; i += 1) {
44660         var t = i / drawSteps;
44661         var tt = t * t;
44662         var ttt = tt * t;
44663         var u = 1 - t;
44664         var uu = u * u;
44665         var uuu = uu * u;
44666         var x = uuu * curve.startPoint.x;
44667         x += 3 * uu * t * curve.control1.x;
44668         x += 3 * u * tt * curve.control2.x;
44669         x += ttt * curve.endPoint.x;
44670         var y = uuu * curve.startPoint.y;
44671         y += 3 * uu * t * curve.control1.y;
44672         y += 3 * u * tt * curve.control2.y;
44673         y += ttt * curve.endPoint.y;
44674         var width = curve.startWidth + ttt * widthDelta;
44675         this.drawCurveSegment(x, y, width);
44676         }
44677         ctx.closePath();
44678         ctx.fill();
44679     },
44680     
44681     drawCurveSegment: function (x, y, width) {
44682         var ctx = this.canvasElCtx();
44683         ctx.moveTo(x, y);
44684         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44685         this.is_empty = false;
44686     },
44687     
44688     clear: function()
44689     {
44690         var ctx = this.canvasElCtx();
44691         var canvas = this.canvasEl().dom;
44692         ctx.fillStyle = this.bg_color;
44693         ctx.clearRect(0, 0, canvas.width, canvas.height);
44694         ctx.fillRect(0, 0, canvas.width, canvas.height);
44695         this.curve_data = [];
44696         this.reset();
44697         this.is_empty = true;
44698     },
44699     
44700     fileEl: function()
44701     {
44702         return  this.el.select('input',true).first();
44703     },
44704     
44705     canvasEl: function()
44706     {
44707         return this.el.select('canvas',true).first();
44708     },
44709     
44710     canvasElCtx: function()
44711     {
44712         return this.el.select('canvas',true).first().dom.getContext('2d');
44713     },
44714     
44715     getImage: function(type)
44716     {
44717         if(this.is_empty) {
44718             return false;
44719         }
44720         
44721         // encryption ?
44722         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44723     },
44724     
44725     drawFromImage: function(img_src)
44726     {
44727         var img = new Image();
44728         
44729         img.onload = function(){
44730             this.canvasElCtx().drawImage(img, 0, 0);
44731         }.bind(this);
44732         
44733         img.src = img_src;
44734         
44735         this.is_empty = false;
44736     },
44737     
44738     selectImage: function()
44739     {
44740         this.fileEl().dom.click();
44741     },
44742     
44743     uploadImage: function(e)
44744     {
44745         var reader = new FileReader();
44746         
44747         reader.onload = function(e){
44748             var img = new Image();
44749             img.onload = function(){
44750                 this.reset();
44751                 this.canvasElCtx().drawImage(img, 0, 0);
44752             }.bind(this);
44753             img.src = e.target.result;
44754         }.bind(this);
44755         
44756         reader.readAsDataURL(e.target.files[0]);
44757     },
44758     
44759     // Bezier Point Constructor
44760     Point: (function () {
44761         function Point(x, y, time) {
44762             this.x = x;
44763             this.y = y;
44764             this.time = time || Date.now();
44765         }
44766         Point.prototype.distanceTo = function (start) {
44767             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44768         };
44769         Point.prototype.equals = function (other) {
44770             return this.x === other.x && this.y === other.y && this.time === other.time;
44771         };
44772         Point.prototype.velocityFrom = function (start) {
44773             return this.time !== start.time
44774             ? this.distanceTo(start) / (this.time - start.time)
44775             : 0;
44776         };
44777         return Point;
44778     }()),
44779     
44780     
44781     // Bezier Constructor
44782     Bezier: (function () {
44783         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44784             this.startPoint = startPoint;
44785             this.control2 = control2;
44786             this.control1 = control1;
44787             this.endPoint = endPoint;
44788             this.startWidth = startWidth;
44789             this.endWidth = endWidth;
44790         }
44791         Bezier.fromPoints = function (points, widths, scope) {
44792             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44793             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44794             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44795         };
44796         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44797             var dx1 = s1.x - s2.x;
44798             var dy1 = s1.y - s2.y;
44799             var dx2 = s2.x - s3.x;
44800             var dy2 = s2.y - s3.y;
44801             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44802             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44803             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44804             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44805             var dxm = m1.x - m2.x;
44806             var dym = m1.y - m2.y;
44807             var k = l2 / (l1 + l2);
44808             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44809             var tx = s2.x - cm.x;
44810             var ty = s2.y - cm.y;
44811             return {
44812                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44813                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44814             };
44815         };
44816         Bezier.prototype.length = function () {
44817             var steps = 10;
44818             var length = 0;
44819             var px;
44820             var py;
44821             for (var i = 0; i <= steps; i += 1) {
44822                 var t = i / steps;
44823                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44824                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44825                 if (i > 0) {
44826                     var xdiff = cx - px;
44827                     var ydiff = cy - py;
44828                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44829                 }
44830                 px = cx;
44831                 py = cy;
44832             }
44833             return length;
44834         };
44835         Bezier.prototype.point = function (t, start, c1, c2, end) {
44836             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44837             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44838             + (3.0 * c2 * (1.0 - t) * t * t)
44839             + (end * t * t * t);
44840         };
44841         return Bezier;
44842     }()),
44843     
44844     throttleStroke: function(fn, wait) {
44845       if (wait === void 0) { wait = 250; }
44846       var previous = 0;
44847       var timeout = null;
44848       var result;
44849       var storedContext;
44850       var storedArgs;
44851       var later = function () {
44852           previous = Date.now();
44853           timeout = null;
44854           result = fn.apply(storedContext, storedArgs);
44855           if (!timeout) {
44856               storedContext = null;
44857               storedArgs = [];
44858           }
44859       };
44860       return function wrapper() {
44861           var args = [];
44862           for (var _i = 0; _i < arguments.length; _i++) {
44863               args[_i] = arguments[_i];
44864           }
44865           var now = Date.now();
44866           var remaining = wait - (now - previous);
44867           storedContext = this;
44868           storedArgs = args;
44869           if (remaining <= 0 || remaining > wait) {
44870               if (timeout) {
44871                   clearTimeout(timeout);
44872                   timeout = null;
44873               }
44874               previous = now;
44875               result = fn.apply(storedContext, storedArgs);
44876               if (!timeout) {
44877                   storedContext = null;
44878                   storedArgs = [];
44879               }
44880           }
44881           else if (!timeout) {
44882               timeout = window.setTimeout(later, remaining);
44883           }
44884           return result;
44885       };
44886   }
44887   
44888 });
44889
44890  
44891
44892