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.popups.length &&  !e.getTarget(".roo-popover")) {
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,  popup);
20423         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
20424         this.hideAll(); //<< why?
20425         //this.popups.push(popup);
20426     },
20427     hideAll : function()
20428     {
20429         this.popups.forEach(function(p) {
20430             p.hide();
20431         });
20432     },
20433     onShow : function() {
20434         Roo.bootstrap.Popover.popups.push(this);
20435     },
20436     onHide : function() {
20437         Roo.bootstrap.Popover.popups.remove(this);
20438     } 
20439
20440 });/*
20441  * - LGPL
20442  *
20443  * Card header - holder for the card header elements.
20444  * 
20445  */
20446
20447 /**
20448  * @class Roo.bootstrap.PopoverNav
20449  * @extends Roo.bootstrap.NavGroup
20450  * Bootstrap Popover header navigation class
20451  * @constructor
20452  * Create a new Popover Header Navigation 
20453  * @param {Object} config The config object
20454  */
20455
20456 Roo.bootstrap.PopoverNav = function(config){
20457     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20458 };
20459
20460 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20461     
20462     
20463     container_method : 'getPopoverHeader' 
20464     
20465      
20466     
20467     
20468    
20469 });
20470
20471  
20472
20473  /*
20474  * - LGPL
20475  *
20476  * Progress
20477  * 
20478  */
20479
20480 /**
20481  * @class Roo.bootstrap.Progress
20482  * @extends Roo.bootstrap.Component
20483  * Bootstrap Progress class
20484  * @cfg {Boolean} striped striped of the progress bar
20485  * @cfg {Boolean} active animated of the progress bar
20486  * 
20487  * 
20488  * @constructor
20489  * Create a new Progress
20490  * @param {Object} config The config object
20491  */
20492
20493 Roo.bootstrap.Progress = function(config){
20494     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20495 };
20496
20497 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20498     
20499     striped : false,
20500     active: false,
20501     
20502     getAutoCreate : function(){
20503         var cfg = {
20504             tag: 'div',
20505             cls: 'progress'
20506         };
20507         
20508         
20509         if(this.striped){
20510             cfg.cls += ' progress-striped';
20511         }
20512       
20513         if(this.active){
20514             cfg.cls += ' active';
20515         }
20516         
20517         
20518         return cfg;
20519     }
20520    
20521 });
20522
20523  
20524
20525  /*
20526  * - LGPL
20527  *
20528  * ProgressBar
20529  * 
20530  */
20531
20532 /**
20533  * @class Roo.bootstrap.ProgressBar
20534  * @extends Roo.bootstrap.Component
20535  * Bootstrap ProgressBar class
20536  * @cfg {Number} aria_valuenow aria-value now
20537  * @cfg {Number} aria_valuemin aria-value min
20538  * @cfg {Number} aria_valuemax aria-value max
20539  * @cfg {String} label label for the progress bar
20540  * @cfg {String} panel (success | info | warning | danger )
20541  * @cfg {String} role role of the progress bar
20542  * @cfg {String} sr_only text
20543  * 
20544  * 
20545  * @constructor
20546  * Create a new ProgressBar
20547  * @param {Object} config The config object
20548  */
20549
20550 Roo.bootstrap.ProgressBar = function(config){
20551     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20552 };
20553
20554 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20555     
20556     aria_valuenow : 0,
20557     aria_valuemin : 0,
20558     aria_valuemax : 100,
20559     label : false,
20560     panel : false,
20561     role : false,
20562     sr_only: false,
20563     
20564     getAutoCreate : function()
20565     {
20566         
20567         var cfg = {
20568             tag: 'div',
20569             cls: 'progress-bar',
20570             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20571         };
20572         
20573         if(this.sr_only){
20574             cfg.cn = {
20575                 tag: 'span',
20576                 cls: 'sr-only',
20577                 html: this.sr_only
20578             }
20579         }
20580         
20581         if(this.role){
20582             cfg.role = this.role;
20583         }
20584         
20585         if(this.aria_valuenow){
20586             cfg['aria-valuenow'] = this.aria_valuenow;
20587         }
20588         
20589         if(this.aria_valuemin){
20590             cfg['aria-valuemin'] = this.aria_valuemin;
20591         }
20592         
20593         if(this.aria_valuemax){
20594             cfg['aria-valuemax'] = this.aria_valuemax;
20595         }
20596         
20597         if(this.label && !this.sr_only){
20598             cfg.html = this.label;
20599         }
20600         
20601         if(this.panel){
20602             cfg.cls += ' progress-bar-' + this.panel;
20603         }
20604         
20605         return cfg;
20606     },
20607     
20608     update : function(aria_valuenow)
20609     {
20610         this.aria_valuenow = aria_valuenow;
20611         
20612         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20613     }
20614    
20615 });
20616
20617  
20618
20619  /*
20620  * - LGPL
20621  *
20622  * column
20623  * 
20624  */
20625
20626 /**
20627  * @class Roo.bootstrap.TabGroup
20628  * @extends Roo.bootstrap.Column
20629  * Bootstrap Column class
20630  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20631  * @cfg {Boolean} carousel true to make the group behave like a carousel
20632  * @cfg {Boolean} bullets show bullets for the panels
20633  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20634  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20635  * @cfg {Boolean} showarrow (true|false) show arrow default true
20636  * 
20637  * @constructor
20638  * Create a new TabGroup
20639  * @param {Object} config The config object
20640  */
20641
20642 Roo.bootstrap.TabGroup = function(config){
20643     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20644     if (!this.navId) {
20645         this.navId = Roo.id();
20646     }
20647     this.tabs = [];
20648     Roo.bootstrap.TabGroup.register(this);
20649     
20650 };
20651
20652 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20653     
20654     carousel : false,
20655     transition : false,
20656     bullets : 0,
20657     timer : 0,
20658     autoslide : false,
20659     slideFn : false,
20660     slideOnTouch : false,
20661     showarrow : true,
20662     
20663     getAutoCreate : function()
20664     {
20665         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20666         
20667         cfg.cls += ' tab-content';
20668         
20669         if (this.carousel) {
20670             cfg.cls += ' carousel slide';
20671             
20672             cfg.cn = [{
20673                cls : 'carousel-inner',
20674                cn : []
20675             }];
20676         
20677             if(this.bullets  && !Roo.isTouch){
20678                 
20679                 var bullets = {
20680                     cls : 'carousel-bullets',
20681                     cn : []
20682                 };
20683                
20684                 if(this.bullets_cls){
20685                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20686                 }
20687                 
20688                 bullets.cn.push({
20689                     cls : 'clear'
20690                 });
20691                 
20692                 cfg.cn[0].cn.push(bullets);
20693             }
20694             
20695             if(this.showarrow){
20696                 cfg.cn[0].cn.push({
20697                     tag : 'div',
20698                     class : 'carousel-arrow',
20699                     cn : [
20700                         {
20701                             tag : 'div',
20702                             class : 'carousel-prev',
20703                             cn : [
20704                                 {
20705                                     tag : 'i',
20706                                     class : 'fa fa-chevron-left'
20707                                 }
20708                             ]
20709                         },
20710                         {
20711                             tag : 'div',
20712                             class : 'carousel-next',
20713                             cn : [
20714                                 {
20715                                     tag : 'i',
20716                                     class : 'fa fa-chevron-right'
20717                                 }
20718                             ]
20719                         }
20720                     ]
20721                 });
20722             }
20723             
20724         }
20725         
20726         return cfg;
20727     },
20728     
20729     initEvents:  function()
20730     {
20731 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20732 //            this.el.on("touchstart", this.onTouchStart, this);
20733 //        }
20734         
20735         if(this.autoslide){
20736             var _this = this;
20737             
20738             this.slideFn = window.setInterval(function() {
20739                 _this.showPanelNext();
20740             }, this.timer);
20741         }
20742         
20743         if(this.showarrow){
20744             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20745             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20746         }
20747         
20748         
20749     },
20750     
20751 //    onTouchStart : function(e, el, o)
20752 //    {
20753 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20754 //            return;
20755 //        }
20756 //        
20757 //        this.showPanelNext();
20758 //    },
20759     
20760     
20761     getChildContainer : function()
20762     {
20763         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20764     },
20765     
20766     /**
20767     * register a Navigation item
20768     * @param {Roo.bootstrap.NavItem} the navitem to add
20769     */
20770     register : function(item)
20771     {
20772         this.tabs.push( item);
20773         item.navId = this.navId; // not really needed..
20774         this.addBullet();
20775     
20776     },
20777     
20778     getActivePanel : function()
20779     {
20780         var r = false;
20781         Roo.each(this.tabs, function(t) {
20782             if (t.active) {
20783                 r = t;
20784                 return false;
20785             }
20786             return null;
20787         });
20788         return r;
20789         
20790     },
20791     getPanelByName : function(n)
20792     {
20793         var r = false;
20794         Roo.each(this.tabs, function(t) {
20795             if (t.tabId == n) {
20796                 r = t;
20797                 return false;
20798             }
20799             return null;
20800         });
20801         return r;
20802     },
20803     indexOfPanel : function(p)
20804     {
20805         var r = false;
20806         Roo.each(this.tabs, function(t,i) {
20807             if (t.tabId == p.tabId) {
20808                 r = i;
20809                 return false;
20810             }
20811             return null;
20812         });
20813         return r;
20814     },
20815     /**
20816      * show a specific panel
20817      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20818      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20819      */
20820     showPanel : function (pan)
20821     {
20822         if(this.transition || typeof(pan) == 'undefined'){
20823             Roo.log("waiting for the transitionend");
20824             return false;
20825         }
20826         
20827         if (typeof(pan) == 'number') {
20828             pan = this.tabs[pan];
20829         }
20830         
20831         if (typeof(pan) == 'string') {
20832             pan = this.getPanelByName(pan);
20833         }
20834         
20835         var cur = this.getActivePanel();
20836         
20837         if(!pan || !cur){
20838             Roo.log('pan or acitve pan is undefined');
20839             return false;
20840         }
20841         
20842         if (pan.tabId == this.getActivePanel().tabId) {
20843             return true;
20844         }
20845         
20846         if (false === cur.fireEvent('beforedeactivate')) {
20847             return false;
20848         }
20849         
20850         if(this.bullets > 0 && !Roo.isTouch){
20851             this.setActiveBullet(this.indexOfPanel(pan));
20852         }
20853         
20854         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20855             
20856             //class="carousel-item carousel-item-next carousel-item-left"
20857             
20858             this.transition = true;
20859             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20860             var lr = dir == 'next' ? 'left' : 'right';
20861             pan.el.addClass(dir); // or prev
20862             pan.el.addClass('carousel-item-' + dir); // or prev
20863             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20864             cur.el.addClass(lr); // or right
20865             pan.el.addClass(lr);
20866             cur.el.addClass('carousel-item-' +lr); // or right
20867             pan.el.addClass('carousel-item-' +lr);
20868             
20869             
20870             var _this = this;
20871             cur.el.on('transitionend', function() {
20872                 Roo.log("trans end?");
20873                 
20874                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20875                 pan.setActive(true);
20876                 
20877                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20878                 cur.setActive(false);
20879                 
20880                 _this.transition = false;
20881                 
20882             }, this, { single:  true } );
20883             
20884             return true;
20885         }
20886         
20887         cur.setActive(false);
20888         pan.setActive(true);
20889         
20890         return true;
20891         
20892     },
20893     showPanelNext : function()
20894     {
20895         var i = this.indexOfPanel(this.getActivePanel());
20896         
20897         if (i >= this.tabs.length - 1 && !this.autoslide) {
20898             return;
20899         }
20900         
20901         if (i >= this.tabs.length - 1 && this.autoslide) {
20902             i = -1;
20903         }
20904         
20905         this.showPanel(this.tabs[i+1]);
20906     },
20907     
20908     showPanelPrev : function()
20909     {
20910         var i = this.indexOfPanel(this.getActivePanel());
20911         
20912         if (i  < 1 && !this.autoslide) {
20913             return;
20914         }
20915         
20916         if (i < 1 && this.autoslide) {
20917             i = this.tabs.length;
20918         }
20919         
20920         this.showPanel(this.tabs[i-1]);
20921     },
20922     
20923     
20924     addBullet: function()
20925     {
20926         if(!this.bullets || Roo.isTouch){
20927             return;
20928         }
20929         var ctr = this.el.select('.carousel-bullets',true).first();
20930         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20931         var bullet = ctr.createChild({
20932             cls : 'bullet bullet-' + i
20933         },ctr.dom.lastChild);
20934         
20935         
20936         var _this = this;
20937         
20938         bullet.on('click', (function(e, el, o, ii, t){
20939
20940             e.preventDefault();
20941
20942             this.showPanel(ii);
20943
20944             if(this.autoslide && this.slideFn){
20945                 clearInterval(this.slideFn);
20946                 this.slideFn = window.setInterval(function() {
20947                     _this.showPanelNext();
20948                 }, this.timer);
20949             }
20950
20951         }).createDelegate(this, [i, bullet], true));
20952                 
20953         
20954     },
20955      
20956     setActiveBullet : function(i)
20957     {
20958         if(Roo.isTouch){
20959             return;
20960         }
20961         
20962         Roo.each(this.el.select('.bullet', true).elements, function(el){
20963             el.removeClass('selected');
20964         });
20965
20966         var bullet = this.el.select('.bullet-' + i, true).first();
20967         
20968         if(!bullet){
20969             return;
20970         }
20971         
20972         bullet.addClass('selected');
20973     }
20974     
20975     
20976   
20977 });
20978
20979  
20980
20981  
20982  
20983 Roo.apply(Roo.bootstrap.TabGroup, {
20984     
20985     groups: {},
20986      /**
20987     * register a Navigation Group
20988     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20989     */
20990     register : function(navgrp)
20991     {
20992         this.groups[navgrp.navId] = navgrp;
20993         
20994     },
20995     /**
20996     * fetch a Navigation Group based on the navigation ID
20997     * if one does not exist , it will get created.
20998     * @param {string} the navgroup to add
20999     * @returns {Roo.bootstrap.NavGroup} the navgroup 
21000     */
21001     get: function(navId) {
21002         if (typeof(this.groups[navId]) == 'undefined') {
21003             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21004         }
21005         return this.groups[navId] ;
21006     }
21007     
21008     
21009     
21010 });
21011
21012  /*
21013  * - LGPL
21014  *
21015  * TabPanel
21016  * 
21017  */
21018
21019 /**
21020  * @class Roo.bootstrap.TabPanel
21021  * @extends Roo.bootstrap.Component
21022  * Bootstrap TabPanel class
21023  * @cfg {Boolean} active panel active
21024  * @cfg {String} html panel content
21025  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21026  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21027  * @cfg {String} href click to link..
21028  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21029  * 
21030  * 
21031  * @constructor
21032  * Create a new TabPanel
21033  * @param {Object} config The config object
21034  */
21035
21036 Roo.bootstrap.TabPanel = function(config){
21037     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21038     this.addEvents({
21039         /**
21040              * @event changed
21041              * Fires when the active status changes
21042              * @param {Roo.bootstrap.TabPanel} this
21043              * @param {Boolean} state the new state
21044             
21045          */
21046         'changed': true,
21047         /**
21048              * @event beforedeactivate
21049              * Fires before a tab is de-activated - can be used to do validation on a form.
21050              * @param {Roo.bootstrap.TabPanel} this
21051              * @return {Boolean} false if there is an error
21052             
21053          */
21054         'beforedeactivate': true
21055      });
21056     
21057     this.tabId = this.tabId || Roo.id();
21058   
21059 };
21060
21061 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21062     
21063     active: false,
21064     html: false,
21065     tabId: false,
21066     navId : false,
21067     href : '',
21068     touchSlide : false,
21069     getAutoCreate : function(){
21070         
21071         
21072         var cfg = {
21073             tag: 'div',
21074             // item is needed for carousel - not sure if it has any effect otherwise
21075             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21076             html: this.html || ''
21077         };
21078         
21079         if(this.active){
21080             cfg.cls += ' active';
21081         }
21082         
21083         if(this.tabId){
21084             cfg.tabId = this.tabId;
21085         }
21086         
21087         
21088         
21089         return cfg;
21090     },
21091     
21092     initEvents:  function()
21093     {
21094         var p = this.parent();
21095         
21096         this.navId = this.navId || p.navId;
21097         
21098         if (typeof(this.navId) != 'undefined') {
21099             // not really needed.. but just in case.. parent should be a NavGroup.
21100             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21101             
21102             tg.register(this);
21103             
21104             var i = tg.tabs.length - 1;
21105             
21106             if(this.active && tg.bullets > 0 && i < tg.bullets){
21107                 tg.setActiveBullet(i);
21108             }
21109         }
21110         
21111         this.el.on('click', this.onClick, this);
21112         
21113         if(Roo.isTouch && this.touchSlide){
21114             this.el.on("touchstart", this.onTouchStart, this);
21115             this.el.on("touchmove", this.onTouchMove, this);
21116             this.el.on("touchend", this.onTouchEnd, this);
21117         }
21118         
21119     },
21120     
21121     onRender : function(ct, position)
21122     {
21123         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21124     },
21125     
21126     setActive : function(state)
21127     {
21128         Roo.log("panel - set active " + this.tabId + "=" + state);
21129         
21130         this.active = state;
21131         if (!state) {
21132             this.el.removeClass('active');
21133             
21134         } else  if (!this.el.hasClass('active')) {
21135             this.el.addClass('active');
21136         }
21137         
21138         this.fireEvent('changed', this, state);
21139     },
21140     
21141     onClick : function(e)
21142     {
21143         e.preventDefault();
21144         
21145         if(!this.href.length){
21146             return;
21147         }
21148         
21149         window.location.href = this.href;
21150     },
21151     
21152     startX : 0,
21153     startY : 0,
21154     endX : 0,
21155     endY : 0,
21156     swiping : false,
21157     
21158     onTouchStart : function(e)
21159     {
21160         this.swiping = false;
21161         
21162         this.startX = e.browserEvent.touches[0].clientX;
21163         this.startY = e.browserEvent.touches[0].clientY;
21164     },
21165     
21166     onTouchMove : function(e)
21167     {
21168         this.swiping = true;
21169         
21170         this.endX = e.browserEvent.touches[0].clientX;
21171         this.endY = e.browserEvent.touches[0].clientY;
21172     },
21173     
21174     onTouchEnd : function(e)
21175     {
21176         if(!this.swiping){
21177             this.onClick(e);
21178             return;
21179         }
21180         
21181         var tabGroup = this.parent();
21182         
21183         if(this.endX > this.startX){ // swiping right
21184             tabGroup.showPanelPrev();
21185             return;
21186         }
21187         
21188         if(this.startX > this.endX){ // swiping left
21189             tabGroup.showPanelNext();
21190             return;
21191         }
21192     }
21193     
21194     
21195 });
21196  
21197
21198  
21199
21200  /*
21201  * - LGPL
21202  *
21203  * DateField
21204  * 
21205  */
21206
21207 /**
21208  * @class Roo.bootstrap.DateField
21209  * @extends Roo.bootstrap.Input
21210  * Bootstrap DateField class
21211  * @cfg {Number} weekStart default 0
21212  * @cfg {String} viewMode default empty, (months|years)
21213  * @cfg {String} minViewMode default empty, (months|years)
21214  * @cfg {Number} startDate default -Infinity
21215  * @cfg {Number} endDate default Infinity
21216  * @cfg {Boolean} todayHighlight default false
21217  * @cfg {Boolean} todayBtn default false
21218  * @cfg {Boolean} calendarWeeks default false
21219  * @cfg {Object} daysOfWeekDisabled default empty
21220  * @cfg {Boolean} singleMode default false (true | false)
21221  * 
21222  * @cfg {Boolean} keyboardNavigation default true
21223  * @cfg {String} language default en
21224  * 
21225  * @constructor
21226  * Create a new DateField
21227  * @param {Object} config The config object
21228  */
21229
21230 Roo.bootstrap.DateField = function(config){
21231     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21232      this.addEvents({
21233             /**
21234              * @event show
21235              * Fires when this field show.
21236              * @param {Roo.bootstrap.DateField} this
21237              * @param {Mixed} date The date value
21238              */
21239             show : true,
21240             /**
21241              * @event show
21242              * Fires when this field hide.
21243              * @param {Roo.bootstrap.DateField} this
21244              * @param {Mixed} date The date value
21245              */
21246             hide : true,
21247             /**
21248              * @event select
21249              * Fires when select a date.
21250              * @param {Roo.bootstrap.DateField} this
21251              * @param {Mixed} date The date value
21252              */
21253             select : true,
21254             /**
21255              * @event beforeselect
21256              * Fires when before select a date.
21257              * @param {Roo.bootstrap.DateField} this
21258              * @param {Mixed} date The date value
21259              */
21260             beforeselect : true
21261         });
21262 };
21263
21264 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21265     
21266     /**
21267      * @cfg {String} format
21268      * The default date format string which can be overriden for localization support.  The format must be
21269      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21270      */
21271     format : "m/d/y",
21272     /**
21273      * @cfg {String} altFormats
21274      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21275      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21276      */
21277     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21278     
21279     weekStart : 0,
21280     
21281     viewMode : '',
21282     
21283     minViewMode : '',
21284     
21285     todayHighlight : false,
21286     
21287     todayBtn: false,
21288     
21289     language: 'en',
21290     
21291     keyboardNavigation: true,
21292     
21293     calendarWeeks: false,
21294     
21295     startDate: -Infinity,
21296     
21297     endDate: Infinity,
21298     
21299     daysOfWeekDisabled: [],
21300     
21301     _events: [],
21302     
21303     singleMode : false,
21304     
21305     UTCDate: function()
21306     {
21307         return new Date(Date.UTC.apply(Date, arguments));
21308     },
21309     
21310     UTCToday: function()
21311     {
21312         var today = new Date();
21313         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21314     },
21315     
21316     getDate: function() {
21317             var d = this.getUTCDate();
21318             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21319     },
21320     
21321     getUTCDate: function() {
21322             return this.date;
21323     },
21324     
21325     setDate: function(d) {
21326             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21327     },
21328     
21329     setUTCDate: function(d) {
21330             this.date = d;
21331             this.setValue(this.formatDate(this.date));
21332     },
21333         
21334     onRender: function(ct, position)
21335     {
21336         
21337         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21338         
21339         this.language = this.language || 'en';
21340         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21341         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21342         
21343         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21344         this.format = this.format || 'm/d/y';
21345         this.isInline = false;
21346         this.isInput = true;
21347         this.component = this.el.select('.add-on', true).first() || false;
21348         this.component = (this.component && this.component.length === 0) ? false : this.component;
21349         this.hasInput = this.component && this.inputEl().length;
21350         
21351         if (typeof(this.minViewMode === 'string')) {
21352             switch (this.minViewMode) {
21353                 case 'months':
21354                     this.minViewMode = 1;
21355                     break;
21356                 case 'years':
21357                     this.minViewMode = 2;
21358                     break;
21359                 default:
21360                     this.minViewMode = 0;
21361                     break;
21362             }
21363         }
21364         
21365         if (typeof(this.viewMode === 'string')) {
21366             switch (this.viewMode) {
21367                 case 'months':
21368                     this.viewMode = 1;
21369                     break;
21370                 case 'years':
21371                     this.viewMode = 2;
21372                     break;
21373                 default:
21374                     this.viewMode = 0;
21375                     break;
21376             }
21377         }
21378                 
21379         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21380         
21381 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21382         
21383         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21384         
21385         this.picker().on('mousedown', this.onMousedown, this);
21386         this.picker().on('click', this.onClick, this);
21387         
21388         this.picker().addClass('datepicker-dropdown');
21389         
21390         this.startViewMode = this.viewMode;
21391         
21392         if(this.singleMode){
21393             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21394                 v.setVisibilityMode(Roo.Element.DISPLAY);
21395                 v.hide();
21396             });
21397             
21398             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21399                 v.setStyle('width', '189px');
21400             });
21401         }
21402         
21403         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21404             if(!this.calendarWeeks){
21405                 v.remove();
21406                 return;
21407             }
21408             
21409             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21410             v.attr('colspan', function(i, val){
21411                 return parseInt(val) + 1;
21412             });
21413         });
21414                         
21415         
21416         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21417         
21418         this.setStartDate(this.startDate);
21419         this.setEndDate(this.endDate);
21420         
21421         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21422         
21423         this.fillDow();
21424         this.fillMonths();
21425         this.update();
21426         this.showMode();
21427         
21428         if(this.isInline) {
21429             this.showPopup();
21430         }
21431     },
21432     
21433     picker : function()
21434     {
21435         return this.pickerEl;
21436 //        return this.el.select('.datepicker', true).first();
21437     },
21438     
21439     fillDow: function()
21440     {
21441         var dowCnt = this.weekStart;
21442         
21443         var dow = {
21444             tag: 'tr',
21445             cn: [
21446                 
21447             ]
21448         };
21449         
21450         if(this.calendarWeeks){
21451             dow.cn.push({
21452                 tag: 'th',
21453                 cls: 'cw',
21454                 html: '&nbsp;'
21455             })
21456         }
21457         
21458         while (dowCnt < this.weekStart + 7) {
21459             dow.cn.push({
21460                 tag: 'th',
21461                 cls: 'dow',
21462                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21463             });
21464         }
21465         
21466         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21467     },
21468     
21469     fillMonths: function()
21470     {    
21471         var i = 0;
21472         var months = this.picker().select('>.datepicker-months td', true).first();
21473         
21474         months.dom.innerHTML = '';
21475         
21476         while (i < 12) {
21477             var month = {
21478                 tag: 'span',
21479                 cls: 'month',
21480                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21481             };
21482             
21483             months.createChild(month);
21484         }
21485         
21486     },
21487     
21488     update: function()
21489     {
21490         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;
21491         
21492         if (this.date < this.startDate) {
21493             this.viewDate = new Date(this.startDate);
21494         } else if (this.date > this.endDate) {
21495             this.viewDate = new Date(this.endDate);
21496         } else {
21497             this.viewDate = new Date(this.date);
21498         }
21499         
21500         this.fill();
21501     },
21502     
21503     fill: function() 
21504     {
21505         var d = new Date(this.viewDate),
21506                 year = d.getUTCFullYear(),
21507                 month = d.getUTCMonth(),
21508                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21509                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21510                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21511                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21512                 currentDate = this.date && this.date.valueOf(),
21513                 today = this.UTCToday();
21514         
21515         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21516         
21517 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21518         
21519 //        this.picker.select('>tfoot th.today').
21520 //                                              .text(dates[this.language].today)
21521 //                                              .toggle(this.todayBtn !== false);
21522     
21523         this.updateNavArrows();
21524         this.fillMonths();
21525                                                 
21526         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21527         
21528         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21529          
21530         prevMonth.setUTCDate(day);
21531         
21532         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21533         
21534         var nextMonth = new Date(prevMonth);
21535         
21536         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21537         
21538         nextMonth = nextMonth.valueOf();
21539         
21540         var fillMonths = false;
21541         
21542         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21543         
21544         while(prevMonth.valueOf() <= nextMonth) {
21545             var clsName = '';
21546             
21547             if (prevMonth.getUTCDay() === this.weekStart) {
21548                 if(fillMonths){
21549                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21550                 }
21551                     
21552                 fillMonths = {
21553                     tag: 'tr',
21554                     cn: []
21555                 };
21556                 
21557                 if(this.calendarWeeks){
21558                     // ISO 8601: First week contains first thursday.
21559                     // ISO also states week starts on Monday, but we can be more abstract here.
21560                     var
21561                     // Start of current week: based on weekstart/current date
21562                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21563                     // Thursday of this week
21564                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21565                     // First Thursday of year, year from thursday
21566                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21567                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21568                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21569                     
21570                     fillMonths.cn.push({
21571                         tag: 'td',
21572                         cls: 'cw',
21573                         html: calWeek
21574                     });
21575                 }
21576             }
21577             
21578             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21579                 clsName += ' old';
21580             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21581                 clsName += ' new';
21582             }
21583             if (this.todayHighlight &&
21584                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21585                 prevMonth.getUTCMonth() == today.getMonth() &&
21586                 prevMonth.getUTCDate() == today.getDate()) {
21587                 clsName += ' today';
21588             }
21589             
21590             if (currentDate && prevMonth.valueOf() === currentDate) {
21591                 clsName += ' active';
21592             }
21593             
21594             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21595                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21596                     clsName += ' disabled';
21597             }
21598             
21599             fillMonths.cn.push({
21600                 tag: 'td',
21601                 cls: 'day ' + clsName,
21602                 html: prevMonth.getDate()
21603             });
21604             
21605             prevMonth.setDate(prevMonth.getDate()+1);
21606         }
21607           
21608         var currentYear = this.date && this.date.getUTCFullYear();
21609         var currentMonth = this.date && this.date.getUTCMonth();
21610         
21611         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21612         
21613         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21614             v.removeClass('active');
21615             
21616             if(currentYear === year && k === currentMonth){
21617                 v.addClass('active');
21618             }
21619             
21620             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21621                 v.addClass('disabled');
21622             }
21623             
21624         });
21625         
21626         
21627         year = parseInt(year/10, 10) * 10;
21628         
21629         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21630         
21631         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21632         
21633         year -= 1;
21634         for (var i = -1; i < 11; i++) {
21635             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21636                 tag: 'span',
21637                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21638                 html: year
21639             });
21640             
21641             year += 1;
21642         }
21643     },
21644     
21645     showMode: function(dir) 
21646     {
21647         if (dir) {
21648             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21649         }
21650         
21651         Roo.each(this.picker().select('>div',true).elements, function(v){
21652             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21653             v.hide();
21654         });
21655         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21656     },
21657     
21658     place: function()
21659     {
21660         if(this.isInline) {
21661             return;
21662         }
21663         
21664         this.picker().removeClass(['bottom', 'top']);
21665         
21666         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21667             /*
21668              * place to the top of element!
21669              *
21670              */
21671             
21672             this.picker().addClass('top');
21673             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21674             
21675             return;
21676         }
21677         
21678         this.picker().addClass('bottom');
21679         
21680         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21681     },
21682     
21683     parseDate : function(value)
21684     {
21685         if(!value || value instanceof Date){
21686             return value;
21687         }
21688         var v = Date.parseDate(value, this.format);
21689         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21690             v = Date.parseDate(value, 'Y-m-d');
21691         }
21692         if(!v && this.altFormats){
21693             if(!this.altFormatsArray){
21694                 this.altFormatsArray = this.altFormats.split("|");
21695             }
21696             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21697                 v = Date.parseDate(value, this.altFormatsArray[i]);
21698             }
21699         }
21700         return v;
21701     },
21702     
21703     formatDate : function(date, fmt)
21704     {   
21705         return (!date || !(date instanceof Date)) ?
21706         date : date.dateFormat(fmt || this.format);
21707     },
21708     
21709     onFocus : function()
21710     {
21711         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21712         this.showPopup();
21713     },
21714     
21715     onBlur : function()
21716     {
21717         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21718         
21719         var d = this.inputEl().getValue();
21720         
21721         this.setValue(d);
21722                 
21723         this.hidePopup();
21724     },
21725     
21726     showPopup : function()
21727     {
21728         this.picker().show();
21729         this.update();
21730         this.place();
21731         
21732         this.fireEvent('showpopup', this, this.date);
21733     },
21734     
21735     hidePopup : function()
21736     {
21737         if(this.isInline) {
21738             return;
21739         }
21740         this.picker().hide();
21741         this.viewMode = this.startViewMode;
21742         this.showMode();
21743         
21744         this.fireEvent('hidepopup', this, this.date);
21745         
21746     },
21747     
21748     onMousedown: function(e)
21749     {
21750         e.stopPropagation();
21751         e.preventDefault();
21752     },
21753     
21754     keyup: function(e)
21755     {
21756         Roo.bootstrap.DateField.superclass.keyup.call(this);
21757         this.update();
21758     },
21759
21760     setValue: function(v)
21761     {
21762         if(this.fireEvent('beforeselect', this, v) !== false){
21763             var d = new Date(this.parseDate(v) ).clearTime();
21764         
21765             if(isNaN(d.getTime())){
21766                 this.date = this.viewDate = '';
21767                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21768                 return;
21769             }
21770
21771             v = this.formatDate(d);
21772
21773             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21774
21775             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21776
21777             this.update();
21778
21779             this.fireEvent('select', this, this.date);
21780         }
21781     },
21782     
21783     getValue: function()
21784     {
21785         return this.formatDate(this.date);
21786     },
21787     
21788     fireKey: function(e)
21789     {
21790         if (!this.picker().isVisible()){
21791             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21792                 this.showPopup();
21793             }
21794             return;
21795         }
21796         
21797         var dateChanged = false,
21798         dir, day, month,
21799         newDate, newViewDate;
21800         
21801         switch(e.keyCode){
21802             case 27: // escape
21803                 this.hidePopup();
21804                 e.preventDefault();
21805                 break;
21806             case 37: // left
21807             case 39: // right
21808                 if (!this.keyboardNavigation) {
21809                     break;
21810                 }
21811                 dir = e.keyCode == 37 ? -1 : 1;
21812                 
21813                 if (e.ctrlKey){
21814                     newDate = this.moveYear(this.date, dir);
21815                     newViewDate = this.moveYear(this.viewDate, dir);
21816                 } else if (e.shiftKey){
21817                     newDate = this.moveMonth(this.date, dir);
21818                     newViewDate = this.moveMonth(this.viewDate, dir);
21819                 } else {
21820                     newDate = new Date(this.date);
21821                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21822                     newViewDate = new Date(this.viewDate);
21823                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21824                 }
21825                 if (this.dateWithinRange(newDate)){
21826                     this.date = newDate;
21827                     this.viewDate = newViewDate;
21828                     this.setValue(this.formatDate(this.date));
21829 //                    this.update();
21830                     e.preventDefault();
21831                     dateChanged = true;
21832                 }
21833                 break;
21834             case 38: // up
21835             case 40: // down
21836                 if (!this.keyboardNavigation) {
21837                     break;
21838                 }
21839                 dir = e.keyCode == 38 ? -1 : 1;
21840                 if (e.ctrlKey){
21841                     newDate = this.moveYear(this.date, dir);
21842                     newViewDate = this.moveYear(this.viewDate, dir);
21843                 } else if (e.shiftKey){
21844                     newDate = this.moveMonth(this.date, dir);
21845                     newViewDate = this.moveMonth(this.viewDate, dir);
21846                 } else {
21847                     newDate = new Date(this.date);
21848                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21849                     newViewDate = new Date(this.viewDate);
21850                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21851                 }
21852                 if (this.dateWithinRange(newDate)){
21853                     this.date = newDate;
21854                     this.viewDate = newViewDate;
21855                     this.setValue(this.formatDate(this.date));
21856 //                    this.update();
21857                     e.preventDefault();
21858                     dateChanged = true;
21859                 }
21860                 break;
21861             case 13: // enter
21862                 this.setValue(this.formatDate(this.date));
21863                 this.hidePopup();
21864                 e.preventDefault();
21865                 break;
21866             case 9: // tab
21867                 this.setValue(this.formatDate(this.date));
21868                 this.hidePopup();
21869                 break;
21870             case 16: // shift
21871             case 17: // ctrl
21872             case 18: // alt
21873                 break;
21874             default :
21875                 this.hidePopup();
21876                 
21877         }
21878     },
21879     
21880     
21881     onClick: function(e) 
21882     {
21883         e.stopPropagation();
21884         e.preventDefault();
21885         
21886         var target = e.getTarget();
21887         
21888         if(target.nodeName.toLowerCase() === 'i'){
21889             target = Roo.get(target).dom.parentNode;
21890         }
21891         
21892         var nodeName = target.nodeName;
21893         var className = target.className;
21894         var html = target.innerHTML;
21895         //Roo.log(nodeName);
21896         
21897         switch(nodeName.toLowerCase()) {
21898             case 'th':
21899                 switch(className) {
21900                     case 'switch':
21901                         this.showMode(1);
21902                         break;
21903                     case 'prev':
21904                     case 'next':
21905                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21906                         switch(this.viewMode){
21907                                 case 0:
21908                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21909                                         break;
21910                                 case 1:
21911                                 case 2:
21912                                         this.viewDate = this.moveYear(this.viewDate, dir);
21913                                         break;
21914                         }
21915                         this.fill();
21916                         break;
21917                     case 'today':
21918                         var date = new Date();
21919                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21920 //                        this.fill()
21921                         this.setValue(this.formatDate(this.date));
21922                         
21923                         this.hidePopup();
21924                         break;
21925                 }
21926                 break;
21927             case 'span':
21928                 if (className.indexOf('disabled') < 0) {
21929                     this.viewDate.setUTCDate(1);
21930                     if (className.indexOf('month') > -1) {
21931                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21932                     } else {
21933                         var year = parseInt(html, 10) || 0;
21934                         this.viewDate.setUTCFullYear(year);
21935                         
21936                     }
21937                     
21938                     if(this.singleMode){
21939                         this.setValue(this.formatDate(this.viewDate));
21940                         this.hidePopup();
21941                         return;
21942                     }
21943                     
21944                     this.showMode(-1);
21945                     this.fill();
21946                 }
21947                 break;
21948                 
21949             case 'td':
21950                 //Roo.log(className);
21951                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21952                     var day = parseInt(html, 10) || 1;
21953                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21954                         month = (this.viewDate || new Date()).getUTCMonth();
21955
21956                     if (className.indexOf('old') > -1) {
21957                         if(month === 0 ){
21958                             month = 11;
21959                             year -= 1;
21960                         }else{
21961                             month -= 1;
21962                         }
21963                     } else if (className.indexOf('new') > -1) {
21964                         if (month == 11) {
21965                             month = 0;
21966                             year += 1;
21967                         } else {
21968                             month += 1;
21969                         }
21970                     }
21971                     //Roo.log([year,month,day]);
21972                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21973                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21974 //                    this.fill();
21975                     //Roo.log(this.formatDate(this.date));
21976                     this.setValue(this.formatDate(this.date));
21977                     this.hidePopup();
21978                 }
21979                 break;
21980         }
21981     },
21982     
21983     setStartDate: function(startDate)
21984     {
21985         this.startDate = startDate || -Infinity;
21986         if (this.startDate !== -Infinity) {
21987             this.startDate = this.parseDate(this.startDate);
21988         }
21989         this.update();
21990         this.updateNavArrows();
21991     },
21992
21993     setEndDate: function(endDate)
21994     {
21995         this.endDate = endDate || Infinity;
21996         if (this.endDate !== Infinity) {
21997             this.endDate = this.parseDate(this.endDate);
21998         }
21999         this.update();
22000         this.updateNavArrows();
22001     },
22002     
22003     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22004     {
22005         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22006         if (typeof(this.daysOfWeekDisabled) !== 'object') {
22007             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22008         }
22009         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22010             return parseInt(d, 10);
22011         });
22012         this.update();
22013         this.updateNavArrows();
22014     },
22015     
22016     updateNavArrows: function() 
22017     {
22018         if(this.singleMode){
22019             return;
22020         }
22021         
22022         var d = new Date(this.viewDate),
22023         year = d.getUTCFullYear(),
22024         month = d.getUTCMonth();
22025         
22026         Roo.each(this.picker().select('.prev', true).elements, function(v){
22027             v.show();
22028             switch (this.viewMode) {
22029                 case 0:
22030
22031                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22032                         v.hide();
22033                     }
22034                     break;
22035                 case 1:
22036                 case 2:
22037                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22038                         v.hide();
22039                     }
22040                     break;
22041             }
22042         });
22043         
22044         Roo.each(this.picker().select('.next', true).elements, function(v){
22045             v.show();
22046             switch (this.viewMode) {
22047                 case 0:
22048
22049                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22050                         v.hide();
22051                     }
22052                     break;
22053                 case 1:
22054                 case 2:
22055                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22056                         v.hide();
22057                     }
22058                     break;
22059             }
22060         })
22061     },
22062     
22063     moveMonth: function(date, dir)
22064     {
22065         if (!dir) {
22066             return date;
22067         }
22068         var new_date = new Date(date.valueOf()),
22069         day = new_date.getUTCDate(),
22070         month = new_date.getUTCMonth(),
22071         mag = Math.abs(dir),
22072         new_month, test;
22073         dir = dir > 0 ? 1 : -1;
22074         if (mag == 1){
22075             test = dir == -1
22076             // If going back one month, make sure month is not current month
22077             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22078             ? function(){
22079                 return new_date.getUTCMonth() == month;
22080             }
22081             // If going forward one month, make sure month is as expected
22082             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22083             : function(){
22084                 return new_date.getUTCMonth() != new_month;
22085             };
22086             new_month = month + dir;
22087             new_date.setUTCMonth(new_month);
22088             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22089             if (new_month < 0 || new_month > 11) {
22090                 new_month = (new_month + 12) % 12;
22091             }
22092         } else {
22093             // For magnitudes >1, move one month at a time...
22094             for (var i=0; i<mag; i++) {
22095                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22096                 new_date = this.moveMonth(new_date, dir);
22097             }
22098             // ...then reset the day, keeping it in the new month
22099             new_month = new_date.getUTCMonth();
22100             new_date.setUTCDate(day);
22101             test = function(){
22102                 return new_month != new_date.getUTCMonth();
22103             };
22104         }
22105         // Common date-resetting loop -- if date is beyond end of month, make it
22106         // end of month
22107         while (test()){
22108             new_date.setUTCDate(--day);
22109             new_date.setUTCMonth(new_month);
22110         }
22111         return new_date;
22112     },
22113
22114     moveYear: function(date, dir)
22115     {
22116         return this.moveMonth(date, dir*12);
22117     },
22118
22119     dateWithinRange: function(date)
22120     {
22121         return date >= this.startDate && date <= this.endDate;
22122     },
22123
22124     
22125     remove: function() 
22126     {
22127         this.picker().remove();
22128     },
22129     
22130     validateValue : function(value)
22131     {
22132         if(this.getVisibilityEl().hasClass('hidden')){
22133             return true;
22134         }
22135         
22136         if(value.length < 1)  {
22137             if(this.allowBlank){
22138                 return true;
22139             }
22140             return false;
22141         }
22142         
22143         if(value.length < this.minLength){
22144             return false;
22145         }
22146         if(value.length > this.maxLength){
22147             return false;
22148         }
22149         if(this.vtype){
22150             var vt = Roo.form.VTypes;
22151             if(!vt[this.vtype](value, this)){
22152                 return false;
22153             }
22154         }
22155         if(typeof this.validator == "function"){
22156             var msg = this.validator(value);
22157             if(msg !== true){
22158                 return false;
22159             }
22160         }
22161         
22162         if(this.regex && !this.regex.test(value)){
22163             return false;
22164         }
22165         
22166         if(typeof(this.parseDate(value)) == 'undefined'){
22167             return false;
22168         }
22169         
22170         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22171             return false;
22172         }      
22173         
22174         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22175             return false;
22176         } 
22177         
22178         
22179         return true;
22180     },
22181     
22182     reset : function()
22183     {
22184         this.date = this.viewDate = '';
22185         
22186         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22187     }
22188    
22189 });
22190
22191 Roo.apply(Roo.bootstrap.DateField,  {
22192     
22193     head : {
22194         tag: 'thead',
22195         cn: [
22196         {
22197             tag: 'tr',
22198             cn: [
22199             {
22200                 tag: 'th',
22201                 cls: 'prev',
22202                 html: '<i class="fa fa-arrow-left"/>'
22203             },
22204             {
22205                 tag: 'th',
22206                 cls: 'switch',
22207                 colspan: '5'
22208             },
22209             {
22210                 tag: 'th',
22211                 cls: 'next',
22212                 html: '<i class="fa fa-arrow-right"/>'
22213             }
22214
22215             ]
22216         }
22217         ]
22218     },
22219     
22220     content : {
22221         tag: 'tbody',
22222         cn: [
22223         {
22224             tag: 'tr',
22225             cn: [
22226             {
22227                 tag: 'td',
22228                 colspan: '7'
22229             }
22230             ]
22231         }
22232         ]
22233     },
22234     
22235     footer : {
22236         tag: 'tfoot',
22237         cn: [
22238         {
22239             tag: 'tr',
22240             cn: [
22241             {
22242                 tag: 'th',
22243                 colspan: '7',
22244                 cls: 'today'
22245             }
22246                     
22247             ]
22248         }
22249         ]
22250     },
22251     
22252     dates:{
22253         en: {
22254             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22255             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22256             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22257             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22258             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22259             today: "Today"
22260         }
22261     },
22262     
22263     modes: [
22264     {
22265         clsName: 'days',
22266         navFnc: 'Month',
22267         navStep: 1
22268     },
22269     {
22270         clsName: 'months',
22271         navFnc: 'FullYear',
22272         navStep: 1
22273     },
22274     {
22275         clsName: 'years',
22276         navFnc: 'FullYear',
22277         navStep: 10
22278     }]
22279 });
22280
22281 Roo.apply(Roo.bootstrap.DateField,  {
22282   
22283     template : {
22284         tag: 'div',
22285         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22286         cn: [
22287         {
22288             tag: 'div',
22289             cls: 'datepicker-days',
22290             cn: [
22291             {
22292                 tag: 'table',
22293                 cls: 'table-condensed',
22294                 cn:[
22295                 Roo.bootstrap.DateField.head,
22296                 {
22297                     tag: 'tbody'
22298                 },
22299                 Roo.bootstrap.DateField.footer
22300                 ]
22301             }
22302             ]
22303         },
22304         {
22305             tag: 'div',
22306             cls: 'datepicker-months',
22307             cn: [
22308             {
22309                 tag: 'table',
22310                 cls: 'table-condensed',
22311                 cn:[
22312                 Roo.bootstrap.DateField.head,
22313                 Roo.bootstrap.DateField.content,
22314                 Roo.bootstrap.DateField.footer
22315                 ]
22316             }
22317             ]
22318         },
22319         {
22320             tag: 'div',
22321             cls: 'datepicker-years',
22322             cn: [
22323             {
22324                 tag: 'table',
22325                 cls: 'table-condensed',
22326                 cn:[
22327                 Roo.bootstrap.DateField.head,
22328                 Roo.bootstrap.DateField.content,
22329                 Roo.bootstrap.DateField.footer
22330                 ]
22331             }
22332             ]
22333         }
22334         ]
22335     }
22336 });
22337
22338  
22339
22340  /*
22341  * - LGPL
22342  *
22343  * TimeField
22344  * 
22345  */
22346
22347 /**
22348  * @class Roo.bootstrap.TimeField
22349  * @extends Roo.bootstrap.Input
22350  * Bootstrap DateField class
22351  * 
22352  * 
22353  * @constructor
22354  * Create a new TimeField
22355  * @param {Object} config The config object
22356  */
22357
22358 Roo.bootstrap.TimeField = function(config){
22359     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22360     this.addEvents({
22361             /**
22362              * @event show
22363              * Fires when this field show.
22364              * @param {Roo.bootstrap.DateField} thisthis
22365              * @param {Mixed} date The date value
22366              */
22367             show : true,
22368             /**
22369              * @event show
22370              * Fires when this field hide.
22371              * @param {Roo.bootstrap.DateField} this
22372              * @param {Mixed} date The date value
22373              */
22374             hide : true,
22375             /**
22376              * @event select
22377              * Fires when select a date.
22378              * @param {Roo.bootstrap.DateField} this
22379              * @param {Mixed} date The date value
22380              */
22381             select : true
22382         });
22383 };
22384
22385 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22386     
22387     /**
22388      * @cfg {String} format
22389      * The default time format string which can be overriden for localization support.  The format must be
22390      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22391      */
22392     format : "H:i",
22393
22394     getAutoCreate : function()
22395     {
22396         this.after = '<i class="fa far fa-clock"></i>';
22397         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22398         
22399          
22400     },
22401     onRender: function(ct, position)
22402     {
22403         
22404         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22405                 
22406         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22407         
22408         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22409         
22410         this.pop = this.picker().select('>.datepicker-time',true).first();
22411         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22412         
22413         this.picker().on('mousedown', this.onMousedown, this);
22414         this.picker().on('click', this.onClick, this);
22415         
22416         this.picker().addClass('datepicker-dropdown');
22417     
22418         this.fillTime();
22419         this.update();
22420             
22421         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22422         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22423         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22424         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22425         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22426         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22427
22428     },
22429     
22430     fireKey: function(e){
22431         if (!this.picker().isVisible()){
22432             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22433                 this.show();
22434             }
22435             return;
22436         }
22437
22438         e.preventDefault();
22439         
22440         switch(e.keyCode){
22441             case 27: // escape
22442                 this.hide();
22443                 break;
22444             case 37: // left
22445             case 39: // right
22446                 this.onTogglePeriod();
22447                 break;
22448             case 38: // up
22449                 this.onIncrementMinutes();
22450                 break;
22451             case 40: // down
22452                 this.onDecrementMinutes();
22453                 break;
22454             case 13: // enter
22455             case 9: // tab
22456                 this.setTime();
22457                 break;
22458         }
22459     },
22460     
22461     onClick: function(e) {
22462         e.stopPropagation();
22463         e.preventDefault();
22464     },
22465     
22466     picker : function()
22467     {
22468         return this.pickerEl;
22469     },
22470     
22471     fillTime: function()
22472     {    
22473         var time = this.pop.select('tbody', true).first();
22474         
22475         time.dom.innerHTML = '';
22476         
22477         time.createChild({
22478             tag: 'tr',
22479             cn: [
22480                 {
22481                     tag: 'td',
22482                     cn: [
22483                         {
22484                             tag: 'a',
22485                             href: '#',
22486                             cls: 'btn',
22487                             cn: [
22488                                 {
22489                                     tag: 'i',
22490                                     cls: 'hours-up fa fas fa-chevron-up'
22491                                 }
22492                             ]
22493                         } 
22494                     ]
22495                 },
22496                 {
22497                     tag: 'td',
22498                     cls: 'separator'
22499                 },
22500                 {
22501                     tag: 'td',
22502                     cn: [
22503                         {
22504                             tag: 'a',
22505                             href: '#',
22506                             cls: 'btn',
22507                             cn: [
22508                                 {
22509                                     tag: 'i',
22510                                     cls: 'minutes-up fa fas fa-chevron-up'
22511                                 }
22512                             ]
22513                         }
22514                     ]
22515                 },
22516                 {
22517                     tag: 'td',
22518                     cls: 'separator'
22519                 }
22520             ]
22521         });
22522         
22523         time.createChild({
22524             tag: 'tr',
22525             cn: [
22526                 {
22527                     tag: 'td',
22528                     cn: [
22529                         {
22530                             tag: 'span',
22531                             cls: 'timepicker-hour',
22532                             html: '00'
22533                         }  
22534                     ]
22535                 },
22536                 {
22537                     tag: 'td',
22538                     cls: 'separator',
22539                     html: ':'
22540                 },
22541                 {
22542                     tag: 'td',
22543                     cn: [
22544                         {
22545                             tag: 'span',
22546                             cls: 'timepicker-minute',
22547                             html: '00'
22548                         }  
22549                     ]
22550                 },
22551                 {
22552                     tag: 'td',
22553                     cls: 'separator'
22554                 },
22555                 {
22556                     tag: 'td',
22557                     cn: [
22558                         {
22559                             tag: 'button',
22560                             type: 'button',
22561                             cls: 'btn btn-primary period',
22562                             html: 'AM'
22563                             
22564                         }
22565                     ]
22566                 }
22567             ]
22568         });
22569         
22570         time.createChild({
22571             tag: 'tr',
22572             cn: [
22573                 {
22574                     tag: 'td',
22575                     cn: [
22576                         {
22577                             tag: 'a',
22578                             href: '#',
22579                             cls: 'btn',
22580                             cn: [
22581                                 {
22582                                     tag: 'span',
22583                                     cls: 'hours-down fa fas fa-chevron-down'
22584                                 }
22585                             ]
22586                         }
22587                     ]
22588                 },
22589                 {
22590                     tag: 'td',
22591                     cls: 'separator'
22592                 },
22593                 {
22594                     tag: 'td',
22595                     cn: [
22596                         {
22597                             tag: 'a',
22598                             href: '#',
22599                             cls: 'btn',
22600                             cn: [
22601                                 {
22602                                     tag: 'span',
22603                                     cls: 'minutes-down fa fas fa-chevron-down'
22604                                 }
22605                             ]
22606                         }
22607                     ]
22608                 },
22609                 {
22610                     tag: 'td',
22611                     cls: 'separator'
22612                 }
22613             ]
22614         });
22615         
22616     },
22617     
22618     update: function()
22619     {
22620         
22621         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22622         
22623         this.fill();
22624     },
22625     
22626     fill: function() 
22627     {
22628         var hours = this.time.getHours();
22629         var minutes = this.time.getMinutes();
22630         var period = 'AM';
22631         
22632         if(hours > 11){
22633             period = 'PM';
22634         }
22635         
22636         if(hours == 0){
22637             hours = 12;
22638         }
22639         
22640         
22641         if(hours > 12){
22642             hours = hours - 12;
22643         }
22644         
22645         if(hours < 10){
22646             hours = '0' + hours;
22647         }
22648         
22649         if(minutes < 10){
22650             minutes = '0' + minutes;
22651         }
22652         
22653         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22654         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22655         this.pop.select('button', true).first().dom.innerHTML = period;
22656         
22657     },
22658     
22659     place: function()
22660     {   
22661         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22662         
22663         var cls = ['bottom'];
22664         
22665         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22666             cls.pop();
22667             cls.push('top');
22668         }
22669         
22670         cls.push('right');
22671         
22672         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22673             cls.pop();
22674             cls.push('left');
22675         }
22676         //this.picker().setXY(20000,20000);
22677         this.picker().addClass(cls.join('-'));
22678         
22679         var _this = this;
22680         
22681         Roo.each(cls, function(c){
22682             if(c == 'bottom'){
22683                 (function() {
22684                  //  
22685                 }).defer(200);
22686                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22687                 //_this.picker().setTop(_this.inputEl().getHeight());
22688                 return;
22689             }
22690             if(c == 'top'){
22691                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22692                 
22693                 //_this.picker().setTop(0 - _this.picker().getHeight());
22694                 return;
22695             }
22696             /*
22697             if(c == 'left'){
22698                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22699                 return;
22700             }
22701             if(c == 'right'){
22702                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22703                 return;
22704             }
22705             */
22706         });
22707         
22708     },
22709   
22710     onFocus : function()
22711     {
22712         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22713         this.show();
22714     },
22715     
22716     onBlur : function()
22717     {
22718         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22719         this.hide();
22720     },
22721     
22722     show : function()
22723     {
22724         this.picker().show();
22725         this.pop.show();
22726         this.update();
22727         this.place();
22728         
22729         this.fireEvent('show', this, this.date);
22730     },
22731     
22732     hide : function()
22733     {
22734         this.picker().hide();
22735         this.pop.hide();
22736         
22737         this.fireEvent('hide', this, this.date);
22738     },
22739     
22740     setTime : function()
22741     {
22742         this.hide();
22743         this.setValue(this.time.format(this.format));
22744         
22745         this.fireEvent('select', this, this.date);
22746         
22747         
22748     },
22749     
22750     onMousedown: function(e){
22751         e.stopPropagation();
22752         e.preventDefault();
22753     },
22754     
22755     onIncrementHours: function()
22756     {
22757         Roo.log('onIncrementHours');
22758         this.time = this.time.add(Date.HOUR, 1);
22759         this.update();
22760         
22761     },
22762     
22763     onDecrementHours: function()
22764     {
22765         Roo.log('onDecrementHours');
22766         this.time = this.time.add(Date.HOUR, -1);
22767         this.update();
22768     },
22769     
22770     onIncrementMinutes: function()
22771     {
22772         Roo.log('onIncrementMinutes');
22773         this.time = this.time.add(Date.MINUTE, 1);
22774         this.update();
22775     },
22776     
22777     onDecrementMinutes: function()
22778     {
22779         Roo.log('onDecrementMinutes');
22780         this.time = this.time.add(Date.MINUTE, -1);
22781         this.update();
22782     },
22783     
22784     onTogglePeriod: function()
22785     {
22786         Roo.log('onTogglePeriod');
22787         this.time = this.time.add(Date.HOUR, 12);
22788         this.update();
22789     }
22790     
22791    
22792 });
22793  
22794
22795 Roo.apply(Roo.bootstrap.TimeField,  {
22796   
22797     template : {
22798         tag: 'div',
22799         cls: 'datepicker dropdown-menu',
22800         cn: [
22801             {
22802                 tag: 'div',
22803                 cls: 'datepicker-time',
22804                 cn: [
22805                 {
22806                     tag: 'table',
22807                     cls: 'table-condensed',
22808                     cn:[
22809                         {
22810                             tag: 'tbody',
22811                             cn: [
22812                                 {
22813                                     tag: 'tr',
22814                                     cn: [
22815                                     {
22816                                         tag: 'td',
22817                                         colspan: '7'
22818                                     }
22819                                     ]
22820                                 }
22821                             ]
22822                         },
22823                         {
22824                             tag: 'tfoot',
22825                             cn: [
22826                                 {
22827                                     tag: 'tr',
22828                                     cn: [
22829                                     {
22830                                         tag: 'th',
22831                                         colspan: '7',
22832                                         cls: '',
22833                                         cn: [
22834                                             {
22835                                                 tag: 'button',
22836                                                 cls: 'btn btn-info ok',
22837                                                 html: 'OK'
22838                                             }
22839                                         ]
22840                                     }
22841                     
22842                                     ]
22843                                 }
22844                             ]
22845                         }
22846                     ]
22847                 }
22848                 ]
22849             }
22850         ]
22851     }
22852 });
22853
22854  
22855
22856  /*
22857  * - LGPL
22858  *
22859  * MonthField
22860  * 
22861  */
22862
22863 /**
22864  * @class Roo.bootstrap.MonthField
22865  * @extends Roo.bootstrap.Input
22866  * Bootstrap MonthField class
22867  * 
22868  * @cfg {String} language default en
22869  * 
22870  * @constructor
22871  * Create a new MonthField
22872  * @param {Object} config The config object
22873  */
22874
22875 Roo.bootstrap.MonthField = function(config){
22876     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22877     
22878     this.addEvents({
22879         /**
22880          * @event show
22881          * Fires when this field show.
22882          * @param {Roo.bootstrap.MonthField} this
22883          * @param {Mixed} date The date value
22884          */
22885         show : true,
22886         /**
22887          * @event show
22888          * Fires when this field hide.
22889          * @param {Roo.bootstrap.MonthField} this
22890          * @param {Mixed} date The date value
22891          */
22892         hide : true,
22893         /**
22894          * @event select
22895          * Fires when select a date.
22896          * @param {Roo.bootstrap.MonthField} this
22897          * @param {String} oldvalue The old value
22898          * @param {String} newvalue The new value
22899          */
22900         select : true
22901     });
22902 };
22903
22904 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22905     
22906     onRender: function(ct, position)
22907     {
22908         
22909         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22910         
22911         this.language = this.language || 'en';
22912         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22913         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22914         
22915         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22916         this.isInline = false;
22917         this.isInput = true;
22918         this.component = this.el.select('.add-on', true).first() || false;
22919         this.component = (this.component && this.component.length === 0) ? false : this.component;
22920         this.hasInput = this.component && this.inputEL().length;
22921         
22922         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22923         
22924         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22925         
22926         this.picker().on('mousedown', this.onMousedown, this);
22927         this.picker().on('click', this.onClick, this);
22928         
22929         this.picker().addClass('datepicker-dropdown');
22930         
22931         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22932             v.setStyle('width', '189px');
22933         });
22934         
22935         this.fillMonths();
22936         
22937         this.update();
22938         
22939         if(this.isInline) {
22940             this.show();
22941         }
22942         
22943     },
22944     
22945     setValue: function(v, suppressEvent)
22946     {   
22947         var o = this.getValue();
22948         
22949         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22950         
22951         this.update();
22952
22953         if(suppressEvent !== true){
22954             this.fireEvent('select', this, o, v);
22955         }
22956         
22957     },
22958     
22959     getValue: function()
22960     {
22961         return this.value;
22962     },
22963     
22964     onClick: function(e) 
22965     {
22966         e.stopPropagation();
22967         e.preventDefault();
22968         
22969         var target = e.getTarget();
22970         
22971         if(target.nodeName.toLowerCase() === 'i'){
22972             target = Roo.get(target).dom.parentNode;
22973         }
22974         
22975         var nodeName = target.nodeName;
22976         var className = target.className;
22977         var html = target.innerHTML;
22978         
22979         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22980             return;
22981         }
22982         
22983         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22984         
22985         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22986         
22987         this.hide();
22988                         
22989     },
22990     
22991     picker : function()
22992     {
22993         return this.pickerEl;
22994     },
22995     
22996     fillMonths: function()
22997     {    
22998         var i = 0;
22999         var months = this.picker().select('>.datepicker-months td', true).first();
23000         
23001         months.dom.innerHTML = '';
23002         
23003         while (i < 12) {
23004             var month = {
23005                 tag: 'span',
23006                 cls: 'month',
23007                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23008             };
23009             
23010             months.createChild(month);
23011         }
23012         
23013     },
23014     
23015     update: function()
23016     {
23017         var _this = this;
23018         
23019         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23020             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23021         }
23022         
23023         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23024             e.removeClass('active');
23025             
23026             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23027                 e.addClass('active');
23028             }
23029         })
23030     },
23031     
23032     place: function()
23033     {
23034         if(this.isInline) {
23035             return;
23036         }
23037         
23038         this.picker().removeClass(['bottom', 'top']);
23039         
23040         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23041             /*
23042              * place to the top of element!
23043              *
23044              */
23045             
23046             this.picker().addClass('top');
23047             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23048             
23049             return;
23050         }
23051         
23052         this.picker().addClass('bottom');
23053         
23054         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23055     },
23056     
23057     onFocus : function()
23058     {
23059         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23060         this.show();
23061     },
23062     
23063     onBlur : function()
23064     {
23065         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23066         
23067         var d = this.inputEl().getValue();
23068         
23069         this.setValue(d);
23070                 
23071         this.hide();
23072     },
23073     
23074     show : function()
23075     {
23076         this.picker().show();
23077         this.picker().select('>.datepicker-months', true).first().show();
23078         this.update();
23079         this.place();
23080         
23081         this.fireEvent('show', this, this.date);
23082     },
23083     
23084     hide : function()
23085     {
23086         if(this.isInline) {
23087             return;
23088         }
23089         this.picker().hide();
23090         this.fireEvent('hide', this, this.date);
23091         
23092     },
23093     
23094     onMousedown: function(e)
23095     {
23096         e.stopPropagation();
23097         e.preventDefault();
23098     },
23099     
23100     keyup: function(e)
23101     {
23102         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23103         this.update();
23104     },
23105
23106     fireKey: function(e)
23107     {
23108         if (!this.picker().isVisible()){
23109             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23110                 this.show();
23111             }
23112             return;
23113         }
23114         
23115         var dir;
23116         
23117         switch(e.keyCode){
23118             case 27: // escape
23119                 this.hide();
23120                 e.preventDefault();
23121                 break;
23122             case 37: // left
23123             case 39: // right
23124                 dir = e.keyCode == 37 ? -1 : 1;
23125                 
23126                 this.vIndex = this.vIndex + dir;
23127                 
23128                 if(this.vIndex < 0){
23129                     this.vIndex = 0;
23130                 }
23131                 
23132                 if(this.vIndex > 11){
23133                     this.vIndex = 11;
23134                 }
23135                 
23136                 if(isNaN(this.vIndex)){
23137                     this.vIndex = 0;
23138                 }
23139                 
23140                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23141                 
23142                 break;
23143             case 38: // up
23144             case 40: // down
23145                 
23146                 dir = e.keyCode == 38 ? -1 : 1;
23147                 
23148                 this.vIndex = this.vIndex + dir * 4;
23149                 
23150                 if(this.vIndex < 0){
23151                     this.vIndex = 0;
23152                 }
23153                 
23154                 if(this.vIndex > 11){
23155                     this.vIndex = 11;
23156                 }
23157                 
23158                 if(isNaN(this.vIndex)){
23159                     this.vIndex = 0;
23160                 }
23161                 
23162                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23163                 break;
23164                 
23165             case 13: // enter
23166                 
23167                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23168                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23169                 }
23170                 
23171                 this.hide();
23172                 e.preventDefault();
23173                 break;
23174             case 9: // tab
23175                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23176                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23177                 }
23178                 this.hide();
23179                 break;
23180             case 16: // shift
23181             case 17: // ctrl
23182             case 18: // alt
23183                 break;
23184             default :
23185                 this.hide();
23186                 
23187         }
23188     },
23189     
23190     remove: function() 
23191     {
23192         this.picker().remove();
23193     }
23194    
23195 });
23196
23197 Roo.apply(Roo.bootstrap.MonthField,  {
23198     
23199     content : {
23200         tag: 'tbody',
23201         cn: [
23202         {
23203             tag: 'tr',
23204             cn: [
23205             {
23206                 tag: 'td',
23207                 colspan: '7'
23208             }
23209             ]
23210         }
23211         ]
23212     },
23213     
23214     dates:{
23215         en: {
23216             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23217             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23218         }
23219     }
23220 });
23221
23222 Roo.apply(Roo.bootstrap.MonthField,  {
23223   
23224     template : {
23225         tag: 'div',
23226         cls: 'datepicker dropdown-menu roo-dynamic',
23227         cn: [
23228             {
23229                 tag: 'div',
23230                 cls: 'datepicker-months',
23231                 cn: [
23232                 {
23233                     tag: 'table',
23234                     cls: 'table-condensed',
23235                     cn:[
23236                         Roo.bootstrap.DateField.content
23237                     ]
23238                 }
23239                 ]
23240             }
23241         ]
23242     }
23243 });
23244
23245  
23246
23247  
23248  /*
23249  * - LGPL
23250  *
23251  * CheckBox
23252  * 
23253  */
23254
23255 /**
23256  * @class Roo.bootstrap.CheckBox
23257  * @extends Roo.bootstrap.Input
23258  * Bootstrap CheckBox class
23259  * 
23260  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23261  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23262  * @cfg {String} boxLabel The text that appears beside the checkbox
23263  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23264  * @cfg {Boolean} checked initnal the element
23265  * @cfg {Boolean} inline inline the element (default false)
23266  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23267  * @cfg {String} tooltip label tooltip
23268  * 
23269  * @constructor
23270  * Create a new CheckBox
23271  * @param {Object} config The config object
23272  */
23273
23274 Roo.bootstrap.CheckBox = function(config){
23275     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23276    
23277     this.addEvents({
23278         /**
23279         * @event check
23280         * Fires when the element is checked or unchecked.
23281         * @param {Roo.bootstrap.CheckBox} this This input
23282         * @param {Boolean} checked The new checked value
23283         */
23284        check : true,
23285        /**
23286         * @event click
23287         * Fires when the element is click.
23288         * @param {Roo.bootstrap.CheckBox} this This input
23289         */
23290        click : true
23291     });
23292     
23293 };
23294
23295 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23296   
23297     inputType: 'checkbox',
23298     inputValue: 1,
23299     valueOff: 0,
23300     boxLabel: false,
23301     checked: false,
23302     weight : false,
23303     inline: false,
23304     tooltip : '',
23305     
23306     // checkbox success does not make any sense really.. 
23307     invalidClass : "",
23308     validClass : "",
23309     
23310     
23311     getAutoCreate : function()
23312     {
23313         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23314         
23315         var id = Roo.id();
23316         
23317         var cfg = {};
23318         
23319         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23320         
23321         if(this.inline){
23322             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23323         }
23324         
23325         var input =  {
23326             tag: 'input',
23327             id : id,
23328             type : this.inputType,
23329             value : this.inputValue,
23330             cls : 'roo-' + this.inputType, //'form-box',
23331             placeholder : this.placeholder || ''
23332             
23333         };
23334         
23335         if(this.inputType != 'radio'){
23336             var hidden =  {
23337                 tag: 'input',
23338                 type : 'hidden',
23339                 cls : 'roo-hidden-value',
23340                 value : this.checked ? this.inputValue : this.valueOff
23341             };
23342         }
23343         
23344             
23345         if (this.weight) { // Validity check?
23346             cfg.cls += " " + this.inputType + "-" + this.weight;
23347         }
23348         
23349         if (this.disabled) {
23350             input.disabled=true;
23351         }
23352         
23353         if(this.checked){
23354             input.checked = this.checked;
23355         }
23356         
23357         if (this.name) {
23358             
23359             input.name = this.name;
23360             
23361             if(this.inputType != 'radio'){
23362                 hidden.name = this.name;
23363                 input.name = '_hidden_' + this.name;
23364             }
23365         }
23366         
23367         if (this.size) {
23368             input.cls += ' input-' + this.size;
23369         }
23370         
23371         var settings=this;
23372         
23373         ['xs','sm','md','lg'].map(function(size){
23374             if (settings[size]) {
23375                 cfg.cls += ' col-' + size + '-' + settings[size];
23376             }
23377         });
23378         
23379         var inputblock = input;
23380          
23381         if (this.before || this.after) {
23382             
23383             inputblock = {
23384                 cls : 'input-group',
23385                 cn :  [] 
23386             };
23387             
23388             if (this.before) {
23389                 inputblock.cn.push({
23390                     tag :'span',
23391                     cls : 'input-group-addon',
23392                     html : this.before
23393                 });
23394             }
23395             
23396             inputblock.cn.push(input);
23397             
23398             if(this.inputType != 'radio'){
23399                 inputblock.cn.push(hidden);
23400             }
23401             
23402             if (this.after) {
23403                 inputblock.cn.push({
23404                     tag :'span',
23405                     cls : 'input-group-addon',
23406                     html : this.after
23407                 });
23408             }
23409             
23410         }
23411         var boxLabelCfg = false;
23412         
23413         if(this.boxLabel){
23414            
23415             boxLabelCfg = {
23416                 tag: 'label',
23417                 //'for': id, // box label is handled by onclick - so no for...
23418                 cls: 'box-label',
23419                 html: this.boxLabel
23420             };
23421             if(this.tooltip){
23422                 boxLabelCfg.tooltip = this.tooltip;
23423             }
23424              
23425         }
23426         
23427         
23428         if (align ==='left' && this.fieldLabel.length) {
23429 //                Roo.log("left and has label");
23430             cfg.cn = [
23431                 {
23432                     tag: 'label',
23433                     'for' :  id,
23434                     cls : 'control-label',
23435                     html : this.fieldLabel
23436                 },
23437                 {
23438                     cls : "", 
23439                     cn: [
23440                         inputblock
23441                     ]
23442                 }
23443             ];
23444             
23445             if (boxLabelCfg) {
23446                 cfg.cn[1].cn.push(boxLabelCfg);
23447             }
23448             
23449             if(this.labelWidth > 12){
23450                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23451             }
23452             
23453             if(this.labelWidth < 13 && this.labelmd == 0){
23454                 this.labelmd = this.labelWidth;
23455             }
23456             
23457             if(this.labellg > 0){
23458                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23459                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23460             }
23461             
23462             if(this.labelmd > 0){
23463                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23464                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23465             }
23466             
23467             if(this.labelsm > 0){
23468                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23469                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23470             }
23471             
23472             if(this.labelxs > 0){
23473                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23474                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23475             }
23476             
23477         } else if ( this.fieldLabel.length) {
23478 //                Roo.log(" label");
23479                 cfg.cn = [
23480                    
23481                     {
23482                         tag: this.boxLabel ? 'span' : 'label',
23483                         'for': id,
23484                         cls: 'control-label box-input-label',
23485                         //cls : 'input-group-addon',
23486                         html : this.fieldLabel
23487                     },
23488                     
23489                     inputblock
23490                     
23491                 ];
23492                 if (boxLabelCfg) {
23493                     cfg.cn.push(boxLabelCfg);
23494                 }
23495
23496         } else {
23497             
23498 //                Roo.log(" no label && no align");
23499                 cfg.cn = [  inputblock ] ;
23500                 if (boxLabelCfg) {
23501                     cfg.cn.push(boxLabelCfg);
23502                 }
23503
23504                 
23505         }
23506         
23507        
23508         
23509         if(this.inputType != 'radio'){
23510             cfg.cn.push(hidden);
23511         }
23512         
23513         return cfg;
23514         
23515     },
23516     
23517     /**
23518      * return the real input element.
23519      */
23520     inputEl: function ()
23521     {
23522         return this.el.select('input.roo-' + this.inputType,true).first();
23523     },
23524     hiddenEl: function ()
23525     {
23526         return this.el.select('input.roo-hidden-value',true).first();
23527     },
23528     
23529     labelEl: function()
23530     {
23531         return this.el.select('label.control-label',true).first();
23532     },
23533     /* depricated... */
23534     
23535     label: function()
23536     {
23537         return this.labelEl();
23538     },
23539     
23540     boxLabelEl: function()
23541     {
23542         return this.el.select('label.box-label',true).first();
23543     },
23544     
23545     initEvents : function()
23546     {
23547 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23548         
23549         this.inputEl().on('click', this.onClick,  this);
23550         
23551         if (this.boxLabel) { 
23552             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23553         }
23554         
23555         this.startValue = this.getValue();
23556         
23557         if(this.groupId){
23558             Roo.bootstrap.CheckBox.register(this);
23559         }
23560     },
23561     
23562     onClick : function(e)
23563     {   
23564         if(this.fireEvent('click', this, e) !== false){
23565             this.setChecked(!this.checked);
23566         }
23567         
23568     },
23569     
23570     setChecked : function(state,suppressEvent)
23571     {
23572         this.startValue = this.getValue();
23573
23574         if(this.inputType == 'radio'){
23575             
23576             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23577                 e.dom.checked = false;
23578             });
23579             
23580             this.inputEl().dom.checked = true;
23581             
23582             this.inputEl().dom.value = this.inputValue;
23583             
23584             if(suppressEvent !== true){
23585                 this.fireEvent('check', this, true);
23586             }
23587             
23588             this.validate();
23589             
23590             return;
23591         }
23592         
23593         this.checked = state;
23594         
23595         this.inputEl().dom.checked = state;
23596         
23597         
23598         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23599         
23600         if(suppressEvent !== true){
23601             this.fireEvent('check', this, state);
23602         }
23603         
23604         this.validate();
23605     },
23606     
23607     getValue : function()
23608     {
23609         if(this.inputType == 'radio'){
23610             return this.getGroupValue();
23611         }
23612         
23613         return this.hiddenEl().dom.value;
23614         
23615     },
23616     
23617     getGroupValue : function()
23618     {
23619         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23620             return '';
23621         }
23622         
23623         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23624     },
23625     
23626     setValue : function(v,suppressEvent)
23627     {
23628         if(this.inputType == 'radio'){
23629             this.setGroupValue(v, suppressEvent);
23630             return;
23631         }
23632         
23633         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23634         
23635         this.validate();
23636     },
23637     
23638     setGroupValue : function(v, suppressEvent)
23639     {
23640         this.startValue = this.getValue();
23641         
23642         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23643             e.dom.checked = false;
23644             
23645             if(e.dom.value == v){
23646                 e.dom.checked = true;
23647             }
23648         });
23649         
23650         if(suppressEvent !== true){
23651             this.fireEvent('check', this, true);
23652         }
23653
23654         this.validate();
23655         
23656         return;
23657     },
23658     
23659     validate : function()
23660     {
23661         if(this.getVisibilityEl().hasClass('hidden')){
23662             return true;
23663         }
23664         
23665         if(
23666                 this.disabled || 
23667                 (this.inputType == 'radio' && this.validateRadio()) ||
23668                 (this.inputType == 'checkbox' && this.validateCheckbox())
23669         ){
23670             this.markValid();
23671             return true;
23672         }
23673         
23674         this.markInvalid();
23675         return false;
23676     },
23677     
23678     validateRadio : function()
23679     {
23680         if(this.getVisibilityEl().hasClass('hidden')){
23681             return true;
23682         }
23683         
23684         if(this.allowBlank){
23685             return true;
23686         }
23687         
23688         var valid = false;
23689         
23690         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23691             if(!e.dom.checked){
23692                 return;
23693             }
23694             
23695             valid = true;
23696             
23697             return false;
23698         });
23699         
23700         return valid;
23701     },
23702     
23703     validateCheckbox : function()
23704     {
23705         if(!this.groupId){
23706             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23707             //return (this.getValue() == this.inputValue) ? true : false;
23708         }
23709         
23710         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23711         
23712         if(!group){
23713             return false;
23714         }
23715         
23716         var r = false;
23717         
23718         for(var i in group){
23719             if(group[i].el.isVisible(true)){
23720                 r = false;
23721                 break;
23722             }
23723             
23724             r = true;
23725         }
23726         
23727         for(var i in group){
23728             if(r){
23729                 break;
23730             }
23731             
23732             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23733         }
23734         
23735         return r;
23736     },
23737     
23738     /**
23739      * Mark this field as valid
23740      */
23741     markValid : function()
23742     {
23743         var _this = this;
23744         
23745         this.fireEvent('valid', this);
23746         
23747         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23748         
23749         if(this.groupId){
23750             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23751         }
23752         
23753         if(label){
23754             label.markValid();
23755         }
23756
23757         if(this.inputType == 'radio'){
23758             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23759                 var fg = e.findParent('.form-group', false, true);
23760                 if (Roo.bootstrap.version == 3) {
23761                     fg.removeClass([_this.invalidClass, _this.validClass]);
23762                     fg.addClass(_this.validClass);
23763                 } else {
23764                     fg.removeClass(['is-valid', 'is-invalid']);
23765                     fg.addClass('is-valid');
23766                 }
23767             });
23768             
23769             return;
23770         }
23771
23772         if(!this.groupId){
23773             var fg = this.el.findParent('.form-group', false, true);
23774             if (Roo.bootstrap.version == 3) {
23775                 fg.removeClass([this.invalidClass, this.validClass]);
23776                 fg.addClass(this.validClass);
23777             } else {
23778                 fg.removeClass(['is-valid', 'is-invalid']);
23779                 fg.addClass('is-valid');
23780             }
23781             return;
23782         }
23783         
23784         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23785         
23786         if(!group){
23787             return;
23788         }
23789         
23790         for(var i in group){
23791             var fg = group[i].el.findParent('.form-group', false, true);
23792             if (Roo.bootstrap.version == 3) {
23793                 fg.removeClass([this.invalidClass, this.validClass]);
23794                 fg.addClass(this.validClass);
23795             } else {
23796                 fg.removeClass(['is-valid', 'is-invalid']);
23797                 fg.addClass('is-valid');
23798             }
23799         }
23800     },
23801     
23802      /**
23803      * Mark this field as invalid
23804      * @param {String} msg The validation message
23805      */
23806     markInvalid : function(msg)
23807     {
23808         if(this.allowBlank){
23809             return;
23810         }
23811         
23812         var _this = this;
23813         
23814         this.fireEvent('invalid', this, msg);
23815         
23816         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23817         
23818         if(this.groupId){
23819             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23820         }
23821         
23822         if(label){
23823             label.markInvalid();
23824         }
23825             
23826         if(this.inputType == 'radio'){
23827             
23828             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23829                 var fg = e.findParent('.form-group', false, true);
23830                 if (Roo.bootstrap.version == 3) {
23831                     fg.removeClass([_this.invalidClass, _this.validClass]);
23832                     fg.addClass(_this.invalidClass);
23833                 } else {
23834                     fg.removeClass(['is-invalid', 'is-valid']);
23835                     fg.addClass('is-invalid');
23836                 }
23837             });
23838             
23839             return;
23840         }
23841         
23842         if(!this.groupId){
23843             var fg = this.el.findParent('.form-group', false, true);
23844             if (Roo.bootstrap.version == 3) {
23845                 fg.removeClass([_this.invalidClass, _this.validClass]);
23846                 fg.addClass(_this.invalidClass);
23847             } else {
23848                 fg.removeClass(['is-invalid', 'is-valid']);
23849                 fg.addClass('is-invalid');
23850             }
23851             return;
23852         }
23853         
23854         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23855         
23856         if(!group){
23857             return;
23858         }
23859         
23860         for(var i in group){
23861             var fg = group[i].el.findParent('.form-group', false, true);
23862             if (Roo.bootstrap.version == 3) {
23863                 fg.removeClass([_this.invalidClass, _this.validClass]);
23864                 fg.addClass(_this.invalidClass);
23865             } else {
23866                 fg.removeClass(['is-invalid', 'is-valid']);
23867                 fg.addClass('is-invalid');
23868             }
23869         }
23870         
23871     },
23872     
23873     clearInvalid : function()
23874     {
23875         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23876         
23877         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23878         
23879         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23880         
23881         if (label && label.iconEl) {
23882             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23883             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23884         }
23885     },
23886     
23887     disable : function()
23888     {
23889         if(this.inputType != 'radio'){
23890             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23891             return;
23892         }
23893         
23894         var _this = this;
23895         
23896         if(this.rendered){
23897             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23898                 _this.getActionEl().addClass(this.disabledClass);
23899                 e.dom.disabled = true;
23900             });
23901         }
23902         
23903         this.disabled = true;
23904         this.fireEvent("disable", this);
23905         return this;
23906     },
23907
23908     enable : function()
23909     {
23910         if(this.inputType != 'radio'){
23911             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23912             return;
23913         }
23914         
23915         var _this = this;
23916         
23917         if(this.rendered){
23918             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23919                 _this.getActionEl().removeClass(this.disabledClass);
23920                 e.dom.disabled = false;
23921             });
23922         }
23923         
23924         this.disabled = false;
23925         this.fireEvent("enable", this);
23926         return this;
23927     },
23928     
23929     setBoxLabel : function(v)
23930     {
23931         this.boxLabel = v;
23932         
23933         if(this.rendered){
23934             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23935         }
23936     }
23937
23938 });
23939
23940 Roo.apply(Roo.bootstrap.CheckBox, {
23941     
23942     groups: {},
23943     
23944      /**
23945     * register a CheckBox Group
23946     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23947     */
23948     register : function(checkbox)
23949     {
23950         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23951             this.groups[checkbox.groupId] = {};
23952         }
23953         
23954         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23955             return;
23956         }
23957         
23958         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23959         
23960     },
23961     /**
23962     * fetch a CheckBox Group based on the group ID
23963     * @param {string} the group ID
23964     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23965     */
23966     get: function(groupId) {
23967         if (typeof(this.groups[groupId]) == 'undefined') {
23968             return false;
23969         }
23970         
23971         return this.groups[groupId] ;
23972     }
23973     
23974     
23975 });
23976 /*
23977  * - LGPL
23978  *
23979  * RadioItem
23980  * 
23981  */
23982
23983 /**
23984  * @class Roo.bootstrap.Radio
23985  * @extends Roo.bootstrap.Component
23986  * Bootstrap Radio class
23987  * @cfg {String} boxLabel - the label associated
23988  * @cfg {String} value - the value of radio
23989  * 
23990  * @constructor
23991  * Create a new Radio
23992  * @param {Object} config The config object
23993  */
23994 Roo.bootstrap.Radio = function(config){
23995     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23996     
23997 };
23998
23999 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24000     
24001     boxLabel : '',
24002     
24003     value : '',
24004     
24005     getAutoCreate : function()
24006     {
24007         var cfg = {
24008             tag : 'div',
24009             cls : 'form-group radio',
24010             cn : [
24011                 {
24012                     tag : 'label',
24013                     cls : 'box-label',
24014                     html : this.boxLabel
24015                 }
24016             ]
24017         };
24018         
24019         return cfg;
24020     },
24021     
24022     initEvents : function() 
24023     {
24024         this.parent().register(this);
24025         
24026         this.el.on('click', this.onClick, this);
24027         
24028     },
24029     
24030     onClick : function(e)
24031     {
24032         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24033             this.setChecked(true);
24034         }
24035     },
24036     
24037     setChecked : function(state, suppressEvent)
24038     {
24039         this.parent().setValue(this.value, suppressEvent);
24040         
24041     },
24042     
24043     setBoxLabel : function(v)
24044     {
24045         this.boxLabel = v;
24046         
24047         if(this.rendered){
24048             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24049         }
24050     }
24051     
24052 });
24053  
24054
24055  /*
24056  * - LGPL
24057  *
24058  * Input
24059  * 
24060  */
24061
24062 /**
24063  * @class Roo.bootstrap.SecurePass
24064  * @extends Roo.bootstrap.Input
24065  * Bootstrap SecurePass class
24066  *
24067  * 
24068  * @constructor
24069  * Create a new SecurePass
24070  * @param {Object} config The config object
24071  */
24072  
24073 Roo.bootstrap.SecurePass = function (config) {
24074     // these go here, so the translation tool can replace them..
24075     this.errors = {
24076         PwdEmpty: "Please type a password, and then retype it to confirm.",
24077         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24078         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24079         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24080         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24081         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24082         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24083         TooWeak: "Your password is Too Weak."
24084     },
24085     this.meterLabel = "Password strength:";
24086     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24087     this.meterClass = [
24088         "roo-password-meter-tooweak", 
24089         "roo-password-meter-weak", 
24090         "roo-password-meter-medium", 
24091         "roo-password-meter-strong", 
24092         "roo-password-meter-grey"
24093     ];
24094     
24095     this.errors = {};
24096     
24097     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24098 }
24099
24100 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24101     /**
24102      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24103      * {
24104      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24105      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24106      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24107      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24108      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24109      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24110      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24111      * })
24112      */
24113     // private
24114     
24115     meterWidth: 300,
24116     errorMsg :'',    
24117     errors: false,
24118     imageRoot: '/',
24119     /**
24120      * @cfg {String/Object} Label for the strength meter (defaults to
24121      * 'Password strength:')
24122      */
24123     // private
24124     meterLabel: '',
24125     /**
24126      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24127      * ['Weak', 'Medium', 'Strong'])
24128      */
24129     // private    
24130     pwdStrengths: false,    
24131     // private
24132     strength: 0,
24133     // private
24134     _lastPwd: null,
24135     // private
24136     kCapitalLetter: 0,
24137     kSmallLetter: 1,
24138     kDigit: 2,
24139     kPunctuation: 3,
24140     
24141     insecure: false,
24142     // private
24143     initEvents: function ()
24144     {
24145         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24146
24147         if (this.el.is('input[type=password]') && Roo.isSafari) {
24148             this.el.on('keydown', this.SafariOnKeyDown, this);
24149         }
24150
24151         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24152     },
24153     // private
24154     onRender: function (ct, position)
24155     {
24156         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24157         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24158         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24159
24160         this.trigger.createChild({
24161                    cn: [
24162                     {
24163                     //id: 'PwdMeter',
24164                     tag: 'div',
24165                     cls: 'roo-password-meter-grey col-xs-12',
24166                     style: {
24167                         //width: 0,
24168                         //width: this.meterWidth + 'px'                                                
24169                         }
24170                     },
24171                     {                            
24172                          cls: 'roo-password-meter-text'                          
24173                     }
24174                 ]            
24175         });
24176
24177          
24178         if (this.hideTrigger) {
24179             this.trigger.setDisplayed(false);
24180         }
24181         this.setSize(this.width || '', this.height || '');
24182     },
24183     // private
24184     onDestroy: function ()
24185     {
24186         if (this.trigger) {
24187             this.trigger.removeAllListeners();
24188             this.trigger.remove();
24189         }
24190         if (this.wrap) {
24191             this.wrap.remove();
24192         }
24193         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24194     },
24195     // private
24196     checkStrength: function ()
24197     {
24198         var pwd = this.inputEl().getValue();
24199         if (pwd == this._lastPwd) {
24200             return;
24201         }
24202
24203         var strength;
24204         if (this.ClientSideStrongPassword(pwd)) {
24205             strength = 3;
24206         } else if (this.ClientSideMediumPassword(pwd)) {
24207             strength = 2;
24208         } else if (this.ClientSideWeakPassword(pwd)) {
24209             strength = 1;
24210         } else {
24211             strength = 0;
24212         }
24213         
24214         Roo.log('strength1: ' + strength);
24215         
24216         //var pm = this.trigger.child('div/div/div').dom;
24217         var pm = this.trigger.child('div/div');
24218         pm.removeClass(this.meterClass);
24219         pm.addClass(this.meterClass[strength]);
24220                 
24221         
24222         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24223                 
24224         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24225         
24226         this._lastPwd = pwd;
24227     },
24228     reset: function ()
24229     {
24230         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24231         
24232         this._lastPwd = '';
24233         
24234         var pm = this.trigger.child('div/div');
24235         pm.removeClass(this.meterClass);
24236         pm.addClass('roo-password-meter-grey');        
24237         
24238         
24239         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24240         
24241         pt.innerHTML = '';
24242         this.inputEl().dom.type='password';
24243     },
24244     // private
24245     validateValue: function (value)
24246     {
24247         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24248             return false;
24249         }
24250         if (value.length == 0) {
24251             if (this.allowBlank) {
24252                 this.clearInvalid();
24253                 return true;
24254             }
24255
24256             this.markInvalid(this.errors.PwdEmpty);
24257             this.errorMsg = this.errors.PwdEmpty;
24258             return false;
24259         }
24260         
24261         if(this.insecure){
24262             return true;
24263         }
24264         
24265         if (!value.match(/[\x21-\x7e]+/)) {
24266             this.markInvalid(this.errors.PwdBadChar);
24267             this.errorMsg = this.errors.PwdBadChar;
24268             return false;
24269         }
24270         if (value.length < 6) {
24271             this.markInvalid(this.errors.PwdShort);
24272             this.errorMsg = this.errors.PwdShort;
24273             return false;
24274         }
24275         if (value.length > 16) {
24276             this.markInvalid(this.errors.PwdLong);
24277             this.errorMsg = this.errors.PwdLong;
24278             return false;
24279         }
24280         var strength;
24281         if (this.ClientSideStrongPassword(value)) {
24282             strength = 3;
24283         } else if (this.ClientSideMediumPassword(value)) {
24284             strength = 2;
24285         } else if (this.ClientSideWeakPassword(value)) {
24286             strength = 1;
24287         } else {
24288             strength = 0;
24289         }
24290
24291         
24292         if (strength < 2) {
24293             //this.markInvalid(this.errors.TooWeak);
24294             this.errorMsg = this.errors.TooWeak;
24295             //return false;
24296         }
24297         
24298         
24299         console.log('strength2: ' + strength);
24300         
24301         //var pm = this.trigger.child('div/div/div').dom;
24302         
24303         var pm = this.trigger.child('div/div');
24304         pm.removeClass(this.meterClass);
24305         pm.addClass(this.meterClass[strength]);
24306                 
24307         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24308                 
24309         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24310         
24311         this.errorMsg = ''; 
24312         return true;
24313     },
24314     // private
24315     CharacterSetChecks: function (type)
24316     {
24317         this.type = type;
24318         this.fResult = false;
24319     },
24320     // private
24321     isctype: function (character, type)
24322     {
24323         switch (type) {  
24324             case this.kCapitalLetter:
24325                 if (character >= 'A' && character <= 'Z') {
24326                     return true;
24327                 }
24328                 break;
24329             
24330             case this.kSmallLetter:
24331                 if (character >= 'a' && character <= 'z') {
24332                     return true;
24333                 }
24334                 break;
24335             
24336             case this.kDigit:
24337                 if (character >= '0' && character <= '9') {
24338                     return true;
24339                 }
24340                 break;
24341             
24342             case this.kPunctuation:
24343                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24344                     return true;
24345                 }
24346                 break;
24347             
24348             default:
24349                 return false;
24350         }
24351
24352     },
24353     // private
24354     IsLongEnough: function (pwd, size)
24355     {
24356         return !(pwd == null || isNaN(size) || pwd.length < size);
24357     },
24358     // private
24359     SpansEnoughCharacterSets: function (word, nb)
24360     {
24361         if (!this.IsLongEnough(word, nb))
24362         {
24363             return false;
24364         }
24365
24366         var characterSetChecks = new Array(
24367             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24368             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24369         );
24370         
24371         for (var index = 0; index < word.length; ++index) {
24372             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24373                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24374                     characterSetChecks[nCharSet].fResult = true;
24375                     break;
24376                 }
24377             }
24378         }
24379
24380         var nCharSets = 0;
24381         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24382             if (characterSetChecks[nCharSet].fResult) {
24383                 ++nCharSets;
24384             }
24385         }
24386
24387         if (nCharSets < nb) {
24388             return false;
24389         }
24390         return true;
24391     },
24392     // private
24393     ClientSideStrongPassword: function (pwd)
24394     {
24395         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24396     },
24397     // private
24398     ClientSideMediumPassword: function (pwd)
24399     {
24400         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24401     },
24402     // private
24403     ClientSideWeakPassword: function (pwd)
24404     {
24405         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24406     }
24407           
24408 })//<script type="text/javascript">
24409
24410 /*
24411  * Based  Ext JS Library 1.1.1
24412  * Copyright(c) 2006-2007, Ext JS, LLC.
24413  * LGPL
24414  *
24415  */
24416  
24417 /**
24418  * @class Roo.HtmlEditorCore
24419  * @extends Roo.Component
24420  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24421  *
24422  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24423  */
24424
24425 Roo.HtmlEditorCore = function(config){
24426     
24427     
24428     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24429     
24430     
24431     this.addEvents({
24432         /**
24433          * @event initialize
24434          * Fires when the editor is fully initialized (including the iframe)
24435          * @param {Roo.HtmlEditorCore} this
24436          */
24437         initialize: true,
24438         /**
24439          * @event activate
24440          * Fires when the editor is first receives the focus. Any insertion must wait
24441          * until after this event.
24442          * @param {Roo.HtmlEditorCore} this
24443          */
24444         activate: true,
24445          /**
24446          * @event beforesync
24447          * Fires before the textarea is updated with content from the editor iframe. Return false
24448          * to cancel the sync.
24449          * @param {Roo.HtmlEditorCore} this
24450          * @param {String} html
24451          */
24452         beforesync: true,
24453          /**
24454          * @event beforepush
24455          * Fires before the iframe editor is updated with content from the textarea. Return false
24456          * to cancel the push.
24457          * @param {Roo.HtmlEditorCore} this
24458          * @param {String} html
24459          */
24460         beforepush: true,
24461          /**
24462          * @event sync
24463          * Fires when the textarea is updated with content from the editor iframe.
24464          * @param {Roo.HtmlEditorCore} this
24465          * @param {String} html
24466          */
24467         sync: true,
24468          /**
24469          * @event push
24470          * Fires when the iframe editor is updated with content from the textarea.
24471          * @param {Roo.HtmlEditorCore} this
24472          * @param {String} html
24473          */
24474         push: true,
24475         
24476         /**
24477          * @event editorevent
24478          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24479          * @param {Roo.HtmlEditorCore} this
24480          */
24481         editorevent: true
24482         
24483     });
24484     
24485     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24486     
24487     // defaults : white / black...
24488     this.applyBlacklists();
24489     
24490     
24491     
24492 };
24493
24494
24495 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24496
24497
24498      /**
24499      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24500      */
24501     
24502     owner : false,
24503     
24504      /**
24505      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24506      *                        Roo.resizable.
24507      */
24508     resizable : false,
24509      /**
24510      * @cfg {Number} height (in pixels)
24511      */   
24512     height: 300,
24513    /**
24514      * @cfg {Number} width (in pixels)
24515      */   
24516     width: 500,
24517     
24518     /**
24519      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24520      * 
24521      */
24522     stylesheets: false,
24523     
24524     // id of frame..
24525     frameId: false,
24526     
24527     // private properties
24528     validationEvent : false,
24529     deferHeight: true,
24530     initialized : false,
24531     activated : false,
24532     sourceEditMode : false,
24533     onFocus : Roo.emptyFn,
24534     iframePad:3,
24535     hideMode:'offsets',
24536     
24537     clearUp: true,
24538     
24539     // blacklist + whitelisted elements..
24540     black: false,
24541     white: false,
24542      
24543     bodyCls : '',
24544
24545     /**
24546      * Protected method that will not generally be called directly. It
24547      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24548      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24549      */
24550     getDocMarkup : function(){
24551         // body styles..
24552         var st = '';
24553         
24554         // inherit styels from page...?? 
24555         if (this.stylesheets === false) {
24556             
24557             Roo.get(document.head).select('style').each(function(node) {
24558                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24559             });
24560             
24561             Roo.get(document.head).select('link').each(function(node) { 
24562                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24563             });
24564             
24565         } else if (!this.stylesheets.length) {
24566                 // simple..
24567                 st = '<style type="text/css">' +
24568                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24569                    '</style>';
24570         } else {
24571             for (var i in this.stylesheets) { 
24572                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24573             }
24574             
24575         }
24576         
24577         st +=  '<style type="text/css">' +
24578             'IMG { cursor: pointer } ' +
24579         '</style>';
24580
24581         var cls = 'roo-htmleditor-body';
24582         
24583         if(this.bodyCls.length){
24584             cls += ' ' + this.bodyCls;
24585         }
24586         
24587         return '<html><head>' + st  +
24588             //<style type="text/css">' +
24589             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24590             //'</style>' +
24591             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24592     },
24593
24594     // private
24595     onRender : function(ct, position)
24596     {
24597         var _t = this;
24598         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24599         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24600         
24601         
24602         this.el.dom.style.border = '0 none';
24603         this.el.dom.setAttribute('tabIndex', -1);
24604         this.el.addClass('x-hidden hide');
24605         
24606         
24607         
24608         if(Roo.isIE){ // fix IE 1px bogus margin
24609             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24610         }
24611        
24612         
24613         this.frameId = Roo.id();
24614         
24615          
24616         
24617         var iframe = this.owner.wrap.createChild({
24618             tag: 'iframe',
24619             cls: 'form-control', // bootstrap..
24620             id: this.frameId,
24621             name: this.frameId,
24622             frameBorder : 'no',
24623             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24624         }, this.el
24625         );
24626         
24627         
24628         this.iframe = iframe.dom;
24629
24630          this.assignDocWin();
24631         
24632         this.doc.designMode = 'on';
24633        
24634         this.doc.open();
24635         this.doc.write(this.getDocMarkup());
24636         this.doc.close();
24637
24638         
24639         var task = { // must defer to wait for browser to be ready
24640             run : function(){
24641                 //console.log("run task?" + this.doc.readyState);
24642                 this.assignDocWin();
24643                 if(this.doc.body || this.doc.readyState == 'complete'){
24644                     try {
24645                         this.doc.designMode="on";
24646                     } catch (e) {
24647                         return;
24648                     }
24649                     Roo.TaskMgr.stop(task);
24650                     this.initEditor.defer(10, this);
24651                 }
24652             },
24653             interval : 10,
24654             duration: 10000,
24655             scope: this
24656         };
24657         Roo.TaskMgr.start(task);
24658
24659     },
24660
24661     // private
24662     onResize : function(w, h)
24663     {
24664          Roo.log('resize: ' +w + ',' + h );
24665         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24666         if(!this.iframe){
24667             return;
24668         }
24669         if(typeof w == 'number'){
24670             
24671             this.iframe.style.width = w + 'px';
24672         }
24673         if(typeof h == 'number'){
24674             
24675             this.iframe.style.height = h + 'px';
24676             if(this.doc){
24677                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24678             }
24679         }
24680         
24681     },
24682
24683     /**
24684      * Toggles the editor between standard and source edit mode.
24685      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24686      */
24687     toggleSourceEdit : function(sourceEditMode){
24688         
24689         this.sourceEditMode = sourceEditMode === true;
24690         
24691         if(this.sourceEditMode){
24692  
24693             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24694             
24695         }else{
24696             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24697             //this.iframe.className = '';
24698             this.deferFocus();
24699         }
24700         //this.setSize(this.owner.wrap.getSize());
24701         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24702     },
24703
24704     
24705   
24706
24707     /**
24708      * Protected method that will not generally be called directly. If you need/want
24709      * custom HTML cleanup, this is the method you should override.
24710      * @param {String} html The HTML to be cleaned
24711      * return {String} The cleaned HTML
24712      */
24713     cleanHtml : function(html){
24714         html = String(html);
24715         if(html.length > 5){
24716             if(Roo.isSafari){ // strip safari nonsense
24717                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24718             }
24719         }
24720         if(html == '&nbsp;'){
24721             html = '';
24722         }
24723         return html;
24724     },
24725
24726     /**
24727      * HTML Editor -> Textarea
24728      * Protected method that will not generally be called directly. Syncs the contents
24729      * of the editor iframe with the textarea.
24730      */
24731     syncValue : function(){
24732         if(this.initialized){
24733             var bd = (this.doc.body || this.doc.documentElement);
24734             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24735             var html = bd.innerHTML;
24736             if(Roo.isSafari){
24737                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24738                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24739                 if(m && m[1]){
24740                     html = '<div style="'+m[0]+'">' + html + '</div>';
24741                 }
24742             }
24743             html = this.cleanHtml(html);
24744             // fix up the special chars.. normaly like back quotes in word...
24745             // however we do not want to do this with chinese..
24746             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24747                 
24748                 var cc = match.charCodeAt();
24749
24750                 // Get the character value, handling surrogate pairs
24751                 if (match.length == 2) {
24752                     // It's a surrogate pair, calculate the Unicode code point
24753                     var high = match.charCodeAt(0) - 0xD800;
24754                     var low  = match.charCodeAt(1) - 0xDC00;
24755                     cc = (high * 0x400) + low + 0x10000;
24756                 }  else if (
24757                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24758                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24759                     (cc >= 0xf900 && cc < 0xfb00 )
24760                 ) {
24761                         return match;
24762                 }  
24763          
24764                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24765                 return "&#" + cc + ";";
24766                 
24767                 
24768             });
24769             
24770             
24771              
24772             if(this.owner.fireEvent('beforesync', this, html) !== false){
24773                 this.el.dom.value = html;
24774                 this.owner.fireEvent('sync', this, html);
24775             }
24776         }
24777     },
24778
24779     /**
24780      * Protected method that will not generally be called directly. Pushes the value of the textarea
24781      * into the iframe editor.
24782      */
24783     pushValue : function(){
24784         if(this.initialized){
24785             var v = this.el.dom.value.trim();
24786             
24787 //            if(v.length < 1){
24788 //                v = '&#160;';
24789 //            }
24790             
24791             if(this.owner.fireEvent('beforepush', this, v) !== false){
24792                 var d = (this.doc.body || this.doc.documentElement);
24793                 d.innerHTML = v;
24794                 this.cleanUpPaste();
24795                 this.el.dom.value = d.innerHTML;
24796                 this.owner.fireEvent('push', this, v);
24797             }
24798         }
24799     },
24800
24801     // private
24802     deferFocus : function(){
24803         this.focus.defer(10, this);
24804     },
24805
24806     // doc'ed in Field
24807     focus : function(){
24808         if(this.win && !this.sourceEditMode){
24809             this.win.focus();
24810         }else{
24811             this.el.focus();
24812         }
24813     },
24814     
24815     assignDocWin: function()
24816     {
24817         var iframe = this.iframe;
24818         
24819          if(Roo.isIE){
24820             this.doc = iframe.contentWindow.document;
24821             this.win = iframe.contentWindow;
24822         } else {
24823 //            if (!Roo.get(this.frameId)) {
24824 //                return;
24825 //            }
24826 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24827 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24828             
24829             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24830                 return;
24831             }
24832             
24833             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24834             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24835         }
24836     },
24837     
24838     // private
24839     initEditor : function(){
24840         //console.log("INIT EDITOR");
24841         this.assignDocWin();
24842         
24843         
24844         
24845         this.doc.designMode="on";
24846         this.doc.open();
24847         this.doc.write(this.getDocMarkup());
24848         this.doc.close();
24849         
24850         var dbody = (this.doc.body || this.doc.documentElement);
24851         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24852         // this copies styles from the containing element into thsi one..
24853         // not sure why we need all of this..
24854         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24855         
24856         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24857         //ss['background-attachment'] = 'fixed'; // w3c
24858         dbody.bgProperties = 'fixed'; // ie
24859         //Roo.DomHelper.applyStyles(dbody, ss);
24860         Roo.EventManager.on(this.doc, {
24861             //'mousedown': this.onEditorEvent,
24862             'mouseup': this.onEditorEvent,
24863             'dblclick': this.onEditorEvent,
24864             'click': this.onEditorEvent,
24865             'keyup': this.onEditorEvent,
24866             buffer:100,
24867             scope: this
24868         });
24869         if(Roo.isGecko){
24870             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24871         }
24872         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24873             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24874         }
24875         this.initialized = true;
24876
24877         this.owner.fireEvent('initialize', this);
24878         this.pushValue();
24879     },
24880
24881     // private
24882     onDestroy : function(){
24883         
24884         
24885         
24886         if(this.rendered){
24887             
24888             //for (var i =0; i < this.toolbars.length;i++) {
24889             //    // fixme - ask toolbars for heights?
24890             //    this.toolbars[i].onDestroy();
24891            // }
24892             
24893             //this.wrap.dom.innerHTML = '';
24894             //this.wrap.remove();
24895         }
24896     },
24897
24898     // private
24899     onFirstFocus : function(){
24900         
24901         this.assignDocWin();
24902         
24903         
24904         this.activated = true;
24905          
24906     
24907         if(Roo.isGecko){ // prevent silly gecko errors
24908             this.win.focus();
24909             var s = this.win.getSelection();
24910             if(!s.focusNode || s.focusNode.nodeType != 3){
24911                 var r = s.getRangeAt(0);
24912                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24913                 r.collapse(true);
24914                 this.deferFocus();
24915             }
24916             try{
24917                 this.execCmd('useCSS', true);
24918                 this.execCmd('styleWithCSS', false);
24919             }catch(e){}
24920         }
24921         this.owner.fireEvent('activate', this);
24922     },
24923
24924     // private
24925     adjustFont: function(btn){
24926         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24927         //if(Roo.isSafari){ // safari
24928         //    adjust *= 2;
24929        // }
24930         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24931         if(Roo.isSafari){ // safari
24932             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24933             v =  (v < 10) ? 10 : v;
24934             v =  (v > 48) ? 48 : v;
24935             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24936             
24937         }
24938         
24939         
24940         v = Math.max(1, v+adjust);
24941         
24942         this.execCmd('FontSize', v  );
24943     },
24944
24945     onEditorEvent : function(e)
24946     {
24947         this.owner.fireEvent('editorevent', this, e);
24948       //  this.updateToolbar();
24949         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24950     },
24951
24952     insertTag : function(tg)
24953     {
24954         // could be a bit smarter... -> wrap the current selected tRoo..
24955         if (tg.toLowerCase() == 'span' ||
24956             tg.toLowerCase() == 'code' ||
24957             tg.toLowerCase() == 'sup' ||
24958             tg.toLowerCase() == 'sub' 
24959             ) {
24960             
24961             range = this.createRange(this.getSelection());
24962             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24963             wrappingNode.appendChild(range.extractContents());
24964             range.insertNode(wrappingNode);
24965
24966             return;
24967             
24968             
24969             
24970         }
24971         this.execCmd("formatblock",   tg);
24972         
24973     },
24974     
24975     insertText : function(txt)
24976     {
24977         
24978         
24979         var range = this.createRange();
24980         range.deleteContents();
24981                //alert(Sender.getAttribute('label'));
24982                
24983         range.insertNode(this.doc.createTextNode(txt));
24984     } ,
24985     
24986      
24987
24988     /**
24989      * Executes a Midas editor command on the editor document and performs necessary focus and
24990      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24991      * @param {String} cmd The Midas command
24992      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24993      */
24994     relayCmd : function(cmd, value){
24995         this.win.focus();
24996         this.execCmd(cmd, value);
24997         this.owner.fireEvent('editorevent', this);
24998         //this.updateToolbar();
24999         this.owner.deferFocus();
25000     },
25001
25002     /**
25003      * Executes a Midas editor command directly on the editor document.
25004      * For visual commands, you should use {@link #relayCmd} instead.
25005      * <b>This should only be called after the editor is initialized.</b>
25006      * @param {String} cmd The Midas command
25007      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25008      */
25009     execCmd : function(cmd, value){
25010         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25011         this.syncValue();
25012     },
25013  
25014  
25015    
25016     /**
25017      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25018      * to insert tRoo.
25019      * @param {String} text | dom node.. 
25020      */
25021     insertAtCursor : function(text)
25022     {
25023         
25024         if(!this.activated){
25025             return;
25026         }
25027         /*
25028         if(Roo.isIE){
25029             this.win.focus();
25030             var r = this.doc.selection.createRange();
25031             if(r){
25032                 r.collapse(true);
25033                 r.pasteHTML(text);
25034                 this.syncValue();
25035                 this.deferFocus();
25036             
25037             }
25038             return;
25039         }
25040         */
25041         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25042             this.win.focus();
25043             
25044             
25045             // from jquery ui (MIT licenced)
25046             var range, node;
25047             var win = this.win;
25048             
25049             if (win.getSelection && win.getSelection().getRangeAt) {
25050                 range = win.getSelection().getRangeAt(0);
25051                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25052                 range.insertNode(node);
25053             } else if (win.document.selection && win.document.selection.createRange) {
25054                 // no firefox support
25055                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25056                 win.document.selection.createRange().pasteHTML(txt);
25057             } else {
25058                 // no firefox support
25059                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25060                 this.execCmd('InsertHTML', txt);
25061             } 
25062             
25063             this.syncValue();
25064             
25065             this.deferFocus();
25066         }
25067     },
25068  // private
25069     mozKeyPress : function(e){
25070         if(e.ctrlKey){
25071             var c = e.getCharCode(), cmd;
25072           
25073             if(c > 0){
25074                 c = String.fromCharCode(c).toLowerCase();
25075                 switch(c){
25076                     case 'b':
25077                         cmd = 'bold';
25078                         break;
25079                     case 'i':
25080                         cmd = 'italic';
25081                         break;
25082                     
25083                     case 'u':
25084                         cmd = 'underline';
25085                         break;
25086                     
25087                     case 'v':
25088                         this.cleanUpPaste.defer(100, this);
25089                         return;
25090                         
25091                 }
25092                 if(cmd){
25093                     this.win.focus();
25094                     this.execCmd(cmd);
25095                     this.deferFocus();
25096                     e.preventDefault();
25097                 }
25098                 
25099             }
25100         }
25101     },
25102
25103     // private
25104     fixKeys : function(){ // load time branching for fastest keydown performance
25105         if(Roo.isIE){
25106             return function(e){
25107                 var k = e.getKey(), r;
25108                 if(k == e.TAB){
25109                     e.stopEvent();
25110                     r = this.doc.selection.createRange();
25111                     if(r){
25112                         r.collapse(true);
25113                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25114                         this.deferFocus();
25115                     }
25116                     return;
25117                 }
25118                 
25119                 if(k == e.ENTER){
25120                     r = this.doc.selection.createRange();
25121                     if(r){
25122                         var target = r.parentElement();
25123                         if(!target || target.tagName.toLowerCase() != 'li'){
25124                             e.stopEvent();
25125                             r.pasteHTML('<br />');
25126                             r.collapse(false);
25127                             r.select();
25128                         }
25129                     }
25130                 }
25131                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25132                     this.cleanUpPaste.defer(100, this);
25133                     return;
25134                 }
25135                 
25136                 
25137             };
25138         }else if(Roo.isOpera){
25139             return function(e){
25140                 var k = e.getKey();
25141                 if(k == e.TAB){
25142                     e.stopEvent();
25143                     this.win.focus();
25144                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25145                     this.deferFocus();
25146                 }
25147                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25148                     this.cleanUpPaste.defer(100, this);
25149                     return;
25150                 }
25151                 
25152             };
25153         }else if(Roo.isSafari){
25154             return function(e){
25155                 var k = e.getKey();
25156                 
25157                 if(k == e.TAB){
25158                     e.stopEvent();
25159                     this.execCmd('InsertText','\t');
25160                     this.deferFocus();
25161                     return;
25162                 }
25163                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25164                     this.cleanUpPaste.defer(100, this);
25165                     return;
25166                 }
25167                 
25168              };
25169         }
25170     }(),
25171     
25172     getAllAncestors: function()
25173     {
25174         var p = this.getSelectedNode();
25175         var a = [];
25176         if (!p) {
25177             a.push(p); // push blank onto stack..
25178             p = this.getParentElement();
25179         }
25180         
25181         
25182         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25183             a.push(p);
25184             p = p.parentNode;
25185         }
25186         a.push(this.doc.body);
25187         return a;
25188     },
25189     lastSel : false,
25190     lastSelNode : false,
25191     
25192     
25193     getSelection : function() 
25194     {
25195         this.assignDocWin();
25196         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25197     },
25198     
25199     getSelectedNode: function() 
25200     {
25201         // this may only work on Gecko!!!
25202         
25203         // should we cache this!!!!
25204         
25205         
25206         
25207          
25208         var range = this.createRange(this.getSelection()).cloneRange();
25209         
25210         if (Roo.isIE) {
25211             var parent = range.parentElement();
25212             while (true) {
25213                 var testRange = range.duplicate();
25214                 testRange.moveToElementText(parent);
25215                 if (testRange.inRange(range)) {
25216                     break;
25217                 }
25218                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25219                     break;
25220                 }
25221                 parent = parent.parentElement;
25222             }
25223             return parent;
25224         }
25225         
25226         // is ancestor a text element.
25227         var ac =  range.commonAncestorContainer;
25228         if (ac.nodeType == 3) {
25229             ac = ac.parentNode;
25230         }
25231         
25232         var ar = ac.childNodes;
25233          
25234         var nodes = [];
25235         var other_nodes = [];
25236         var has_other_nodes = false;
25237         for (var i=0;i<ar.length;i++) {
25238             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25239                 continue;
25240             }
25241             // fullly contained node.
25242             
25243             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25244                 nodes.push(ar[i]);
25245                 continue;
25246             }
25247             
25248             // probably selected..
25249             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25250                 other_nodes.push(ar[i]);
25251                 continue;
25252             }
25253             // outer..
25254             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25255                 continue;
25256             }
25257             
25258             
25259             has_other_nodes = true;
25260         }
25261         if (!nodes.length && other_nodes.length) {
25262             nodes= other_nodes;
25263         }
25264         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25265             return false;
25266         }
25267         
25268         return nodes[0];
25269     },
25270     createRange: function(sel)
25271     {
25272         // this has strange effects when using with 
25273         // top toolbar - not sure if it's a great idea.
25274         //this.editor.contentWindow.focus();
25275         if (typeof sel != "undefined") {
25276             try {
25277                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25278             } catch(e) {
25279                 return this.doc.createRange();
25280             }
25281         } else {
25282             return this.doc.createRange();
25283         }
25284     },
25285     getParentElement: function()
25286     {
25287         
25288         this.assignDocWin();
25289         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25290         
25291         var range = this.createRange(sel);
25292          
25293         try {
25294             var p = range.commonAncestorContainer;
25295             while (p.nodeType == 3) { // text node
25296                 p = p.parentNode;
25297             }
25298             return p;
25299         } catch (e) {
25300             return null;
25301         }
25302     
25303     },
25304     /***
25305      *
25306      * Range intersection.. the hard stuff...
25307      *  '-1' = before
25308      *  '0' = hits..
25309      *  '1' = after.
25310      *         [ -- selected range --- ]
25311      *   [fail]                        [fail]
25312      *
25313      *    basically..
25314      *      if end is before start or  hits it. fail.
25315      *      if start is after end or hits it fail.
25316      *
25317      *   if either hits (but other is outside. - then it's not 
25318      *   
25319      *    
25320      **/
25321     
25322     
25323     // @see http://www.thismuchiknow.co.uk/?p=64.
25324     rangeIntersectsNode : function(range, node)
25325     {
25326         var nodeRange = node.ownerDocument.createRange();
25327         try {
25328             nodeRange.selectNode(node);
25329         } catch (e) {
25330             nodeRange.selectNodeContents(node);
25331         }
25332     
25333         var rangeStartRange = range.cloneRange();
25334         rangeStartRange.collapse(true);
25335     
25336         var rangeEndRange = range.cloneRange();
25337         rangeEndRange.collapse(false);
25338     
25339         var nodeStartRange = nodeRange.cloneRange();
25340         nodeStartRange.collapse(true);
25341     
25342         var nodeEndRange = nodeRange.cloneRange();
25343         nodeEndRange.collapse(false);
25344     
25345         return rangeStartRange.compareBoundaryPoints(
25346                  Range.START_TO_START, nodeEndRange) == -1 &&
25347                rangeEndRange.compareBoundaryPoints(
25348                  Range.START_TO_START, nodeStartRange) == 1;
25349         
25350          
25351     },
25352     rangeCompareNode : function(range, node)
25353     {
25354         var nodeRange = node.ownerDocument.createRange();
25355         try {
25356             nodeRange.selectNode(node);
25357         } catch (e) {
25358             nodeRange.selectNodeContents(node);
25359         }
25360         
25361         
25362         range.collapse(true);
25363     
25364         nodeRange.collapse(true);
25365      
25366         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25367         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25368          
25369         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25370         
25371         var nodeIsBefore   =  ss == 1;
25372         var nodeIsAfter    = ee == -1;
25373         
25374         if (nodeIsBefore && nodeIsAfter) {
25375             return 0; // outer
25376         }
25377         if (!nodeIsBefore && nodeIsAfter) {
25378             return 1; //right trailed.
25379         }
25380         
25381         if (nodeIsBefore && !nodeIsAfter) {
25382             return 2;  // left trailed.
25383         }
25384         // fully contined.
25385         return 3;
25386     },
25387
25388     // private? - in a new class?
25389     cleanUpPaste :  function()
25390     {
25391         // cleans up the whole document..
25392         Roo.log('cleanuppaste');
25393         
25394         this.cleanUpChildren(this.doc.body);
25395         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25396         if (clean != this.doc.body.innerHTML) {
25397             this.doc.body.innerHTML = clean;
25398         }
25399         
25400     },
25401     
25402     cleanWordChars : function(input) {// change the chars to hex code
25403         var he = Roo.HtmlEditorCore;
25404         
25405         var output = input;
25406         Roo.each(he.swapCodes, function(sw) { 
25407             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25408             
25409             output = output.replace(swapper, sw[1]);
25410         });
25411         
25412         return output;
25413     },
25414     
25415     
25416     cleanUpChildren : function (n)
25417     {
25418         if (!n.childNodes.length) {
25419             return;
25420         }
25421         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25422            this.cleanUpChild(n.childNodes[i]);
25423         }
25424     },
25425     
25426     
25427         
25428     
25429     cleanUpChild : function (node)
25430     {
25431         var ed = this;
25432         //console.log(node);
25433         if (node.nodeName == "#text") {
25434             // clean up silly Windows -- stuff?
25435             return; 
25436         }
25437         if (node.nodeName == "#comment") {
25438             node.parentNode.removeChild(node);
25439             // clean up silly Windows -- stuff?
25440             return; 
25441         }
25442         var lcname = node.tagName.toLowerCase();
25443         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25444         // whitelist of tags..
25445         
25446         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25447             // remove node.
25448             node.parentNode.removeChild(node);
25449             return;
25450             
25451         }
25452         
25453         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25454         
25455         // spans with no attributes - just remove them..
25456         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25457             remove_keep_children = true;
25458         }
25459         
25460         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25461         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25462         
25463         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25464         //    remove_keep_children = true;
25465         //}
25466         
25467         if (remove_keep_children) {
25468             this.cleanUpChildren(node);
25469             // inserts everything just before this node...
25470             while (node.childNodes.length) {
25471                 var cn = node.childNodes[0];
25472                 node.removeChild(cn);
25473                 node.parentNode.insertBefore(cn, node);
25474             }
25475             node.parentNode.removeChild(node);
25476             return;
25477         }
25478         
25479         if (!node.attributes || !node.attributes.length) {
25480             
25481           
25482             
25483             
25484             this.cleanUpChildren(node);
25485             return;
25486         }
25487         
25488         function cleanAttr(n,v)
25489         {
25490             
25491             if (v.match(/^\./) || v.match(/^\//)) {
25492                 return;
25493             }
25494             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25495                 return;
25496             }
25497             if (v.match(/^#/)) {
25498                 return;
25499             }
25500             if (v.match(/^\{/)) { // allow template editing.
25501                 return;
25502             }
25503 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25504             node.removeAttribute(n);
25505             
25506         }
25507         
25508         var cwhite = this.cwhite;
25509         var cblack = this.cblack;
25510             
25511         function cleanStyle(n,v)
25512         {
25513             if (v.match(/expression/)) { //XSS?? should we even bother..
25514                 node.removeAttribute(n);
25515                 return;
25516             }
25517             
25518             var parts = v.split(/;/);
25519             var clean = [];
25520             
25521             Roo.each(parts, function(p) {
25522                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25523                 if (!p.length) {
25524                     return true;
25525                 }
25526                 var l = p.split(':').shift().replace(/\s+/g,'');
25527                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25528                 
25529                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25530 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25531                     //node.removeAttribute(n);
25532                     return true;
25533                 }
25534                 //Roo.log()
25535                 // only allow 'c whitelisted system attributes'
25536                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25537 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25538                     //node.removeAttribute(n);
25539                     return true;
25540                 }
25541                 
25542                 
25543                  
25544                 
25545                 clean.push(p);
25546                 return true;
25547             });
25548             if (clean.length) { 
25549                 node.setAttribute(n, clean.join(';'));
25550             } else {
25551                 node.removeAttribute(n);
25552             }
25553             
25554         }
25555         
25556         
25557         for (var i = node.attributes.length-1; i > -1 ; i--) {
25558             var a = node.attributes[i];
25559             //console.log(a);
25560             
25561             if (a.name.toLowerCase().substr(0,2)=='on')  {
25562                 node.removeAttribute(a.name);
25563                 continue;
25564             }
25565             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25566                 node.removeAttribute(a.name);
25567                 continue;
25568             }
25569             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25570                 cleanAttr(a.name,a.value); // fixme..
25571                 continue;
25572             }
25573             if (a.name == 'style') {
25574                 cleanStyle(a.name,a.value);
25575                 continue;
25576             }
25577             /// clean up MS crap..
25578             // tecnically this should be a list of valid class'es..
25579             
25580             
25581             if (a.name == 'class') {
25582                 if (a.value.match(/^Mso/)) {
25583                     node.removeAttribute('class');
25584                 }
25585                 
25586                 if (a.value.match(/^body$/)) {
25587                     node.removeAttribute('class');
25588                 }
25589                 continue;
25590             }
25591             
25592             // style cleanup!?
25593             // class cleanup?
25594             
25595         }
25596         
25597         
25598         this.cleanUpChildren(node);
25599         
25600         
25601     },
25602     
25603     /**
25604      * Clean up MS wordisms...
25605      */
25606     cleanWord : function(node)
25607     {
25608         if (!node) {
25609             this.cleanWord(this.doc.body);
25610             return;
25611         }
25612         
25613         if(
25614                 node.nodeName == 'SPAN' &&
25615                 !node.hasAttributes() &&
25616                 node.childNodes.length == 1 &&
25617                 node.firstChild.nodeName == "#text"  
25618         ) {
25619             var textNode = node.firstChild;
25620             node.removeChild(textNode);
25621             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25622                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25623             }
25624             node.parentNode.insertBefore(textNode, node);
25625             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25626                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25627             }
25628             node.parentNode.removeChild(node);
25629         }
25630         
25631         if (node.nodeName == "#text") {
25632             // clean up silly Windows -- stuff?
25633             return; 
25634         }
25635         if (node.nodeName == "#comment") {
25636             node.parentNode.removeChild(node);
25637             // clean up silly Windows -- stuff?
25638             return; 
25639         }
25640         
25641         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25642             node.parentNode.removeChild(node);
25643             return;
25644         }
25645         //Roo.log(node.tagName);
25646         // remove - but keep children..
25647         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25648             //Roo.log('-- removed');
25649             while (node.childNodes.length) {
25650                 var cn = node.childNodes[0];
25651                 node.removeChild(cn);
25652                 node.parentNode.insertBefore(cn, node);
25653                 // move node to parent - and clean it..
25654                 this.cleanWord(cn);
25655             }
25656             node.parentNode.removeChild(node);
25657             /// no need to iterate chidlren = it's got none..
25658             //this.iterateChildren(node, this.cleanWord);
25659             return;
25660         }
25661         // clean styles
25662         if (node.className.length) {
25663             
25664             var cn = node.className.split(/\W+/);
25665             var cna = [];
25666             Roo.each(cn, function(cls) {
25667                 if (cls.match(/Mso[a-zA-Z]+/)) {
25668                     return;
25669                 }
25670                 cna.push(cls);
25671             });
25672             node.className = cna.length ? cna.join(' ') : '';
25673             if (!cna.length) {
25674                 node.removeAttribute("class");
25675             }
25676         }
25677         
25678         if (node.hasAttribute("lang")) {
25679             node.removeAttribute("lang");
25680         }
25681         
25682         if (node.hasAttribute("style")) {
25683             
25684             var styles = node.getAttribute("style").split(";");
25685             var nstyle = [];
25686             Roo.each(styles, function(s) {
25687                 if (!s.match(/:/)) {
25688                     return;
25689                 }
25690                 var kv = s.split(":");
25691                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25692                     return;
25693                 }
25694                 // what ever is left... we allow.
25695                 nstyle.push(s);
25696             });
25697             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25698             if (!nstyle.length) {
25699                 node.removeAttribute('style');
25700             }
25701         }
25702         this.iterateChildren(node, this.cleanWord);
25703         
25704         
25705         
25706     },
25707     /**
25708      * iterateChildren of a Node, calling fn each time, using this as the scole..
25709      * @param {DomNode} node node to iterate children of.
25710      * @param {Function} fn method of this class to call on each item.
25711      */
25712     iterateChildren : function(node, fn)
25713     {
25714         if (!node.childNodes.length) {
25715                 return;
25716         }
25717         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25718            fn.call(this, node.childNodes[i])
25719         }
25720     },
25721     
25722     
25723     /**
25724      * cleanTableWidths.
25725      *
25726      * Quite often pasting from word etc.. results in tables with column and widths.
25727      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25728      *
25729      */
25730     cleanTableWidths : function(node)
25731     {
25732          
25733          
25734         if (!node) {
25735             this.cleanTableWidths(this.doc.body);
25736             return;
25737         }
25738         
25739         // ignore list...
25740         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25741             return; 
25742         }
25743         Roo.log(node.tagName);
25744         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25745             this.iterateChildren(node, this.cleanTableWidths);
25746             return;
25747         }
25748         if (node.hasAttribute('width')) {
25749             node.removeAttribute('width');
25750         }
25751         
25752          
25753         if (node.hasAttribute("style")) {
25754             // pretty basic...
25755             
25756             var styles = node.getAttribute("style").split(";");
25757             var nstyle = [];
25758             Roo.each(styles, function(s) {
25759                 if (!s.match(/:/)) {
25760                     return;
25761                 }
25762                 var kv = s.split(":");
25763                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25764                     return;
25765                 }
25766                 // what ever is left... we allow.
25767                 nstyle.push(s);
25768             });
25769             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25770             if (!nstyle.length) {
25771                 node.removeAttribute('style');
25772             }
25773         }
25774         
25775         this.iterateChildren(node, this.cleanTableWidths);
25776         
25777         
25778     },
25779     
25780     
25781     
25782     
25783     domToHTML : function(currentElement, depth, nopadtext) {
25784         
25785         depth = depth || 0;
25786         nopadtext = nopadtext || false;
25787     
25788         if (!currentElement) {
25789             return this.domToHTML(this.doc.body);
25790         }
25791         
25792         //Roo.log(currentElement);
25793         var j;
25794         var allText = false;
25795         var nodeName = currentElement.nodeName;
25796         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25797         
25798         if  (nodeName == '#text') {
25799             
25800             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25801         }
25802         
25803         
25804         var ret = '';
25805         if (nodeName != 'BODY') {
25806              
25807             var i = 0;
25808             // Prints the node tagName, such as <A>, <IMG>, etc
25809             if (tagName) {
25810                 var attr = [];
25811                 for(i = 0; i < currentElement.attributes.length;i++) {
25812                     // quoting?
25813                     var aname = currentElement.attributes.item(i).name;
25814                     if (!currentElement.attributes.item(i).value.length) {
25815                         continue;
25816                     }
25817                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25818                 }
25819                 
25820                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25821             } 
25822             else {
25823                 
25824                 // eack
25825             }
25826         } else {
25827             tagName = false;
25828         }
25829         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25830             return ret;
25831         }
25832         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25833             nopadtext = true;
25834         }
25835         
25836         
25837         // Traverse the tree
25838         i = 0;
25839         var currentElementChild = currentElement.childNodes.item(i);
25840         var allText = true;
25841         var innerHTML  = '';
25842         lastnode = '';
25843         while (currentElementChild) {
25844             // Formatting code (indent the tree so it looks nice on the screen)
25845             var nopad = nopadtext;
25846             if (lastnode == 'SPAN') {
25847                 nopad  = true;
25848             }
25849             // text
25850             if  (currentElementChild.nodeName == '#text') {
25851                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25852                 toadd = nopadtext ? toadd : toadd.trim();
25853                 if (!nopad && toadd.length > 80) {
25854                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25855                 }
25856                 innerHTML  += toadd;
25857                 
25858                 i++;
25859                 currentElementChild = currentElement.childNodes.item(i);
25860                 lastNode = '';
25861                 continue;
25862             }
25863             allText = false;
25864             
25865             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25866                 
25867             // Recursively traverse the tree structure of the child node
25868             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25869             lastnode = currentElementChild.nodeName;
25870             i++;
25871             currentElementChild=currentElement.childNodes.item(i);
25872         }
25873         
25874         ret += innerHTML;
25875         
25876         if (!allText) {
25877                 // The remaining code is mostly for formatting the tree
25878             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25879         }
25880         
25881         
25882         if (tagName) {
25883             ret+= "</"+tagName+">";
25884         }
25885         return ret;
25886         
25887     },
25888         
25889     applyBlacklists : function()
25890     {
25891         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25892         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25893         
25894         this.white = [];
25895         this.black = [];
25896         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25897             if (b.indexOf(tag) > -1) {
25898                 return;
25899             }
25900             this.white.push(tag);
25901             
25902         }, this);
25903         
25904         Roo.each(w, function(tag) {
25905             if (b.indexOf(tag) > -1) {
25906                 return;
25907             }
25908             if (this.white.indexOf(tag) > -1) {
25909                 return;
25910             }
25911             this.white.push(tag);
25912             
25913         }, this);
25914         
25915         
25916         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25917             if (w.indexOf(tag) > -1) {
25918                 return;
25919             }
25920             this.black.push(tag);
25921             
25922         }, this);
25923         
25924         Roo.each(b, function(tag) {
25925             if (w.indexOf(tag) > -1) {
25926                 return;
25927             }
25928             if (this.black.indexOf(tag) > -1) {
25929                 return;
25930             }
25931             this.black.push(tag);
25932             
25933         }, this);
25934         
25935         
25936         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25937         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25938         
25939         this.cwhite = [];
25940         this.cblack = [];
25941         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25942             if (b.indexOf(tag) > -1) {
25943                 return;
25944             }
25945             this.cwhite.push(tag);
25946             
25947         }, this);
25948         
25949         Roo.each(w, function(tag) {
25950             if (b.indexOf(tag) > -1) {
25951                 return;
25952             }
25953             if (this.cwhite.indexOf(tag) > -1) {
25954                 return;
25955             }
25956             this.cwhite.push(tag);
25957             
25958         }, this);
25959         
25960         
25961         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25962             if (w.indexOf(tag) > -1) {
25963                 return;
25964             }
25965             this.cblack.push(tag);
25966             
25967         }, this);
25968         
25969         Roo.each(b, function(tag) {
25970             if (w.indexOf(tag) > -1) {
25971                 return;
25972             }
25973             if (this.cblack.indexOf(tag) > -1) {
25974                 return;
25975             }
25976             this.cblack.push(tag);
25977             
25978         }, this);
25979     },
25980     
25981     setStylesheets : function(stylesheets)
25982     {
25983         if(typeof(stylesheets) == 'string'){
25984             Roo.get(this.iframe.contentDocument.head).createChild({
25985                 tag : 'link',
25986                 rel : 'stylesheet',
25987                 type : 'text/css',
25988                 href : stylesheets
25989             });
25990             
25991             return;
25992         }
25993         var _this = this;
25994      
25995         Roo.each(stylesheets, function(s) {
25996             if(!s.length){
25997                 return;
25998             }
25999             
26000             Roo.get(_this.iframe.contentDocument.head).createChild({
26001                 tag : 'link',
26002                 rel : 'stylesheet',
26003                 type : 'text/css',
26004                 href : s
26005             });
26006         });
26007
26008         
26009     },
26010     
26011     removeStylesheets : function()
26012     {
26013         var _this = this;
26014         
26015         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26016             s.remove();
26017         });
26018     },
26019     
26020     setStyle : function(style)
26021     {
26022         Roo.get(this.iframe.contentDocument.head).createChild({
26023             tag : 'style',
26024             type : 'text/css',
26025             html : style
26026         });
26027
26028         return;
26029     }
26030     
26031     // hide stuff that is not compatible
26032     /**
26033      * @event blur
26034      * @hide
26035      */
26036     /**
26037      * @event change
26038      * @hide
26039      */
26040     /**
26041      * @event focus
26042      * @hide
26043      */
26044     /**
26045      * @event specialkey
26046      * @hide
26047      */
26048     /**
26049      * @cfg {String} fieldClass @hide
26050      */
26051     /**
26052      * @cfg {String} focusClass @hide
26053      */
26054     /**
26055      * @cfg {String} autoCreate @hide
26056      */
26057     /**
26058      * @cfg {String} inputType @hide
26059      */
26060     /**
26061      * @cfg {String} invalidClass @hide
26062      */
26063     /**
26064      * @cfg {String} invalidText @hide
26065      */
26066     /**
26067      * @cfg {String} msgFx @hide
26068      */
26069     /**
26070      * @cfg {String} validateOnBlur @hide
26071      */
26072 });
26073
26074 Roo.HtmlEditorCore.white = [
26075         'area', 'br', 'img', 'input', 'hr', 'wbr',
26076         
26077        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26078        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26079        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26080        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26081        'table',   'ul',         'xmp', 
26082        
26083        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26084       'thead',   'tr', 
26085      
26086       'dir', 'menu', 'ol', 'ul', 'dl',
26087        
26088       'embed',  'object'
26089 ];
26090
26091
26092 Roo.HtmlEditorCore.black = [
26093     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26094         'applet', // 
26095         'base',   'basefont', 'bgsound', 'blink',  'body', 
26096         'frame',  'frameset', 'head',    'html',   'ilayer', 
26097         'iframe', 'layer',  'link',     'meta',    'object',   
26098         'script', 'style' ,'title',  'xml' // clean later..
26099 ];
26100 Roo.HtmlEditorCore.clean = [
26101     'script', 'style', 'title', 'xml'
26102 ];
26103 Roo.HtmlEditorCore.remove = [
26104     'font'
26105 ];
26106 // attributes..
26107
26108 Roo.HtmlEditorCore.ablack = [
26109     'on'
26110 ];
26111     
26112 Roo.HtmlEditorCore.aclean = [ 
26113     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26114 ];
26115
26116 // protocols..
26117 Roo.HtmlEditorCore.pwhite= [
26118         'http',  'https',  'mailto'
26119 ];
26120
26121 // white listed style attributes.
26122 Roo.HtmlEditorCore.cwhite= [
26123       //  'text-align', /// default is to allow most things..
26124       
26125          
26126 //        'font-size'//??
26127 ];
26128
26129 // black listed style attributes.
26130 Roo.HtmlEditorCore.cblack= [
26131       //  'font-size' -- this can be set by the project 
26132 ];
26133
26134
26135 Roo.HtmlEditorCore.swapCodes   =[ 
26136     [    8211, "&#8211;" ], 
26137     [    8212, "&#8212;" ], 
26138     [    8216,  "'" ],  
26139     [    8217, "'" ],  
26140     [    8220, '"' ],  
26141     [    8221, '"' ],  
26142     [    8226, "*" ],  
26143     [    8230, "..." ]
26144 ]; 
26145
26146     /*
26147  * - LGPL
26148  *
26149  * HtmlEditor
26150  * 
26151  */
26152
26153 /**
26154  * @class Roo.bootstrap.HtmlEditor
26155  * @extends Roo.bootstrap.TextArea
26156  * Bootstrap HtmlEditor class
26157
26158  * @constructor
26159  * Create a new HtmlEditor
26160  * @param {Object} config The config object
26161  */
26162
26163 Roo.bootstrap.HtmlEditor = function(config){
26164     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26165     if (!this.toolbars) {
26166         this.toolbars = [];
26167     }
26168     
26169     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26170     this.addEvents({
26171             /**
26172              * @event initialize
26173              * Fires when the editor is fully initialized (including the iframe)
26174              * @param {HtmlEditor} this
26175              */
26176             initialize: true,
26177             /**
26178              * @event activate
26179              * Fires when the editor is first receives the focus. Any insertion must wait
26180              * until after this event.
26181              * @param {HtmlEditor} this
26182              */
26183             activate: true,
26184              /**
26185              * @event beforesync
26186              * Fires before the textarea is updated with content from the editor iframe. Return false
26187              * to cancel the sync.
26188              * @param {HtmlEditor} this
26189              * @param {String} html
26190              */
26191             beforesync: true,
26192              /**
26193              * @event beforepush
26194              * Fires before the iframe editor is updated with content from the textarea. Return false
26195              * to cancel the push.
26196              * @param {HtmlEditor} this
26197              * @param {String} html
26198              */
26199             beforepush: true,
26200              /**
26201              * @event sync
26202              * Fires when the textarea is updated with content from the editor iframe.
26203              * @param {HtmlEditor} this
26204              * @param {String} html
26205              */
26206             sync: true,
26207              /**
26208              * @event push
26209              * Fires when the iframe editor is updated with content from the textarea.
26210              * @param {HtmlEditor} this
26211              * @param {String} html
26212              */
26213             push: true,
26214              /**
26215              * @event editmodechange
26216              * Fires when the editor switches edit modes
26217              * @param {HtmlEditor} this
26218              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26219              */
26220             editmodechange: true,
26221             /**
26222              * @event editorevent
26223              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26224              * @param {HtmlEditor} this
26225              */
26226             editorevent: true,
26227             /**
26228              * @event firstfocus
26229              * Fires when on first focus - needed by toolbars..
26230              * @param {HtmlEditor} this
26231              */
26232             firstfocus: true,
26233             /**
26234              * @event autosave
26235              * Auto save the htmlEditor value as a file into Events
26236              * @param {HtmlEditor} this
26237              */
26238             autosave: true,
26239             /**
26240              * @event savedpreview
26241              * preview the saved version of htmlEditor
26242              * @param {HtmlEditor} this
26243              */
26244             savedpreview: true
26245         });
26246 };
26247
26248
26249 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26250     
26251     
26252       /**
26253      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26254      */
26255     toolbars : false,
26256     
26257      /**
26258     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26259     */
26260     btns : [],
26261    
26262      /**
26263      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26264      *                        Roo.resizable.
26265      */
26266     resizable : false,
26267      /**
26268      * @cfg {Number} height (in pixels)
26269      */   
26270     height: 300,
26271    /**
26272      * @cfg {Number} width (in pixels)
26273      */   
26274     width: false,
26275     
26276     /**
26277      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26278      * 
26279      */
26280     stylesheets: false,
26281     
26282     // id of frame..
26283     frameId: false,
26284     
26285     // private properties
26286     validationEvent : false,
26287     deferHeight: true,
26288     initialized : false,
26289     activated : false,
26290     
26291     onFocus : Roo.emptyFn,
26292     iframePad:3,
26293     hideMode:'offsets',
26294     
26295     tbContainer : false,
26296     
26297     bodyCls : '',
26298     
26299     toolbarContainer :function() {
26300         return this.wrap.select('.x-html-editor-tb',true).first();
26301     },
26302
26303     /**
26304      * Protected method that will not generally be called directly. It
26305      * is called when the editor creates its toolbar. Override this method if you need to
26306      * add custom toolbar buttons.
26307      * @param {HtmlEditor} editor
26308      */
26309     createToolbar : function(){
26310         Roo.log('renewing');
26311         Roo.log("create toolbars");
26312         
26313         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26314         this.toolbars[0].render(this.toolbarContainer());
26315         
26316         return;
26317         
26318 //        if (!editor.toolbars || !editor.toolbars.length) {
26319 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26320 //        }
26321 //        
26322 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26323 //            editor.toolbars[i] = Roo.factory(
26324 //                    typeof(editor.toolbars[i]) == 'string' ?
26325 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26326 //                Roo.bootstrap.HtmlEditor);
26327 //            editor.toolbars[i].init(editor);
26328 //        }
26329     },
26330
26331      
26332     // private
26333     onRender : function(ct, position)
26334     {
26335        // Roo.log("Call onRender: " + this.xtype);
26336         var _t = this;
26337         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26338       
26339         this.wrap = this.inputEl().wrap({
26340             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26341         });
26342         
26343         this.editorcore.onRender(ct, position);
26344          
26345         if (this.resizable) {
26346             this.resizeEl = new Roo.Resizable(this.wrap, {
26347                 pinned : true,
26348                 wrap: true,
26349                 dynamic : true,
26350                 minHeight : this.height,
26351                 height: this.height,
26352                 handles : this.resizable,
26353                 width: this.width,
26354                 listeners : {
26355                     resize : function(r, w, h) {
26356                         _t.onResize(w,h); // -something
26357                     }
26358                 }
26359             });
26360             
26361         }
26362         this.createToolbar(this);
26363        
26364         
26365         if(!this.width && this.resizable){
26366             this.setSize(this.wrap.getSize());
26367         }
26368         if (this.resizeEl) {
26369             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26370             // should trigger onReize..
26371         }
26372         
26373     },
26374
26375     // private
26376     onResize : function(w, h)
26377     {
26378         Roo.log('resize: ' +w + ',' + h );
26379         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26380         var ew = false;
26381         var eh = false;
26382         
26383         if(this.inputEl() ){
26384             if(typeof w == 'number'){
26385                 var aw = w - this.wrap.getFrameWidth('lr');
26386                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26387                 ew = aw;
26388             }
26389             if(typeof h == 'number'){
26390                  var tbh = -11;  // fixme it needs to tool bar size!
26391                 for (var i =0; i < this.toolbars.length;i++) {
26392                     // fixme - ask toolbars for heights?
26393                     tbh += this.toolbars[i].el.getHeight();
26394                     //if (this.toolbars[i].footer) {
26395                     //    tbh += this.toolbars[i].footer.el.getHeight();
26396                     //}
26397                 }
26398               
26399                 
26400                 
26401                 
26402                 
26403                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26404                 ah -= 5; // knock a few pixes off for look..
26405                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26406                 var eh = ah;
26407             }
26408         }
26409         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26410         this.editorcore.onResize(ew,eh);
26411         
26412     },
26413
26414     /**
26415      * Toggles the editor between standard and source edit mode.
26416      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26417      */
26418     toggleSourceEdit : function(sourceEditMode)
26419     {
26420         this.editorcore.toggleSourceEdit(sourceEditMode);
26421         
26422         if(this.editorcore.sourceEditMode){
26423             Roo.log('editor - showing textarea');
26424             
26425 //            Roo.log('in');
26426 //            Roo.log(this.syncValue());
26427             this.syncValue();
26428             this.inputEl().removeClass(['hide', 'x-hidden']);
26429             this.inputEl().dom.removeAttribute('tabIndex');
26430             this.inputEl().focus();
26431         }else{
26432             Roo.log('editor - hiding textarea');
26433 //            Roo.log('out')
26434 //            Roo.log(this.pushValue()); 
26435             this.pushValue();
26436             
26437             this.inputEl().addClass(['hide', 'x-hidden']);
26438             this.inputEl().dom.setAttribute('tabIndex', -1);
26439             //this.deferFocus();
26440         }
26441          
26442         if(this.resizable){
26443             this.setSize(this.wrap.getSize());
26444         }
26445         
26446         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26447     },
26448  
26449     // private (for BoxComponent)
26450     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26451
26452     // private (for BoxComponent)
26453     getResizeEl : function(){
26454         return this.wrap;
26455     },
26456
26457     // private (for BoxComponent)
26458     getPositionEl : function(){
26459         return this.wrap;
26460     },
26461
26462     // private
26463     initEvents : function(){
26464         this.originalValue = this.getValue();
26465     },
26466
26467 //    /**
26468 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26469 //     * @method
26470 //     */
26471 //    markInvalid : Roo.emptyFn,
26472 //    /**
26473 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26474 //     * @method
26475 //     */
26476 //    clearInvalid : Roo.emptyFn,
26477
26478     setValue : function(v){
26479         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26480         this.editorcore.pushValue();
26481     },
26482
26483      
26484     // private
26485     deferFocus : function(){
26486         this.focus.defer(10, this);
26487     },
26488
26489     // doc'ed in Field
26490     focus : function(){
26491         this.editorcore.focus();
26492         
26493     },
26494       
26495
26496     // private
26497     onDestroy : function(){
26498         
26499         
26500         
26501         if(this.rendered){
26502             
26503             for (var i =0; i < this.toolbars.length;i++) {
26504                 // fixme - ask toolbars for heights?
26505                 this.toolbars[i].onDestroy();
26506             }
26507             
26508             this.wrap.dom.innerHTML = '';
26509             this.wrap.remove();
26510         }
26511     },
26512
26513     // private
26514     onFirstFocus : function(){
26515         //Roo.log("onFirstFocus");
26516         this.editorcore.onFirstFocus();
26517          for (var i =0; i < this.toolbars.length;i++) {
26518             this.toolbars[i].onFirstFocus();
26519         }
26520         
26521     },
26522     
26523     // private
26524     syncValue : function()
26525     {   
26526         this.editorcore.syncValue();
26527     },
26528     
26529     pushValue : function()
26530     {   
26531         this.editorcore.pushValue();
26532     }
26533      
26534     
26535     // hide stuff that is not compatible
26536     /**
26537      * @event blur
26538      * @hide
26539      */
26540     /**
26541      * @event change
26542      * @hide
26543      */
26544     /**
26545      * @event focus
26546      * @hide
26547      */
26548     /**
26549      * @event specialkey
26550      * @hide
26551      */
26552     /**
26553      * @cfg {String} fieldClass @hide
26554      */
26555     /**
26556      * @cfg {String} focusClass @hide
26557      */
26558     /**
26559      * @cfg {String} autoCreate @hide
26560      */
26561     /**
26562      * @cfg {String} inputType @hide
26563      */
26564      
26565     /**
26566      * @cfg {String} invalidText @hide
26567      */
26568     /**
26569      * @cfg {String} msgFx @hide
26570      */
26571     /**
26572      * @cfg {String} validateOnBlur @hide
26573      */
26574 });
26575  
26576     
26577    
26578    
26579    
26580       
26581 Roo.namespace('Roo.bootstrap.htmleditor');
26582 /**
26583  * @class Roo.bootstrap.HtmlEditorToolbar1
26584  * Basic Toolbar
26585  * 
26586  * @example
26587  * Usage:
26588  *
26589  new Roo.bootstrap.HtmlEditor({
26590     ....
26591     toolbars : [
26592         new Roo.bootstrap.HtmlEditorToolbar1({
26593             disable : { fonts: 1 , format: 1, ..., ... , ...],
26594             btns : [ .... ]
26595         })
26596     }
26597      
26598  * 
26599  * @cfg {Object} disable List of elements to disable..
26600  * @cfg {Array} btns List of additional buttons.
26601  * 
26602  * 
26603  * NEEDS Extra CSS? 
26604  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26605  */
26606  
26607 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26608 {
26609     
26610     Roo.apply(this, config);
26611     
26612     // default disabled, based on 'good practice'..
26613     this.disable = this.disable || {};
26614     Roo.applyIf(this.disable, {
26615         fontSize : true,
26616         colors : true,
26617         specialElements : true
26618     });
26619     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26620     
26621     this.editor = config.editor;
26622     this.editorcore = config.editor.editorcore;
26623     
26624     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26625     
26626     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26627     // dont call parent... till later.
26628 }
26629 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26630      
26631     bar : true,
26632     
26633     editor : false,
26634     editorcore : false,
26635     
26636     
26637     formats : [
26638         "p" ,  
26639         "h1","h2","h3","h4","h5","h6", 
26640         "pre", "code", 
26641         "abbr", "acronym", "address", "cite", "samp", "var",
26642         'div','span'
26643     ],
26644     
26645     onRender : function(ct, position)
26646     {
26647        // Roo.log("Call onRender: " + this.xtype);
26648         
26649        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26650        Roo.log(this.el);
26651        this.el.dom.style.marginBottom = '0';
26652        var _this = this;
26653        var editorcore = this.editorcore;
26654        var editor= this.editor;
26655        
26656        var children = [];
26657        var btn = function(id,cmd , toggle, handler, html){
26658        
26659             var  event = toggle ? 'toggle' : 'click';
26660        
26661             var a = {
26662                 size : 'sm',
26663                 xtype: 'Button',
26664                 xns: Roo.bootstrap,
26665                 //glyphicon : id,
26666                 fa: id,
26667                 cmd : id || cmd,
26668                 enableToggle:toggle !== false,
26669                 html : html || '',
26670                 pressed : toggle ? false : null,
26671                 listeners : {}
26672             };
26673             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26674                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26675             };
26676             children.push(a);
26677             return a;
26678        }
26679        
26680     //    var cb_box = function...
26681         
26682         var style = {
26683                 xtype: 'Button',
26684                 size : 'sm',
26685                 xns: Roo.bootstrap,
26686                 fa : 'font',
26687                 //html : 'submit'
26688                 menu : {
26689                     xtype: 'Menu',
26690                     xns: Roo.bootstrap,
26691                     items:  []
26692                 }
26693         };
26694         Roo.each(this.formats, function(f) {
26695             style.menu.items.push({
26696                 xtype :'MenuItem',
26697                 xns: Roo.bootstrap,
26698                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26699                 tagname : f,
26700                 listeners : {
26701                     click : function()
26702                     {
26703                         editorcore.insertTag(this.tagname);
26704                         editor.focus();
26705                     }
26706                 }
26707                 
26708             });
26709         });
26710         children.push(style);   
26711         
26712         btn('bold',false,true);
26713         btn('italic',false,true);
26714         btn('align-left', 'justifyleft',true);
26715         btn('align-center', 'justifycenter',true);
26716         btn('align-right' , 'justifyright',true);
26717         btn('link', false, false, function(btn) {
26718             //Roo.log("create link?");
26719             var url = prompt(this.createLinkText, this.defaultLinkValue);
26720             if(url && url != 'http:/'+'/'){
26721                 this.editorcore.relayCmd('createlink', url);
26722             }
26723         }),
26724         btn('list','insertunorderedlist',true);
26725         btn('pencil', false,true, function(btn){
26726                 Roo.log(this);
26727                 this.toggleSourceEdit(btn.pressed);
26728         });
26729         
26730         if (this.editor.btns.length > 0) {
26731             for (var i = 0; i<this.editor.btns.length; i++) {
26732                 children.push(this.editor.btns[i]);
26733             }
26734         }
26735         
26736         /*
26737         var cog = {
26738                 xtype: 'Button',
26739                 size : 'sm',
26740                 xns: Roo.bootstrap,
26741                 glyphicon : 'cog',
26742                 //html : 'submit'
26743                 menu : {
26744                     xtype: 'Menu',
26745                     xns: Roo.bootstrap,
26746                     items:  []
26747                 }
26748         };
26749         
26750         cog.menu.items.push({
26751             xtype :'MenuItem',
26752             xns: Roo.bootstrap,
26753             html : Clean styles,
26754             tagname : f,
26755             listeners : {
26756                 click : function()
26757                 {
26758                     editorcore.insertTag(this.tagname);
26759                     editor.focus();
26760                 }
26761             }
26762             
26763         });
26764        */
26765         
26766          
26767        this.xtype = 'NavSimplebar';
26768         
26769         for(var i=0;i< children.length;i++) {
26770             
26771             this.buttons.add(this.addxtypeChild(children[i]));
26772             
26773         }
26774         
26775         editor.on('editorevent', this.updateToolbar, this);
26776     },
26777     onBtnClick : function(id)
26778     {
26779        this.editorcore.relayCmd(id);
26780        this.editorcore.focus();
26781     },
26782     
26783     /**
26784      * Protected method that will not generally be called directly. It triggers
26785      * a toolbar update by reading the markup state of the current selection in the editor.
26786      */
26787     updateToolbar: function(){
26788
26789         if(!this.editorcore.activated){
26790             this.editor.onFirstFocus(); // is this neeed?
26791             return;
26792         }
26793
26794         var btns = this.buttons; 
26795         var doc = this.editorcore.doc;
26796         btns.get('bold').setActive(doc.queryCommandState('bold'));
26797         btns.get('italic').setActive(doc.queryCommandState('italic'));
26798         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26799         
26800         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26801         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26802         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26803         
26804         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26805         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26806          /*
26807         
26808         var ans = this.editorcore.getAllAncestors();
26809         if (this.formatCombo) {
26810             
26811             
26812             var store = this.formatCombo.store;
26813             this.formatCombo.setValue("");
26814             for (var i =0; i < ans.length;i++) {
26815                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26816                     // select it..
26817                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26818                     break;
26819                 }
26820             }
26821         }
26822         
26823         
26824         
26825         // hides menus... - so this cant be on a menu...
26826         Roo.bootstrap.MenuMgr.hideAll();
26827         */
26828         Roo.bootstrap.MenuMgr.hideAll();
26829         //this.editorsyncValue();
26830     },
26831     onFirstFocus: function() {
26832         this.buttons.each(function(item){
26833            item.enable();
26834         });
26835     },
26836     toggleSourceEdit : function(sourceEditMode){
26837         
26838           
26839         if(sourceEditMode){
26840             Roo.log("disabling buttons");
26841            this.buttons.each( function(item){
26842                 if(item.cmd != 'pencil'){
26843                     item.disable();
26844                 }
26845             });
26846           
26847         }else{
26848             Roo.log("enabling buttons");
26849             if(this.editorcore.initialized){
26850                 this.buttons.each( function(item){
26851                     item.enable();
26852                 });
26853             }
26854             
26855         }
26856         Roo.log("calling toggole on editor");
26857         // tell the editor that it's been pressed..
26858         this.editor.toggleSourceEdit(sourceEditMode);
26859        
26860     }
26861 });
26862
26863
26864
26865
26866  
26867 /*
26868  * - LGPL
26869  */
26870
26871 /**
26872  * @class Roo.bootstrap.Markdown
26873  * @extends Roo.bootstrap.TextArea
26874  * Bootstrap Showdown editable area
26875  * @cfg {string} content
26876  * 
26877  * @constructor
26878  * Create a new Showdown
26879  */
26880
26881 Roo.bootstrap.Markdown = function(config){
26882     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26883    
26884 };
26885
26886 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26887     
26888     editing :false,
26889     
26890     initEvents : function()
26891     {
26892         
26893         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26894         this.markdownEl = this.el.createChild({
26895             cls : 'roo-markdown-area'
26896         });
26897         this.inputEl().addClass('d-none');
26898         if (this.getValue() == '') {
26899             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26900             
26901         } else {
26902             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26903         }
26904         this.markdownEl.on('click', this.toggleTextEdit, this);
26905         this.on('blur', this.toggleTextEdit, this);
26906         this.on('specialkey', this.resizeTextArea, this);
26907     },
26908     
26909     toggleTextEdit : function()
26910     {
26911         var sh = this.markdownEl.getHeight();
26912         this.inputEl().addClass('d-none');
26913         this.markdownEl.addClass('d-none');
26914         if (!this.editing) {
26915             // show editor?
26916             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26917             this.inputEl().removeClass('d-none');
26918             this.inputEl().focus();
26919             this.editing = true;
26920             return;
26921         }
26922         // show showdown...
26923         this.updateMarkdown();
26924         this.markdownEl.removeClass('d-none');
26925         this.editing = false;
26926         return;
26927     },
26928     updateMarkdown : function()
26929     {
26930         if (this.getValue() == '') {
26931             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26932             return;
26933         }
26934  
26935         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26936     },
26937     
26938     resizeTextArea: function () {
26939         
26940         var sh = 100;
26941         Roo.log([sh, this.getValue().split("\n").length * 30]);
26942         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26943     },
26944     setValue : function(val)
26945     {
26946         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26947         if (!this.editing) {
26948             this.updateMarkdown();
26949         }
26950         
26951     },
26952     focus : function()
26953     {
26954         if (!this.editing) {
26955             this.toggleTextEdit();
26956         }
26957         
26958     }
26959
26960
26961 });
26962 /**
26963  * @class Roo.bootstrap.Table.AbstractSelectionModel
26964  * @extends Roo.util.Observable
26965  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26966  * implemented by descendant classes.  This class should not be directly instantiated.
26967  * @constructor
26968  */
26969 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26970     this.locked = false;
26971     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26972 };
26973
26974
26975 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26976     /** @ignore Called by the grid automatically. Do not call directly. */
26977     init : function(grid){
26978         this.grid = grid;
26979         this.initEvents();
26980     },
26981
26982     /**
26983      * Locks the selections.
26984      */
26985     lock : function(){
26986         this.locked = true;
26987     },
26988
26989     /**
26990      * Unlocks the selections.
26991      */
26992     unlock : function(){
26993         this.locked = false;
26994     },
26995
26996     /**
26997      * Returns true if the selections are locked.
26998      * @return {Boolean}
26999      */
27000     isLocked : function(){
27001         return this.locked;
27002     },
27003     
27004     
27005     initEvents : function ()
27006     {
27007         
27008     }
27009 });
27010 /**
27011  * @extends Roo.bootstrap.Table.AbstractSelectionModel
27012  * @class Roo.bootstrap.Table.RowSelectionModel
27013  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27014  * It supports multiple selections and keyboard selection/navigation. 
27015  * @constructor
27016  * @param {Object} config
27017  */
27018
27019 Roo.bootstrap.Table.RowSelectionModel = function(config){
27020     Roo.apply(this, config);
27021     this.selections = new Roo.util.MixedCollection(false, function(o){
27022         return o.id;
27023     });
27024
27025     this.last = false;
27026     this.lastActive = false;
27027
27028     this.addEvents({
27029         /**
27030              * @event selectionchange
27031              * Fires when the selection changes
27032              * @param {SelectionModel} this
27033              */
27034             "selectionchange" : true,
27035         /**
27036              * @event afterselectionchange
27037              * Fires after the selection changes (eg. by key press or clicking)
27038              * @param {SelectionModel} this
27039              */
27040             "afterselectionchange" : true,
27041         /**
27042              * @event beforerowselect
27043              * Fires when a row is selected being selected, return false to cancel.
27044              * @param {SelectionModel} this
27045              * @param {Number} rowIndex The selected index
27046              * @param {Boolean} keepExisting False if other selections will be cleared
27047              */
27048             "beforerowselect" : true,
27049         /**
27050              * @event rowselect
27051              * Fires when a row is selected.
27052              * @param {SelectionModel} this
27053              * @param {Number} rowIndex The selected index
27054              * @param {Roo.data.Record} r The record
27055              */
27056             "rowselect" : true,
27057         /**
27058              * @event rowdeselect
27059              * Fires when a row is deselected.
27060              * @param {SelectionModel} this
27061              * @param {Number} rowIndex The selected index
27062              */
27063         "rowdeselect" : true
27064     });
27065     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27066     this.locked = false;
27067  };
27068
27069 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27070     /**
27071      * @cfg {Boolean} singleSelect
27072      * True to allow selection of only one row at a time (defaults to false)
27073      */
27074     singleSelect : false,
27075
27076     // private
27077     initEvents : function()
27078     {
27079
27080         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27081         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27082         //}else{ // allow click to work like normal
27083          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27084         //}
27085         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27086         this.grid.on("rowclick", this.handleMouseDown, this);
27087         
27088         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27089             "up" : function(e){
27090                 if(!e.shiftKey){
27091                     this.selectPrevious(e.shiftKey);
27092                 }else if(this.last !== false && this.lastActive !== false){
27093                     var last = this.last;
27094                     this.selectRange(this.last,  this.lastActive-1);
27095                     this.grid.getView().focusRow(this.lastActive);
27096                     if(last !== false){
27097                         this.last = last;
27098                     }
27099                 }else{
27100                     this.selectFirstRow();
27101                 }
27102                 this.fireEvent("afterselectionchange", this);
27103             },
27104             "down" : function(e){
27105                 if(!e.shiftKey){
27106                     this.selectNext(e.shiftKey);
27107                 }else if(this.last !== false && this.lastActive !== false){
27108                     var last = this.last;
27109                     this.selectRange(this.last,  this.lastActive+1);
27110                     this.grid.getView().focusRow(this.lastActive);
27111                     if(last !== false){
27112                         this.last = last;
27113                     }
27114                 }else{
27115                     this.selectFirstRow();
27116                 }
27117                 this.fireEvent("afterselectionchange", this);
27118             },
27119             scope: this
27120         });
27121         this.grid.store.on('load', function(){
27122             this.selections.clear();
27123         },this);
27124         /*
27125         var view = this.grid.view;
27126         view.on("refresh", this.onRefresh, this);
27127         view.on("rowupdated", this.onRowUpdated, this);
27128         view.on("rowremoved", this.onRemove, this);
27129         */
27130     },
27131
27132     // private
27133     onRefresh : function()
27134     {
27135         var ds = this.grid.store, i, v = this.grid.view;
27136         var s = this.selections;
27137         s.each(function(r){
27138             if((i = ds.indexOfId(r.id)) != -1){
27139                 v.onRowSelect(i);
27140             }else{
27141                 s.remove(r);
27142             }
27143         });
27144     },
27145
27146     // private
27147     onRemove : function(v, index, r){
27148         this.selections.remove(r);
27149     },
27150
27151     // private
27152     onRowUpdated : function(v, index, r){
27153         if(this.isSelected(r)){
27154             v.onRowSelect(index);
27155         }
27156     },
27157
27158     /**
27159      * Select records.
27160      * @param {Array} records The records to select
27161      * @param {Boolean} keepExisting (optional) True to keep existing selections
27162      */
27163     selectRecords : function(records, keepExisting)
27164     {
27165         if(!keepExisting){
27166             this.clearSelections();
27167         }
27168             var ds = this.grid.store;
27169         for(var i = 0, len = records.length; i < len; i++){
27170             this.selectRow(ds.indexOf(records[i]), true);
27171         }
27172     },
27173
27174     /**
27175      * Gets the number of selected rows.
27176      * @return {Number}
27177      */
27178     getCount : function(){
27179         return this.selections.length;
27180     },
27181
27182     /**
27183      * Selects the first row in the grid.
27184      */
27185     selectFirstRow : function(){
27186         this.selectRow(0);
27187     },
27188
27189     /**
27190      * Select the last row.
27191      * @param {Boolean} keepExisting (optional) True to keep existing selections
27192      */
27193     selectLastRow : function(keepExisting){
27194         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27195         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27196     },
27197
27198     /**
27199      * Selects the row immediately following the last selected row.
27200      * @param {Boolean} keepExisting (optional) True to keep existing selections
27201      */
27202     selectNext : function(keepExisting)
27203     {
27204             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27205             this.selectRow(this.last+1, keepExisting);
27206             this.grid.getView().focusRow(this.last);
27207         }
27208     },
27209
27210     /**
27211      * Selects the row that precedes the last selected row.
27212      * @param {Boolean} keepExisting (optional) True to keep existing selections
27213      */
27214     selectPrevious : function(keepExisting){
27215         if(this.last){
27216             this.selectRow(this.last-1, keepExisting);
27217             this.grid.getView().focusRow(this.last);
27218         }
27219     },
27220
27221     /**
27222      * Returns the selected records
27223      * @return {Array} Array of selected records
27224      */
27225     getSelections : function(){
27226         return [].concat(this.selections.items);
27227     },
27228
27229     /**
27230      * Returns the first selected record.
27231      * @return {Record}
27232      */
27233     getSelected : function(){
27234         return this.selections.itemAt(0);
27235     },
27236
27237
27238     /**
27239      * Clears all selections.
27240      */
27241     clearSelections : function(fast)
27242     {
27243         if(this.locked) {
27244             return;
27245         }
27246         if(fast !== true){
27247                 var ds = this.grid.store;
27248             var s = this.selections;
27249             s.each(function(r){
27250                 this.deselectRow(ds.indexOfId(r.id));
27251             }, this);
27252             s.clear();
27253         }else{
27254             this.selections.clear();
27255         }
27256         this.last = false;
27257     },
27258
27259
27260     /**
27261      * Selects all rows.
27262      */
27263     selectAll : function(){
27264         if(this.locked) {
27265             return;
27266         }
27267         this.selections.clear();
27268         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27269             this.selectRow(i, true);
27270         }
27271     },
27272
27273     /**
27274      * Returns True if there is a selection.
27275      * @return {Boolean}
27276      */
27277     hasSelection : function(){
27278         return this.selections.length > 0;
27279     },
27280
27281     /**
27282      * Returns True if the specified row is selected.
27283      * @param {Number/Record} record The record or index of the record to check
27284      * @return {Boolean}
27285      */
27286     isSelected : function(index){
27287             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27288         return (r && this.selections.key(r.id) ? true : false);
27289     },
27290
27291     /**
27292      * Returns True if the specified record id is selected.
27293      * @param {String} id The id of record to check
27294      * @return {Boolean}
27295      */
27296     isIdSelected : function(id){
27297         return (this.selections.key(id) ? true : false);
27298     },
27299
27300
27301     // private
27302     handleMouseDBClick : function(e, t){
27303         
27304     },
27305     // private
27306     handleMouseDown : function(e, t)
27307     {
27308             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27309         if(this.isLocked() || rowIndex < 0 ){
27310             return;
27311         };
27312         if(e.shiftKey && this.last !== false){
27313             var last = this.last;
27314             this.selectRange(last, rowIndex, e.ctrlKey);
27315             this.last = last; // reset the last
27316             t.focus();
27317     
27318         }else{
27319             var isSelected = this.isSelected(rowIndex);
27320             //Roo.log("select row:" + rowIndex);
27321             if(isSelected){
27322                 this.deselectRow(rowIndex);
27323             } else {
27324                         this.selectRow(rowIndex, true);
27325             }
27326     
27327             /*
27328                 if(e.button !== 0 && isSelected){
27329                 alert('rowIndex 2: ' + rowIndex);
27330                     view.focusRow(rowIndex);
27331                 }else if(e.ctrlKey && isSelected){
27332                     this.deselectRow(rowIndex);
27333                 }else if(!isSelected){
27334                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27335                     view.focusRow(rowIndex);
27336                 }
27337             */
27338         }
27339         this.fireEvent("afterselectionchange", this);
27340     },
27341     // private
27342     handleDragableRowClick :  function(grid, rowIndex, e) 
27343     {
27344         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27345             this.selectRow(rowIndex, false);
27346             grid.view.focusRow(rowIndex);
27347              this.fireEvent("afterselectionchange", this);
27348         }
27349     },
27350     
27351     /**
27352      * Selects multiple rows.
27353      * @param {Array} rows Array of the indexes of the row to select
27354      * @param {Boolean} keepExisting (optional) True to keep existing selections
27355      */
27356     selectRows : function(rows, keepExisting){
27357         if(!keepExisting){
27358             this.clearSelections();
27359         }
27360         for(var i = 0, len = rows.length; i < len; i++){
27361             this.selectRow(rows[i], true);
27362         }
27363     },
27364
27365     /**
27366      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27367      * @param {Number} startRow The index of the first row in the range
27368      * @param {Number} endRow The index of the last row in the range
27369      * @param {Boolean} keepExisting (optional) True to retain existing selections
27370      */
27371     selectRange : function(startRow, endRow, keepExisting){
27372         if(this.locked) {
27373             return;
27374         }
27375         if(!keepExisting){
27376             this.clearSelections();
27377         }
27378         if(startRow <= endRow){
27379             for(var i = startRow; i <= endRow; i++){
27380                 this.selectRow(i, true);
27381             }
27382         }else{
27383             for(var i = startRow; i >= endRow; i--){
27384                 this.selectRow(i, true);
27385             }
27386         }
27387     },
27388
27389     /**
27390      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27391      * @param {Number} startRow The index of the first row in the range
27392      * @param {Number} endRow The index of the last row in the range
27393      */
27394     deselectRange : function(startRow, endRow, preventViewNotify){
27395         if(this.locked) {
27396             return;
27397         }
27398         for(var i = startRow; i <= endRow; i++){
27399             this.deselectRow(i, preventViewNotify);
27400         }
27401     },
27402
27403     /**
27404      * Selects a row.
27405      * @param {Number} row The index of the row to select
27406      * @param {Boolean} keepExisting (optional) True to keep existing selections
27407      */
27408     selectRow : function(index, keepExisting, preventViewNotify)
27409     {
27410             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27411             return;
27412         }
27413         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27414             if(!keepExisting || this.singleSelect){
27415                 this.clearSelections();
27416             }
27417             
27418             var r = this.grid.store.getAt(index);
27419             //console.log('selectRow - record id :' + r.id);
27420             
27421             this.selections.add(r);
27422             this.last = this.lastActive = index;
27423             if(!preventViewNotify){
27424                 var proxy = new Roo.Element(
27425                                 this.grid.getRowDom(index)
27426                 );
27427                 proxy.addClass('bg-info info');
27428             }
27429             this.fireEvent("rowselect", this, index, r);
27430             this.fireEvent("selectionchange", this);
27431         }
27432     },
27433
27434     /**
27435      * Deselects a row.
27436      * @param {Number} row The index of the row to deselect
27437      */
27438     deselectRow : function(index, preventViewNotify)
27439     {
27440         if(this.locked) {
27441             return;
27442         }
27443         if(this.last == index){
27444             this.last = false;
27445         }
27446         if(this.lastActive == index){
27447             this.lastActive = false;
27448         }
27449         
27450         var r = this.grid.store.getAt(index);
27451         if (!r) {
27452             return;
27453         }
27454         
27455         this.selections.remove(r);
27456         //.console.log('deselectRow - record id :' + r.id);
27457         if(!preventViewNotify){
27458         
27459             var proxy = new Roo.Element(
27460                 this.grid.getRowDom(index)
27461             );
27462             proxy.removeClass('bg-info info');
27463         }
27464         this.fireEvent("rowdeselect", this, index);
27465         this.fireEvent("selectionchange", this);
27466     },
27467
27468     // private
27469     restoreLast : function(){
27470         if(this._last){
27471             this.last = this._last;
27472         }
27473     },
27474
27475     // private
27476     acceptsNav : function(row, col, cm){
27477         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27478     },
27479
27480     // private
27481     onEditorKey : function(field, e){
27482         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27483         if(k == e.TAB){
27484             e.stopEvent();
27485             ed.completeEdit();
27486             if(e.shiftKey){
27487                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27488             }else{
27489                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27490             }
27491         }else if(k == e.ENTER && !e.ctrlKey){
27492             e.stopEvent();
27493             ed.completeEdit();
27494             if(e.shiftKey){
27495                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27496             }else{
27497                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27498             }
27499         }else if(k == e.ESC){
27500             ed.cancelEdit();
27501         }
27502         if(newCell){
27503             g.startEditing(newCell[0], newCell[1]);
27504         }
27505     }
27506 });
27507 /*
27508  * Based on:
27509  * Ext JS Library 1.1.1
27510  * Copyright(c) 2006-2007, Ext JS, LLC.
27511  *
27512  * Originally Released Under LGPL - original licence link has changed is not relivant.
27513  *
27514  * Fork - LGPL
27515  * <script type="text/javascript">
27516  */
27517  
27518 /**
27519  * @class Roo.bootstrap.PagingToolbar
27520  * @extends Roo.bootstrap.NavSimplebar
27521  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27522  * @constructor
27523  * Create a new PagingToolbar
27524  * @param {Object} config The config object
27525  * @param {Roo.data.Store} store
27526  */
27527 Roo.bootstrap.PagingToolbar = function(config)
27528 {
27529     // old args format still supported... - xtype is prefered..
27530         // created from xtype...
27531     
27532     this.ds = config.dataSource;
27533     
27534     if (config.store && !this.ds) {
27535         this.store= Roo.factory(config.store, Roo.data);
27536         this.ds = this.store;
27537         this.ds.xmodule = this.xmodule || false;
27538     }
27539     
27540     this.toolbarItems = [];
27541     if (config.items) {
27542         this.toolbarItems = config.items;
27543     }
27544     
27545     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27546     
27547     this.cursor = 0;
27548     
27549     if (this.ds) { 
27550         this.bind(this.ds);
27551     }
27552     
27553     if (Roo.bootstrap.version == 4) {
27554         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27555     } else {
27556         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27557     }
27558     
27559 };
27560
27561 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27562     /**
27563      * @cfg {Roo.data.Store} dataSource
27564      * The underlying data store providing the paged data
27565      */
27566     /**
27567      * @cfg {String/HTMLElement/Element} container
27568      * container The id or element that will contain the toolbar
27569      */
27570     /**
27571      * @cfg {Boolean} displayInfo
27572      * True to display the displayMsg (defaults to false)
27573      */
27574     /**
27575      * @cfg {Number} pageSize
27576      * The number of records to display per page (defaults to 20)
27577      */
27578     pageSize: 20,
27579     /**
27580      * @cfg {String} displayMsg
27581      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27582      */
27583     displayMsg : 'Displaying {0} - {1} of {2}',
27584     /**
27585      * @cfg {String} emptyMsg
27586      * The message to display when no records are found (defaults to "No data to display")
27587      */
27588     emptyMsg : 'No data to display',
27589     /**
27590      * Customizable piece of the default paging text (defaults to "Page")
27591      * @type String
27592      */
27593     beforePageText : "Page",
27594     /**
27595      * Customizable piece of the default paging text (defaults to "of %0")
27596      * @type String
27597      */
27598     afterPageText : "of {0}",
27599     /**
27600      * Customizable piece of the default paging text (defaults to "First Page")
27601      * @type String
27602      */
27603     firstText : "First Page",
27604     /**
27605      * Customizable piece of the default paging text (defaults to "Previous Page")
27606      * @type String
27607      */
27608     prevText : "Previous Page",
27609     /**
27610      * Customizable piece of the default paging text (defaults to "Next Page")
27611      * @type String
27612      */
27613     nextText : "Next Page",
27614     /**
27615      * Customizable piece of the default paging text (defaults to "Last Page")
27616      * @type String
27617      */
27618     lastText : "Last Page",
27619     /**
27620      * Customizable piece of the default paging text (defaults to "Refresh")
27621      * @type String
27622      */
27623     refreshText : "Refresh",
27624
27625     buttons : false,
27626     // private
27627     onRender : function(ct, position) 
27628     {
27629         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27630         this.navgroup.parentId = this.id;
27631         this.navgroup.onRender(this.el, null);
27632         // add the buttons to the navgroup
27633         
27634         if(this.displayInfo){
27635             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27636             this.displayEl = this.el.select('.x-paging-info', true).first();
27637 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27638 //            this.displayEl = navel.el.select('span',true).first();
27639         }
27640         
27641         var _this = this;
27642         
27643         if(this.buttons){
27644             Roo.each(_this.buttons, function(e){ // this might need to use render????
27645                Roo.factory(e).render(_this.el);
27646             });
27647         }
27648             
27649         Roo.each(_this.toolbarItems, function(e) {
27650             _this.navgroup.addItem(e);
27651         });
27652         
27653         
27654         this.first = this.navgroup.addItem({
27655             tooltip: this.firstText,
27656             cls: "prev btn-outline-secondary",
27657             html : ' <i class="fa fa-step-backward"></i>',
27658             disabled: true,
27659             preventDefault: true,
27660             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27661         });
27662         
27663         this.prev =  this.navgroup.addItem({
27664             tooltip: this.prevText,
27665             cls: "prev btn-outline-secondary",
27666             html : ' <i class="fa fa-backward"></i>',
27667             disabled: true,
27668             preventDefault: true,
27669             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27670         });
27671     //this.addSeparator();
27672         
27673         
27674         var field = this.navgroup.addItem( {
27675             tagtype : 'span',
27676             cls : 'x-paging-position  btn-outline-secondary',
27677              disabled: true,
27678             html : this.beforePageText  +
27679                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27680                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27681          } ); //?? escaped?
27682         
27683         this.field = field.el.select('input', true).first();
27684         this.field.on("keydown", this.onPagingKeydown, this);
27685         this.field.on("focus", function(){this.dom.select();});
27686     
27687     
27688         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27689         //this.field.setHeight(18);
27690         //this.addSeparator();
27691         this.next = this.navgroup.addItem({
27692             tooltip: this.nextText,
27693             cls: "next btn-outline-secondary",
27694             html : ' <i class="fa fa-forward"></i>',
27695             disabled: true,
27696             preventDefault: true,
27697             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27698         });
27699         this.last = this.navgroup.addItem({
27700             tooltip: this.lastText,
27701             html : ' <i class="fa fa-step-forward"></i>',
27702             cls: "next btn-outline-secondary",
27703             disabled: true,
27704             preventDefault: true,
27705             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27706         });
27707     //this.addSeparator();
27708         this.loading = this.navgroup.addItem({
27709             tooltip: this.refreshText,
27710             cls: "btn-outline-secondary",
27711             html : ' <i class="fa fa-refresh"></i>',
27712             preventDefault: true,
27713             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27714         });
27715         
27716     },
27717
27718     // private
27719     updateInfo : function(){
27720         if(this.displayEl){
27721             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27722             var msg = count == 0 ?
27723                 this.emptyMsg :
27724                 String.format(
27725                     this.displayMsg,
27726                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27727                 );
27728             this.displayEl.update(msg);
27729         }
27730     },
27731
27732     // private
27733     onLoad : function(ds, r, o)
27734     {
27735         this.cursor = o.params && o.params.start ? o.params.start : 0;
27736         
27737         var d = this.getPageData(),
27738             ap = d.activePage,
27739             ps = d.pages;
27740         
27741         
27742         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27743         this.field.dom.value = ap;
27744         this.first.setDisabled(ap == 1);
27745         this.prev.setDisabled(ap == 1);
27746         this.next.setDisabled(ap == ps);
27747         this.last.setDisabled(ap == ps);
27748         this.loading.enable();
27749         this.updateInfo();
27750     },
27751
27752     // private
27753     getPageData : function(){
27754         var total = this.ds.getTotalCount();
27755         return {
27756             total : total,
27757             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27758             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27759         };
27760     },
27761
27762     // private
27763     onLoadError : function(){
27764         this.loading.enable();
27765     },
27766
27767     // private
27768     onPagingKeydown : function(e){
27769         var k = e.getKey();
27770         var d = this.getPageData();
27771         if(k == e.RETURN){
27772             var v = this.field.dom.value, pageNum;
27773             if(!v || isNaN(pageNum = parseInt(v, 10))){
27774                 this.field.dom.value = d.activePage;
27775                 return;
27776             }
27777             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27778             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27779             e.stopEvent();
27780         }
27781         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))
27782         {
27783           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27784           this.field.dom.value = pageNum;
27785           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27786           e.stopEvent();
27787         }
27788         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27789         {
27790           var v = this.field.dom.value, pageNum; 
27791           var increment = (e.shiftKey) ? 10 : 1;
27792           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27793                 increment *= -1;
27794           }
27795           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27796             this.field.dom.value = d.activePage;
27797             return;
27798           }
27799           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27800           {
27801             this.field.dom.value = parseInt(v, 10) + increment;
27802             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27803             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27804           }
27805           e.stopEvent();
27806         }
27807     },
27808
27809     // private
27810     beforeLoad : function(){
27811         if(this.loading){
27812             this.loading.disable();
27813         }
27814     },
27815
27816     // private
27817     onClick : function(which){
27818         
27819         var ds = this.ds;
27820         if (!ds) {
27821             return;
27822         }
27823         
27824         switch(which){
27825             case "first":
27826                 ds.load({params:{start: 0, limit: this.pageSize}});
27827             break;
27828             case "prev":
27829                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27830             break;
27831             case "next":
27832                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27833             break;
27834             case "last":
27835                 var total = ds.getTotalCount();
27836                 var extra = total % this.pageSize;
27837                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27838                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27839             break;
27840             case "refresh":
27841                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27842             break;
27843         }
27844     },
27845
27846     /**
27847      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27848      * @param {Roo.data.Store} store The data store to unbind
27849      */
27850     unbind : function(ds){
27851         ds.un("beforeload", this.beforeLoad, this);
27852         ds.un("load", this.onLoad, this);
27853         ds.un("loadexception", this.onLoadError, this);
27854         ds.un("remove", this.updateInfo, this);
27855         ds.un("add", this.updateInfo, this);
27856         this.ds = undefined;
27857     },
27858
27859     /**
27860      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27861      * @param {Roo.data.Store} store The data store to bind
27862      */
27863     bind : function(ds){
27864         ds.on("beforeload", this.beforeLoad, this);
27865         ds.on("load", this.onLoad, this);
27866         ds.on("loadexception", this.onLoadError, this);
27867         ds.on("remove", this.updateInfo, this);
27868         ds.on("add", this.updateInfo, this);
27869         this.ds = ds;
27870     }
27871 });/*
27872  * - LGPL
27873  *
27874  * element
27875  * 
27876  */
27877
27878 /**
27879  * @class Roo.bootstrap.MessageBar
27880  * @extends Roo.bootstrap.Component
27881  * Bootstrap MessageBar class
27882  * @cfg {String} html contents of the MessageBar
27883  * @cfg {String} weight (info | success | warning | danger) default info
27884  * @cfg {String} beforeClass insert the bar before the given class
27885  * @cfg {Boolean} closable (true | false) default false
27886  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27887  * 
27888  * @constructor
27889  * Create a new Element
27890  * @param {Object} config The config object
27891  */
27892
27893 Roo.bootstrap.MessageBar = function(config){
27894     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27895 };
27896
27897 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27898     
27899     html: '',
27900     weight: 'info',
27901     closable: false,
27902     fixed: false,
27903     beforeClass: 'bootstrap-sticky-wrap',
27904     
27905     getAutoCreate : function(){
27906         
27907         var cfg = {
27908             tag: 'div',
27909             cls: 'alert alert-dismissable alert-' + this.weight,
27910             cn: [
27911                 {
27912                     tag: 'span',
27913                     cls: 'message',
27914                     html: this.html || ''
27915                 }
27916             ]
27917         };
27918         
27919         if(this.fixed){
27920             cfg.cls += ' alert-messages-fixed';
27921         }
27922         
27923         if(this.closable){
27924             cfg.cn.push({
27925                 tag: 'button',
27926                 cls: 'close',
27927                 html: 'x'
27928             });
27929         }
27930         
27931         return cfg;
27932     },
27933     
27934     onRender : function(ct, position)
27935     {
27936         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27937         
27938         if(!this.el){
27939             var cfg = Roo.apply({},  this.getAutoCreate());
27940             cfg.id = Roo.id();
27941             
27942             if (this.cls) {
27943                 cfg.cls += ' ' + this.cls;
27944             }
27945             if (this.style) {
27946                 cfg.style = this.style;
27947             }
27948             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27949             
27950             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27951         }
27952         
27953         this.el.select('>button.close').on('click', this.hide, this);
27954         
27955     },
27956     
27957     show : function()
27958     {
27959         if (!this.rendered) {
27960             this.render();
27961         }
27962         
27963         this.el.show();
27964         
27965         this.fireEvent('show', this);
27966         
27967     },
27968     
27969     hide : function()
27970     {
27971         if (!this.rendered) {
27972             this.render();
27973         }
27974         
27975         this.el.hide();
27976         
27977         this.fireEvent('hide', this);
27978     },
27979     
27980     update : function()
27981     {
27982 //        var e = this.el.dom.firstChild;
27983 //        
27984 //        if(this.closable){
27985 //            e = e.nextSibling;
27986 //        }
27987 //        
27988 //        e.data = this.html || '';
27989
27990         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27991     }
27992    
27993 });
27994
27995  
27996
27997      /*
27998  * - LGPL
27999  *
28000  * Graph
28001  * 
28002  */
28003
28004
28005 /**
28006  * @class Roo.bootstrap.Graph
28007  * @extends Roo.bootstrap.Component
28008  * Bootstrap Graph class
28009 > Prameters
28010  -sm {number} sm 4
28011  -md {number} md 5
28012  @cfg {String} graphtype  bar | vbar | pie
28013  @cfg {number} g_x coodinator | centre x (pie)
28014  @cfg {number} g_y coodinator | centre y (pie)
28015  @cfg {number} g_r radius (pie)
28016  @cfg {number} g_height height of the chart (respected by all elements in the set)
28017  @cfg {number} g_width width of the chart (respected by all elements in the set)
28018  @cfg {Object} title The title of the chart
28019     
28020  -{Array}  values
28021  -opts (object) options for the chart 
28022      o {
28023      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28024      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28025      o vgutter (number)
28026      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.
28027      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28028      o to
28029      o stretch (boolean)
28030      o }
28031  -opts (object) options for the pie
28032      o{
28033      o cut
28034      o startAngle (number)
28035      o endAngle (number)
28036      } 
28037  *
28038  * @constructor
28039  * Create a new Input
28040  * @param {Object} config The config object
28041  */
28042
28043 Roo.bootstrap.Graph = function(config){
28044     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28045     
28046     this.addEvents({
28047         // img events
28048         /**
28049          * @event click
28050          * The img click event for the img.
28051          * @param {Roo.EventObject} e
28052          */
28053         "click" : true
28054     });
28055 };
28056
28057 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28058     
28059     sm: 4,
28060     md: 5,
28061     graphtype: 'bar',
28062     g_height: 250,
28063     g_width: 400,
28064     g_x: 50,
28065     g_y: 50,
28066     g_r: 30,
28067     opts:{
28068         //g_colors: this.colors,
28069         g_type: 'soft',
28070         g_gutter: '20%'
28071
28072     },
28073     title : false,
28074
28075     getAutoCreate : function(){
28076         
28077         var cfg = {
28078             tag: 'div',
28079             html : null
28080         };
28081         
28082         
28083         return  cfg;
28084     },
28085
28086     onRender : function(ct,position){
28087         
28088         
28089         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28090         
28091         if (typeof(Raphael) == 'undefined') {
28092             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28093             return;
28094         }
28095         
28096         this.raphael = Raphael(this.el.dom);
28097         
28098                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28099                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28100                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28101                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28102                 /*
28103                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28104                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28105                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28106                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28107                 
28108                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28109                 r.barchart(330, 10, 300, 220, data1);
28110                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28111                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28112                 */
28113                 
28114                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28115                 // r.barchart(30, 30, 560, 250,  xdata, {
28116                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28117                 //     axis : "0 0 1 1",
28118                 //     axisxlabels :  xdata
28119                 //     //yvalues : cols,
28120                    
28121                 // });
28122 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28123 //        
28124 //        this.load(null,xdata,{
28125 //                axis : "0 0 1 1",
28126 //                axisxlabels :  xdata
28127 //                });
28128
28129     },
28130
28131     load : function(graphtype,xdata,opts)
28132     {
28133         this.raphael.clear();
28134         if(!graphtype) {
28135             graphtype = this.graphtype;
28136         }
28137         if(!opts){
28138             opts = this.opts;
28139         }
28140         var r = this.raphael,
28141             fin = function () {
28142                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28143             },
28144             fout = function () {
28145                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28146             },
28147             pfin = function() {
28148                 this.sector.stop();
28149                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28150
28151                 if (this.label) {
28152                     this.label[0].stop();
28153                     this.label[0].attr({ r: 7.5 });
28154                     this.label[1].attr({ "font-weight": 800 });
28155                 }
28156             },
28157             pfout = function() {
28158                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28159
28160                 if (this.label) {
28161                     this.label[0].animate({ r: 5 }, 500, "bounce");
28162                     this.label[1].attr({ "font-weight": 400 });
28163                 }
28164             };
28165
28166         switch(graphtype){
28167             case 'bar':
28168                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28169                 break;
28170             case 'hbar':
28171                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28172                 break;
28173             case 'pie':
28174 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28175 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28176 //            
28177                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28178                 
28179                 break;
28180
28181         }
28182         
28183         if(this.title){
28184             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28185         }
28186         
28187     },
28188     
28189     setTitle: function(o)
28190     {
28191         this.title = o;
28192     },
28193     
28194     initEvents: function() {
28195         
28196         if(!this.href){
28197             this.el.on('click', this.onClick, this);
28198         }
28199     },
28200     
28201     onClick : function(e)
28202     {
28203         Roo.log('img onclick');
28204         this.fireEvent('click', this, e);
28205     }
28206    
28207 });
28208
28209  
28210 /*
28211  * - LGPL
28212  *
28213  * numberBox
28214  * 
28215  */
28216 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28217
28218 /**
28219  * @class Roo.bootstrap.dash.NumberBox
28220  * @extends Roo.bootstrap.Component
28221  * Bootstrap NumberBox class
28222  * @cfg {String} headline Box headline
28223  * @cfg {String} content Box content
28224  * @cfg {String} icon Box icon
28225  * @cfg {String} footer Footer text
28226  * @cfg {String} fhref Footer href
28227  * 
28228  * @constructor
28229  * Create a new NumberBox
28230  * @param {Object} config The config object
28231  */
28232
28233
28234 Roo.bootstrap.dash.NumberBox = function(config){
28235     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28236     
28237 };
28238
28239 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28240     
28241     headline : '',
28242     content : '',
28243     icon : '',
28244     footer : '',
28245     fhref : '',
28246     ficon : '',
28247     
28248     getAutoCreate : function(){
28249         
28250         var cfg = {
28251             tag : 'div',
28252             cls : 'small-box ',
28253             cn : [
28254                 {
28255                     tag : 'div',
28256                     cls : 'inner',
28257                     cn :[
28258                         {
28259                             tag : 'h3',
28260                             cls : 'roo-headline',
28261                             html : this.headline
28262                         },
28263                         {
28264                             tag : 'p',
28265                             cls : 'roo-content',
28266                             html : this.content
28267                         }
28268                     ]
28269                 }
28270             ]
28271         };
28272         
28273         if(this.icon){
28274             cfg.cn.push({
28275                 tag : 'div',
28276                 cls : 'icon',
28277                 cn :[
28278                     {
28279                         tag : 'i',
28280                         cls : 'ion ' + this.icon
28281                     }
28282                 ]
28283             });
28284         }
28285         
28286         if(this.footer){
28287             var footer = {
28288                 tag : 'a',
28289                 cls : 'small-box-footer',
28290                 href : this.fhref || '#',
28291                 html : this.footer
28292             };
28293             
28294             cfg.cn.push(footer);
28295             
28296         }
28297         
28298         return  cfg;
28299     },
28300
28301     onRender : function(ct,position){
28302         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28303
28304
28305        
28306                 
28307     },
28308
28309     setHeadline: function (value)
28310     {
28311         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28312     },
28313     
28314     setFooter: function (value, href)
28315     {
28316         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28317         
28318         if(href){
28319             this.el.select('a.small-box-footer',true).first().attr('href', href);
28320         }
28321         
28322     },
28323
28324     setContent: function (value)
28325     {
28326         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28327     },
28328
28329     initEvents: function() 
28330     {   
28331         
28332     }
28333     
28334 });
28335
28336  
28337 /*
28338  * - LGPL
28339  *
28340  * TabBox
28341  * 
28342  */
28343 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28344
28345 /**
28346  * @class Roo.bootstrap.dash.TabBox
28347  * @extends Roo.bootstrap.Component
28348  * Bootstrap TabBox class
28349  * @cfg {String} title Title of the TabBox
28350  * @cfg {String} icon Icon of the TabBox
28351  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28352  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28353  * 
28354  * @constructor
28355  * Create a new TabBox
28356  * @param {Object} config The config object
28357  */
28358
28359
28360 Roo.bootstrap.dash.TabBox = function(config){
28361     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28362     this.addEvents({
28363         // raw events
28364         /**
28365          * @event addpane
28366          * When a pane is added
28367          * @param {Roo.bootstrap.dash.TabPane} pane
28368          */
28369         "addpane" : true,
28370         /**
28371          * @event activatepane
28372          * When a pane is activated
28373          * @param {Roo.bootstrap.dash.TabPane} pane
28374          */
28375         "activatepane" : true
28376         
28377          
28378     });
28379     
28380     this.panes = [];
28381 };
28382
28383 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28384
28385     title : '',
28386     icon : false,
28387     showtabs : true,
28388     tabScrollable : false,
28389     
28390     getChildContainer : function()
28391     {
28392         return this.el.select('.tab-content', true).first();
28393     },
28394     
28395     getAutoCreate : function(){
28396         
28397         var header = {
28398             tag: 'li',
28399             cls: 'pull-left header',
28400             html: this.title,
28401             cn : []
28402         };
28403         
28404         if(this.icon){
28405             header.cn.push({
28406                 tag: 'i',
28407                 cls: 'fa ' + this.icon
28408             });
28409         }
28410         
28411         var h = {
28412             tag: 'ul',
28413             cls: 'nav nav-tabs pull-right',
28414             cn: [
28415                 header
28416             ]
28417         };
28418         
28419         if(this.tabScrollable){
28420             h = {
28421                 tag: 'div',
28422                 cls: 'tab-header',
28423                 cn: [
28424                     {
28425                         tag: 'ul',
28426                         cls: 'nav nav-tabs pull-right',
28427                         cn: [
28428                             header
28429                         ]
28430                     }
28431                 ]
28432             };
28433         }
28434         
28435         var cfg = {
28436             tag: 'div',
28437             cls: 'nav-tabs-custom',
28438             cn: [
28439                 h,
28440                 {
28441                     tag: 'div',
28442                     cls: 'tab-content no-padding',
28443                     cn: []
28444                 }
28445             ]
28446         };
28447
28448         return  cfg;
28449     },
28450     initEvents : function()
28451     {
28452         //Roo.log('add add pane handler');
28453         this.on('addpane', this.onAddPane, this);
28454     },
28455      /**
28456      * Updates the box title
28457      * @param {String} html to set the title to.
28458      */
28459     setTitle : function(value)
28460     {
28461         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28462     },
28463     onAddPane : function(pane)
28464     {
28465         this.panes.push(pane);
28466         //Roo.log('addpane');
28467         //Roo.log(pane);
28468         // tabs are rendere left to right..
28469         if(!this.showtabs){
28470             return;
28471         }
28472         
28473         var ctr = this.el.select('.nav-tabs', true).first();
28474          
28475          
28476         var existing = ctr.select('.nav-tab',true);
28477         var qty = existing.getCount();;
28478         
28479         
28480         var tab = ctr.createChild({
28481             tag : 'li',
28482             cls : 'nav-tab' + (qty ? '' : ' active'),
28483             cn : [
28484                 {
28485                     tag : 'a',
28486                     href:'#',
28487                     html : pane.title
28488                 }
28489             ]
28490         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28491         pane.tab = tab;
28492         
28493         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28494         if (!qty) {
28495             pane.el.addClass('active');
28496         }
28497         
28498                 
28499     },
28500     onTabClick : function(ev,un,ob,pane)
28501     {
28502         //Roo.log('tab - prev default');
28503         ev.preventDefault();
28504         
28505         
28506         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28507         pane.tab.addClass('active');
28508         //Roo.log(pane.title);
28509         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28510         // technically we should have a deactivate event.. but maybe add later.
28511         // and it should not de-activate the selected tab...
28512         this.fireEvent('activatepane', pane);
28513         pane.el.addClass('active');
28514         pane.fireEvent('activate');
28515         
28516         
28517     },
28518     
28519     getActivePane : function()
28520     {
28521         var r = false;
28522         Roo.each(this.panes, function(p) {
28523             if(p.el.hasClass('active')){
28524                 r = p;
28525                 return false;
28526             }
28527             
28528             return;
28529         });
28530         
28531         return r;
28532     }
28533     
28534     
28535 });
28536
28537  
28538 /*
28539  * - LGPL
28540  *
28541  * Tab pane
28542  * 
28543  */
28544 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28545 /**
28546  * @class Roo.bootstrap.TabPane
28547  * @extends Roo.bootstrap.Component
28548  * Bootstrap TabPane class
28549  * @cfg {Boolean} active (false | true) Default false
28550  * @cfg {String} title title of panel
28551
28552  * 
28553  * @constructor
28554  * Create a new TabPane
28555  * @param {Object} config The config object
28556  */
28557
28558 Roo.bootstrap.dash.TabPane = function(config){
28559     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28560     
28561     this.addEvents({
28562         // raw events
28563         /**
28564          * @event activate
28565          * When a pane is activated
28566          * @param {Roo.bootstrap.dash.TabPane} pane
28567          */
28568         "activate" : true
28569          
28570     });
28571 };
28572
28573 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28574     
28575     active : false,
28576     title : '',
28577     
28578     // the tabBox that this is attached to.
28579     tab : false,
28580      
28581     getAutoCreate : function() 
28582     {
28583         var cfg = {
28584             tag: 'div',
28585             cls: 'tab-pane'
28586         };
28587         
28588         if(this.active){
28589             cfg.cls += ' active';
28590         }
28591         
28592         return cfg;
28593     },
28594     initEvents  : function()
28595     {
28596         //Roo.log('trigger add pane handler');
28597         this.parent().fireEvent('addpane', this)
28598     },
28599     
28600      /**
28601      * Updates the tab title 
28602      * @param {String} html to set the title to.
28603      */
28604     setTitle: function(str)
28605     {
28606         if (!this.tab) {
28607             return;
28608         }
28609         this.title = str;
28610         this.tab.select('a', true).first().dom.innerHTML = str;
28611         
28612     }
28613     
28614     
28615     
28616 });
28617
28618  
28619
28620
28621  /*
28622  * - LGPL
28623  *
28624  * menu
28625  * 
28626  */
28627 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28628
28629 /**
28630  * @class Roo.bootstrap.menu.Menu
28631  * @extends Roo.bootstrap.Component
28632  * Bootstrap Menu class - container for Menu
28633  * @cfg {String} html Text of the menu
28634  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28635  * @cfg {String} icon Font awesome icon
28636  * @cfg {String} pos Menu align to (top | bottom) default bottom
28637  * 
28638  * 
28639  * @constructor
28640  * Create a new Menu
28641  * @param {Object} config The config object
28642  */
28643
28644
28645 Roo.bootstrap.menu.Menu = function(config){
28646     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28647     
28648     this.addEvents({
28649         /**
28650          * @event beforeshow
28651          * Fires before this menu is displayed
28652          * @param {Roo.bootstrap.menu.Menu} this
28653          */
28654         beforeshow : true,
28655         /**
28656          * @event beforehide
28657          * Fires before this menu is hidden
28658          * @param {Roo.bootstrap.menu.Menu} this
28659          */
28660         beforehide : true,
28661         /**
28662          * @event show
28663          * Fires after this menu is displayed
28664          * @param {Roo.bootstrap.menu.Menu} this
28665          */
28666         show : true,
28667         /**
28668          * @event hide
28669          * Fires after this menu is hidden
28670          * @param {Roo.bootstrap.menu.Menu} this
28671          */
28672         hide : true,
28673         /**
28674          * @event click
28675          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28676          * @param {Roo.bootstrap.menu.Menu} this
28677          * @param {Roo.EventObject} e
28678          */
28679         click : true
28680     });
28681     
28682 };
28683
28684 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28685     
28686     submenu : false,
28687     html : '',
28688     weight : 'default',
28689     icon : false,
28690     pos : 'bottom',
28691     
28692     
28693     getChildContainer : function() {
28694         if(this.isSubMenu){
28695             return this.el;
28696         }
28697         
28698         return this.el.select('ul.dropdown-menu', true).first();  
28699     },
28700     
28701     getAutoCreate : function()
28702     {
28703         var text = [
28704             {
28705                 tag : 'span',
28706                 cls : 'roo-menu-text',
28707                 html : this.html
28708             }
28709         ];
28710         
28711         if(this.icon){
28712             text.unshift({
28713                 tag : 'i',
28714                 cls : 'fa ' + this.icon
28715             })
28716         }
28717         
28718         
28719         var cfg = {
28720             tag : 'div',
28721             cls : 'btn-group',
28722             cn : [
28723                 {
28724                     tag : 'button',
28725                     cls : 'dropdown-button btn btn-' + this.weight,
28726                     cn : text
28727                 },
28728                 {
28729                     tag : 'button',
28730                     cls : 'dropdown-toggle btn btn-' + this.weight,
28731                     cn : [
28732                         {
28733                             tag : 'span',
28734                             cls : 'caret'
28735                         }
28736                     ]
28737                 },
28738                 {
28739                     tag : 'ul',
28740                     cls : 'dropdown-menu'
28741                 }
28742             ]
28743             
28744         };
28745         
28746         if(this.pos == 'top'){
28747             cfg.cls += ' dropup';
28748         }
28749         
28750         if(this.isSubMenu){
28751             cfg = {
28752                 tag : 'ul',
28753                 cls : 'dropdown-menu'
28754             }
28755         }
28756         
28757         return cfg;
28758     },
28759     
28760     onRender : function(ct, position)
28761     {
28762         this.isSubMenu = ct.hasClass('dropdown-submenu');
28763         
28764         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28765     },
28766     
28767     initEvents : function() 
28768     {
28769         if(this.isSubMenu){
28770             return;
28771         }
28772         
28773         this.hidden = true;
28774         
28775         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28776         this.triggerEl.on('click', this.onTriggerPress, this);
28777         
28778         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28779         this.buttonEl.on('click', this.onClick, this);
28780         
28781     },
28782     
28783     list : function()
28784     {
28785         if(this.isSubMenu){
28786             return this.el;
28787         }
28788         
28789         return this.el.select('ul.dropdown-menu', true).first();
28790     },
28791     
28792     onClick : function(e)
28793     {
28794         this.fireEvent("click", this, e);
28795     },
28796     
28797     onTriggerPress  : function(e)
28798     {   
28799         if (this.isVisible()) {
28800             this.hide();
28801         } else {
28802             this.show();
28803         }
28804     },
28805     
28806     isVisible : function(){
28807         return !this.hidden;
28808     },
28809     
28810     show : function()
28811     {
28812         this.fireEvent("beforeshow", this);
28813         
28814         this.hidden = false;
28815         this.el.addClass('open');
28816         
28817         Roo.get(document).on("mouseup", this.onMouseUp, this);
28818         
28819         this.fireEvent("show", this);
28820         
28821         
28822     },
28823     
28824     hide : function()
28825     {
28826         this.fireEvent("beforehide", this);
28827         
28828         this.hidden = true;
28829         this.el.removeClass('open');
28830         
28831         Roo.get(document).un("mouseup", this.onMouseUp);
28832         
28833         this.fireEvent("hide", this);
28834     },
28835     
28836     onMouseUp : function()
28837     {
28838         this.hide();
28839     }
28840     
28841 });
28842
28843  
28844  /*
28845  * - LGPL
28846  *
28847  * menu item
28848  * 
28849  */
28850 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28851
28852 /**
28853  * @class Roo.bootstrap.menu.Item
28854  * @extends Roo.bootstrap.Component
28855  * Bootstrap MenuItem class
28856  * @cfg {Boolean} submenu (true | false) default false
28857  * @cfg {String} html text of the item
28858  * @cfg {String} href the link
28859  * @cfg {Boolean} disable (true | false) default false
28860  * @cfg {Boolean} preventDefault (true | false) default true
28861  * @cfg {String} icon Font awesome icon
28862  * @cfg {String} pos Submenu align to (left | right) default right 
28863  * 
28864  * 
28865  * @constructor
28866  * Create a new Item
28867  * @param {Object} config The config object
28868  */
28869
28870
28871 Roo.bootstrap.menu.Item = function(config){
28872     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28873     this.addEvents({
28874         /**
28875          * @event mouseover
28876          * Fires when the mouse is hovering over this menu
28877          * @param {Roo.bootstrap.menu.Item} this
28878          * @param {Roo.EventObject} e
28879          */
28880         mouseover : true,
28881         /**
28882          * @event mouseout
28883          * Fires when the mouse exits this menu
28884          * @param {Roo.bootstrap.menu.Item} this
28885          * @param {Roo.EventObject} e
28886          */
28887         mouseout : true,
28888         // raw events
28889         /**
28890          * @event click
28891          * The raw click event for the entire grid.
28892          * @param {Roo.EventObject} e
28893          */
28894         click : true
28895     });
28896 };
28897
28898 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28899     
28900     submenu : false,
28901     href : '',
28902     html : '',
28903     preventDefault: true,
28904     disable : false,
28905     icon : false,
28906     pos : 'right',
28907     
28908     getAutoCreate : function()
28909     {
28910         var text = [
28911             {
28912                 tag : 'span',
28913                 cls : 'roo-menu-item-text',
28914                 html : this.html
28915             }
28916         ];
28917         
28918         if(this.icon){
28919             text.unshift({
28920                 tag : 'i',
28921                 cls : 'fa ' + this.icon
28922             })
28923         }
28924         
28925         var cfg = {
28926             tag : 'li',
28927             cn : [
28928                 {
28929                     tag : 'a',
28930                     href : this.href || '#',
28931                     cn : text
28932                 }
28933             ]
28934         };
28935         
28936         if(this.disable){
28937             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28938         }
28939         
28940         if(this.submenu){
28941             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28942             
28943             if(this.pos == 'left'){
28944                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28945             }
28946         }
28947         
28948         return cfg;
28949     },
28950     
28951     initEvents : function() 
28952     {
28953         this.el.on('mouseover', this.onMouseOver, this);
28954         this.el.on('mouseout', this.onMouseOut, this);
28955         
28956         this.el.select('a', true).first().on('click', this.onClick, this);
28957         
28958     },
28959     
28960     onClick : function(e)
28961     {
28962         if(this.preventDefault){
28963             e.preventDefault();
28964         }
28965         
28966         this.fireEvent("click", this, e);
28967     },
28968     
28969     onMouseOver : function(e)
28970     {
28971         if(this.submenu && this.pos == 'left'){
28972             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28973         }
28974         
28975         this.fireEvent("mouseover", this, e);
28976     },
28977     
28978     onMouseOut : function(e)
28979     {
28980         this.fireEvent("mouseout", this, e);
28981     }
28982 });
28983
28984  
28985
28986  /*
28987  * - LGPL
28988  *
28989  * menu separator
28990  * 
28991  */
28992 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28993
28994 /**
28995  * @class Roo.bootstrap.menu.Separator
28996  * @extends Roo.bootstrap.Component
28997  * Bootstrap Separator class
28998  * 
28999  * @constructor
29000  * Create a new Separator
29001  * @param {Object} config The config object
29002  */
29003
29004
29005 Roo.bootstrap.menu.Separator = function(config){
29006     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29007 };
29008
29009 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29010     
29011     getAutoCreate : function(){
29012         var cfg = {
29013             tag : 'li',
29014             cls: 'dropdown-divider divider'
29015         };
29016         
29017         return cfg;
29018     }
29019    
29020 });
29021
29022  
29023
29024  /*
29025  * - LGPL
29026  *
29027  * Tooltip
29028  * 
29029  */
29030
29031 /**
29032  * @class Roo.bootstrap.Tooltip
29033  * Bootstrap Tooltip class
29034  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29035  * to determine which dom element triggers the tooltip.
29036  * 
29037  * It needs to add support for additional attributes like tooltip-position
29038  * 
29039  * @constructor
29040  * Create a new Toolti
29041  * @param {Object} config The config object
29042  */
29043
29044 Roo.bootstrap.Tooltip = function(config){
29045     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29046     
29047     this.alignment = Roo.bootstrap.Tooltip.alignment;
29048     
29049     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29050         this.alignment = config.alignment;
29051     }
29052     
29053 };
29054
29055 Roo.apply(Roo.bootstrap.Tooltip, {
29056     /**
29057      * @function init initialize tooltip monitoring.
29058      * @static
29059      */
29060     currentEl : false,
29061     currentTip : false,
29062     currentRegion : false,
29063     
29064     //  init : delay?
29065     
29066     init : function()
29067     {
29068         Roo.get(document).on('mouseover', this.enter ,this);
29069         Roo.get(document).on('mouseout', this.leave, this);
29070          
29071         
29072         this.currentTip = new Roo.bootstrap.Tooltip();
29073     },
29074     
29075     enter : function(ev)
29076     {
29077         var dom = ev.getTarget();
29078         
29079         //Roo.log(['enter',dom]);
29080         var el = Roo.fly(dom);
29081         if (this.currentEl) {
29082             //Roo.log(dom);
29083             //Roo.log(this.currentEl);
29084             //Roo.log(this.currentEl.contains(dom));
29085             if (this.currentEl == el) {
29086                 return;
29087             }
29088             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29089                 return;
29090             }
29091
29092         }
29093         
29094         if (this.currentTip.el) {
29095             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29096         }    
29097         //Roo.log(ev);
29098         
29099         if(!el || el.dom == document){
29100             return;
29101         }
29102         
29103         var bindEl = el; 
29104         var pel = false;
29105         if (!el.attr('tooltip')) {
29106             pel = el.findParent("[tooltip]");
29107             if (pel) {
29108                 bindEl = Roo.get(pel);
29109             }
29110         }
29111         
29112        
29113         
29114         // you can not look for children, as if el is the body.. then everythign is the child..
29115         if (!pel && !el.attr('tooltip')) { //
29116             if (!el.select("[tooltip]").elements.length) {
29117                 return;
29118             }
29119             // is the mouse over this child...?
29120             bindEl = el.select("[tooltip]").first();
29121             var xy = ev.getXY();
29122             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29123                 //Roo.log("not in region.");
29124                 return;
29125             }
29126             //Roo.log("child element over..");
29127             
29128         }
29129         this.currentEl = el;
29130         this.currentTip.bind(bindEl);
29131         this.currentRegion = Roo.lib.Region.getRegion(dom);
29132         this.currentTip.enter();
29133         
29134     },
29135     leave : function(ev)
29136     {
29137         var dom = ev.getTarget();
29138         //Roo.log(['leave',dom]);
29139         if (!this.currentEl) {
29140             return;
29141         }
29142         
29143         
29144         if (dom != this.currentEl.dom) {
29145             return;
29146         }
29147         var xy = ev.getXY();
29148         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29149             return;
29150         }
29151         // only activate leave if mouse cursor is outside... bounding box..
29152         
29153         
29154         
29155         
29156         if (this.currentTip) {
29157             this.currentTip.leave();
29158         }
29159         //Roo.log('clear currentEl');
29160         this.currentEl = false;
29161         
29162         
29163     },
29164     alignment : {
29165         'left' : ['r-l', [-2,0], 'right'],
29166         'right' : ['l-r', [2,0], 'left'],
29167         'bottom' : ['t-b', [0,2], 'top'],
29168         'top' : [ 'b-t', [0,-2], 'bottom']
29169     }
29170     
29171 });
29172
29173
29174 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29175     
29176     
29177     bindEl : false,
29178     
29179     delay : null, // can be { show : 300 , hide: 500}
29180     
29181     timeout : null,
29182     
29183     hoverState : null, //???
29184     
29185     placement : 'bottom', 
29186     
29187     alignment : false,
29188     
29189     getAutoCreate : function(){
29190     
29191         var cfg = {
29192            cls : 'tooltip',   
29193            role : 'tooltip',
29194            cn : [
29195                 {
29196                     cls : 'tooltip-arrow arrow'
29197                 },
29198                 {
29199                     cls : 'tooltip-inner'
29200                 }
29201            ]
29202         };
29203         
29204         return cfg;
29205     },
29206     bind : function(el)
29207     {
29208         this.bindEl = el;
29209     },
29210     
29211     initEvents : function()
29212     {
29213         this.arrowEl = this.el.select('.arrow', true).first();
29214         this.innerEl = this.el.select('.tooltip-inner', true).first();
29215     },
29216     
29217     enter : function () {
29218        
29219         if (this.timeout != null) {
29220             clearTimeout(this.timeout);
29221         }
29222         
29223         this.hoverState = 'in';
29224          //Roo.log("enter - show");
29225         if (!this.delay || !this.delay.show) {
29226             this.show();
29227             return;
29228         }
29229         var _t = this;
29230         this.timeout = setTimeout(function () {
29231             if (_t.hoverState == 'in') {
29232                 _t.show();
29233             }
29234         }, this.delay.show);
29235     },
29236     leave : function()
29237     {
29238         clearTimeout(this.timeout);
29239     
29240         this.hoverState = 'out';
29241          if (!this.delay || !this.delay.hide) {
29242             this.hide();
29243             return;
29244         }
29245        
29246         var _t = this;
29247         this.timeout = setTimeout(function () {
29248             //Roo.log("leave - timeout");
29249             
29250             if (_t.hoverState == 'out') {
29251                 _t.hide();
29252                 Roo.bootstrap.Tooltip.currentEl = false;
29253             }
29254         }, delay);
29255     },
29256     
29257     show : function (msg)
29258     {
29259         if (!this.el) {
29260             this.render(document.body);
29261         }
29262         // set content.
29263         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29264         
29265         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29266         
29267         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29268         
29269         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29270                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29271         
29272         var placement = typeof this.placement == 'function' ?
29273             this.placement.call(this, this.el, on_el) :
29274             this.placement;
29275             
29276         var autoToken = /\s?auto?\s?/i;
29277         var autoPlace = autoToken.test(placement);
29278         if (autoPlace) {
29279             placement = placement.replace(autoToken, '') || 'top';
29280         }
29281         
29282         //this.el.detach()
29283         //this.el.setXY([0,0]);
29284         this.el.show();
29285         //this.el.dom.style.display='block';
29286         
29287         //this.el.appendTo(on_el);
29288         
29289         var p = this.getPosition();
29290         var box = this.el.getBox();
29291         
29292         if (autoPlace) {
29293             // fixme..
29294         }
29295         
29296         var align = this.alignment[placement];
29297         
29298         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29299         
29300         if(placement == 'top' || placement == 'bottom'){
29301             if(xy[0] < 0){
29302                 placement = 'right';
29303             }
29304             
29305             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29306                 placement = 'left';
29307             }
29308             
29309             var scroll = Roo.select('body', true).first().getScroll();
29310             
29311             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29312                 placement = 'top';
29313             }
29314             
29315             align = this.alignment[placement];
29316             
29317             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29318             
29319         }
29320         
29321         var elems = document.getElementsByTagName('div');
29322         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29323         for (var i = 0; i < elems.length; i++) {
29324           var zindex = Number.parseInt(
29325                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29326                 10
29327           );
29328           if (zindex > highest) {
29329             highest = zindex;
29330           }
29331         }
29332         
29333         
29334         
29335         this.el.dom.style.zIndex = highest;
29336         
29337         this.el.alignTo(this.bindEl, align[0],align[1]);
29338         //var arrow = this.el.select('.arrow',true).first();
29339         //arrow.set(align[2], 
29340         
29341         this.el.addClass(placement);
29342         this.el.addClass("bs-tooltip-"+ placement);
29343         
29344         this.el.addClass('in fade show');
29345         
29346         this.hoverState = null;
29347         
29348         if (this.el.hasClass('fade')) {
29349             // fade it?
29350         }
29351         
29352         
29353         
29354         
29355         
29356     },
29357     hide : function()
29358     {
29359          
29360         if (!this.el) {
29361             return;
29362         }
29363         //this.el.setXY([0,0]);
29364         this.el.removeClass(['show', 'in']);
29365         //this.el.hide();
29366         
29367     }
29368     
29369 });
29370  
29371
29372  /*
29373  * - LGPL
29374  *
29375  * Location Picker
29376  * 
29377  */
29378
29379 /**
29380  * @class Roo.bootstrap.LocationPicker
29381  * @extends Roo.bootstrap.Component
29382  * Bootstrap LocationPicker class
29383  * @cfg {Number} latitude Position when init default 0
29384  * @cfg {Number} longitude Position when init default 0
29385  * @cfg {Number} zoom default 15
29386  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29387  * @cfg {Boolean} mapTypeControl default false
29388  * @cfg {Boolean} disableDoubleClickZoom default false
29389  * @cfg {Boolean} scrollwheel default true
29390  * @cfg {Boolean} streetViewControl default false
29391  * @cfg {Number} radius default 0
29392  * @cfg {String} locationName
29393  * @cfg {Boolean} draggable default true
29394  * @cfg {Boolean} enableAutocomplete default false
29395  * @cfg {Boolean} enableReverseGeocode default true
29396  * @cfg {String} markerTitle
29397  * 
29398  * @constructor
29399  * Create a new LocationPicker
29400  * @param {Object} config The config object
29401  */
29402
29403
29404 Roo.bootstrap.LocationPicker = function(config){
29405     
29406     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29407     
29408     this.addEvents({
29409         /**
29410          * @event initial
29411          * Fires when the picker initialized.
29412          * @param {Roo.bootstrap.LocationPicker} this
29413          * @param {Google Location} location
29414          */
29415         initial : true,
29416         /**
29417          * @event positionchanged
29418          * Fires when the picker position changed.
29419          * @param {Roo.bootstrap.LocationPicker} this
29420          * @param {Google Location} location
29421          */
29422         positionchanged : true,
29423         /**
29424          * @event resize
29425          * Fires when the map resize.
29426          * @param {Roo.bootstrap.LocationPicker} this
29427          */
29428         resize : true,
29429         /**
29430          * @event show
29431          * Fires when the map show.
29432          * @param {Roo.bootstrap.LocationPicker} this
29433          */
29434         show : true,
29435         /**
29436          * @event hide
29437          * Fires when the map hide.
29438          * @param {Roo.bootstrap.LocationPicker} this
29439          */
29440         hide : true,
29441         /**
29442          * @event mapClick
29443          * Fires when click the map.
29444          * @param {Roo.bootstrap.LocationPicker} this
29445          * @param {Map event} e
29446          */
29447         mapClick : true,
29448         /**
29449          * @event mapRightClick
29450          * Fires when right click the map.
29451          * @param {Roo.bootstrap.LocationPicker} this
29452          * @param {Map event} e
29453          */
29454         mapRightClick : true,
29455         /**
29456          * @event markerClick
29457          * Fires when click the marker.
29458          * @param {Roo.bootstrap.LocationPicker} this
29459          * @param {Map event} e
29460          */
29461         markerClick : true,
29462         /**
29463          * @event markerRightClick
29464          * Fires when right click the marker.
29465          * @param {Roo.bootstrap.LocationPicker} this
29466          * @param {Map event} e
29467          */
29468         markerRightClick : true,
29469         /**
29470          * @event OverlayViewDraw
29471          * Fires when OverlayView Draw
29472          * @param {Roo.bootstrap.LocationPicker} this
29473          */
29474         OverlayViewDraw : true,
29475         /**
29476          * @event OverlayViewOnAdd
29477          * Fires when OverlayView Draw
29478          * @param {Roo.bootstrap.LocationPicker} this
29479          */
29480         OverlayViewOnAdd : true,
29481         /**
29482          * @event OverlayViewOnRemove
29483          * Fires when OverlayView Draw
29484          * @param {Roo.bootstrap.LocationPicker} this
29485          */
29486         OverlayViewOnRemove : true,
29487         /**
29488          * @event OverlayViewShow
29489          * Fires when OverlayView Draw
29490          * @param {Roo.bootstrap.LocationPicker} this
29491          * @param {Pixel} cpx
29492          */
29493         OverlayViewShow : true,
29494         /**
29495          * @event OverlayViewHide
29496          * Fires when OverlayView Draw
29497          * @param {Roo.bootstrap.LocationPicker} this
29498          */
29499         OverlayViewHide : true,
29500         /**
29501          * @event loadexception
29502          * Fires when load google lib failed.
29503          * @param {Roo.bootstrap.LocationPicker} this
29504          */
29505         loadexception : true
29506     });
29507         
29508 };
29509
29510 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29511     
29512     gMapContext: false,
29513     
29514     latitude: 0,
29515     longitude: 0,
29516     zoom: 15,
29517     mapTypeId: false,
29518     mapTypeControl: false,
29519     disableDoubleClickZoom: false,
29520     scrollwheel: true,
29521     streetViewControl: false,
29522     radius: 0,
29523     locationName: '',
29524     draggable: true,
29525     enableAutocomplete: false,
29526     enableReverseGeocode: true,
29527     markerTitle: '',
29528     
29529     getAutoCreate: function()
29530     {
29531
29532         var cfg = {
29533             tag: 'div',
29534             cls: 'roo-location-picker'
29535         };
29536         
29537         return cfg
29538     },
29539     
29540     initEvents: function(ct, position)
29541     {       
29542         if(!this.el.getWidth() || this.isApplied()){
29543             return;
29544         }
29545         
29546         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29547         
29548         this.initial();
29549     },
29550     
29551     initial: function()
29552     {
29553         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29554             this.fireEvent('loadexception', this);
29555             return;
29556         }
29557         
29558         if(!this.mapTypeId){
29559             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29560         }
29561         
29562         this.gMapContext = this.GMapContext();
29563         
29564         this.initOverlayView();
29565         
29566         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29567         
29568         var _this = this;
29569                 
29570         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29571             _this.setPosition(_this.gMapContext.marker.position);
29572         });
29573         
29574         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29575             _this.fireEvent('mapClick', this, event);
29576             
29577         });
29578
29579         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29580             _this.fireEvent('mapRightClick', this, event);
29581             
29582         });
29583         
29584         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29585             _this.fireEvent('markerClick', this, event);
29586             
29587         });
29588
29589         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29590             _this.fireEvent('markerRightClick', this, event);
29591             
29592         });
29593         
29594         this.setPosition(this.gMapContext.location);
29595         
29596         this.fireEvent('initial', this, this.gMapContext.location);
29597     },
29598     
29599     initOverlayView: function()
29600     {
29601         var _this = this;
29602         
29603         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29604             
29605             draw: function()
29606             {
29607                 _this.fireEvent('OverlayViewDraw', _this);
29608             },
29609             
29610             onAdd: function()
29611             {
29612                 _this.fireEvent('OverlayViewOnAdd', _this);
29613             },
29614             
29615             onRemove: function()
29616             {
29617                 _this.fireEvent('OverlayViewOnRemove', _this);
29618             },
29619             
29620             show: function(cpx)
29621             {
29622                 _this.fireEvent('OverlayViewShow', _this, cpx);
29623             },
29624             
29625             hide: function()
29626             {
29627                 _this.fireEvent('OverlayViewHide', _this);
29628             }
29629             
29630         });
29631     },
29632     
29633     fromLatLngToContainerPixel: function(event)
29634     {
29635         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29636     },
29637     
29638     isApplied: function() 
29639     {
29640         return this.getGmapContext() == false ? false : true;
29641     },
29642     
29643     getGmapContext: function() 
29644     {
29645         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29646     },
29647     
29648     GMapContext: function() 
29649     {
29650         var position = new google.maps.LatLng(this.latitude, this.longitude);
29651         
29652         var _map = new google.maps.Map(this.el.dom, {
29653             center: position,
29654             zoom: this.zoom,
29655             mapTypeId: this.mapTypeId,
29656             mapTypeControl: this.mapTypeControl,
29657             disableDoubleClickZoom: this.disableDoubleClickZoom,
29658             scrollwheel: this.scrollwheel,
29659             streetViewControl: this.streetViewControl,
29660             locationName: this.locationName,
29661             draggable: this.draggable,
29662             enableAutocomplete: this.enableAutocomplete,
29663             enableReverseGeocode: this.enableReverseGeocode
29664         });
29665         
29666         var _marker = new google.maps.Marker({
29667             position: position,
29668             map: _map,
29669             title: this.markerTitle,
29670             draggable: this.draggable
29671         });
29672         
29673         return {
29674             map: _map,
29675             marker: _marker,
29676             circle: null,
29677             location: position,
29678             radius: this.radius,
29679             locationName: this.locationName,
29680             addressComponents: {
29681                 formatted_address: null,
29682                 addressLine1: null,
29683                 addressLine2: null,
29684                 streetName: null,
29685                 streetNumber: null,
29686                 city: null,
29687                 district: null,
29688                 state: null,
29689                 stateOrProvince: null
29690             },
29691             settings: this,
29692             domContainer: this.el.dom,
29693             geodecoder: new google.maps.Geocoder()
29694         };
29695     },
29696     
29697     drawCircle: function(center, radius, options) 
29698     {
29699         if (this.gMapContext.circle != null) {
29700             this.gMapContext.circle.setMap(null);
29701         }
29702         if (radius > 0) {
29703             radius *= 1;
29704             options = Roo.apply({}, options, {
29705                 strokeColor: "#0000FF",
29706                 strokeOpacity: .35,
29707                 strokeWeight: 2,
29708                 fillColor: "#0000FF",
29709                 fillOpacity: .2
29710             });
29711             
29712             options.map = this.gMapContext.map;
29713             options.radius = radius;
29714             options.center = center;
29715             this.gMapContext.circle = new google.maps.Circle(options);
29716             return this.gMapContext.circle;
29717         }
29718         
29719         return null;
29720     },
29721     
29722     setPosition: function(location) 
29723     {
29724         this.gMapContext.location = location;
29725         this.gMapContext.marker.setPosition(location);
29726         this.gMapContext.map.panTo(location);
29727         this.drawCircle(location, this.gMapContext.radius, {});
29728         
29729         var _this = this;
29730         
29731         if (this.gMapContext.settings.enableReverseGeocode) {
29732             this.gMapContext.geodecoder.geocode({
29733                 latLng: this.gMapContext.location
29734             }, function(results, status) {
29735                 
29736                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29737                     _this.gMapContext.locationName = results[0].formatted_address;
29738                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29739                     
29740                     _this.fireEvent('positionchanged', this, location);
29741                 }
29742             });
29743             
29744             return;
29745         }
29746         
29747         this.fireEvent('positionchanged', this, location);
29748     },
29749     
29750     resize: function()
29751     {
29752         google.maps.event.trigger(this.gMapContext.map, "resize");
29753         
29754         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29755         
29756         this.fireEvent('resize', this);
29757     },
29758     
29759     setPositionByLatLng: function(latitude, longitude)
29760     {
29761         this.setPosition(new google.maps.LatLng(latitude, longitude));
29762     },
29763     
29764     getCurrentPosition: function() 
29765     {
29766         return {
29767             latitude: this.gMapContext.location.lat(),
29768             longitude: this.gMapContext.location.lng()
29769         };
29770     },
29771     
29772     getAddressName: function() 
29773     {
29774         return this.gMapContext.locationName;
29775     },
29776     
29777     getAddressComponents: function() 
29778     {
29779         return this.gMapContext.addressComponents;
29780     },
29781     
29782     address_component_from_google_geocode: function(address_components) 
29783     {
29784         var result = {};
29785         
29786         for (var i = 0; i < address_components.length; i++) {
29787             var component = address_components[i];
29788             if (component.types.indexOf("postal_code") >= 0) {
29789                 result.postalCode = component.short_name;
29790             } else if (component.types.indexOf("street_number") >= 0) {
29791                 result.streetNumber = component.short_name;
29792             } else if (component.types.indexOf("route") >= 0) {
29793                 result.streetName = component.short_name;
29794             } else if (component.types.indexOf("neighborhood") >= 0) {
29795                 result.city = component.short_name;
29796             } else if (component.types.indexOf("locality") >= 0) {
29797                 result.city = component.short_name;
29798             } else if (component.types.indexOf("sublocality") >= 0) {
29799                 result.district = component.short_name;
29800             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29801                 result.stateOrProvince = component.short_name;
29802             } else if (component.types.indexOf("country") >= 0) {
29803                 result.country = component.short_name;
29804             }
29805         }
29806         
29807         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29808         result.addressLine2 = "";
29809         return result;
29810     },
29811     
29812     setZoomLevel: function(zoom)
29813     {
29814         this.gMapContext.map.setZoom(zoom);
29815     },
29816     
29817     show: function()
29818     {
29819         if(!this.el){
29820             return;
29821         }
29822         
29823         this.el.show();
29824         
29825         this.resize();
29826         
29827         this.fireEvent('show', this);
29828     },
29829     
29830     hide: function()
29831     {
29832         if(!this.el){
29833             return;
29834         }
29835         
29836         this.el.hide();
29837         
29838         this.fireEvent('hide', this);
29839     }
29840     
29841 });
29842
29843 Roo.apply(Roo.bootstrap.LocationPicker, {
29844     
29845     OverlayView : function(map, options)
29846     {
29847         options = options || {};
29848         
29849         this.setMap(map);
29850     }
29851     
29852     
29853 });/**
29854  * @class Roo.bootstrap.Alert
29855  * @extends Roo.bootstrap.Component
29856  * Bootstrap Alert class - shows an alert area box
29857  * eg
29858  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29859   Enter a valid email address
29860 </div>
29861  * @licence LGPL
29862  * @cfg {String} title The title of alert
29863  * @cfg {String} html The content of alert
29864  * @cfg {String} weight (  success | info | warning | danger )
29865  * @cfg {String} fa font-awesomeicon
29866  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29867  * @cfg {Boolean} close true to show a x closer
29868  * 
29869  * 
29870  * @constructor
29871  * Create a new alert
29872  * @param {Object} config The config object
29873  */
29874
29875
29876 Roo.bootstrap.Alert = function(config){
29877     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29878     
29879 };
29880
29881 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29882     
29883     title: '',
29884     html: '',
29885     weight: false,
29886     fa: false,
29887     faicon: false, // BC
29888     close : false,
29889     
29890     
29891     getAutoCreate : function()
29892     {
29893         
29894         var cfg = {
29895             tag : 'div',
29896             cls : 'alert',
29897             cn : [
29898                 {
29899                     tag: 'button',
29900                     type :  "button",
29901                     cls: "close",
29902                     html : '×',
29903                     style : this.close ? '' : 'display:none'
29904                 },
29905                 {
29906                     tag : 'i',
29907                     cls : 'roo-alert-icon'
29908                     
29909                 },
29910                 {
29911                     tag : 'b',
29912                     cls : 'roo-alert-title',
29913                     html : this.title
29914                 },
29915                 {
29916                     tag : 'span',
29917                     cls : 'roo-alert-text',
29918                     html : this.html
29919                 }
29920             ]
29921         };
29922         
29923         if(this.faicon){
29924             cfg.cn[0].cls += ' fa ' + this.faicon;
29925         }
29926         if(this.fa){
29927             cfg.cn[0].cls += ' fa ' + this.fa;
29928         }
29929         
29930         if(this.weight){
29931             cfg.cls += ' alert-' + this.weight;
29932         }
29933         
29934         return cfg;
29935     },
29936     
29937     initEvents: function() 
29938     {
29939         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29940         this.titleEl =  this.el.select('.roo-alert-title',true).first();
29941         this.iconEl = this.el.select('.roo-alert-icon',true).first();
29942         if (this.seconds > 0) {
29943             this.hide.defer(this.seconds, this);
29944         }
29945     },
29946     
29947     setTitle : function(str)
29948     {
29949         this.titleEl.dom.innerHTML = str;
29950     },
29951     
29952     setText : function(str)
29953     {
29954         this.titleEl.dom.innerHTML = str;
29955     },
29956     
29957     setWeight : function(weight)
29958     {
29959         if(this.weight){
29960             this.el.removeClass('alert-' + this.weight);
29961         }
29962         
29963         this.weight = weight;
29964         
29965         this.el.addClass('alert-' + this.weight);
29966     },
29967     
29968     setIcon : function(icon)
29969     {
29970         if(this.faicon){
29971             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29972         }
29973         
29974         this.faicon = icon;
29975         
29976         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
29977     },
29978     
29979     hide: function() 
29980     {
29981         this.el.hide();   
29982     },
29983     
29984     show: function() 
29985     {  
29986         this.el.show();   
29987     }
29988     
29989 });
29990
29991  
29992 /*
29993 * Licence: LGPL
29994 */
29995
29996 /**
29997  * @class Roo.bootstrap.UploadCropbox
29998  * @extends Roo.bootstrap.Component
29999  * Bootstrap UploadCropbox class
30000  * @cfg {String} emptyText show when image has been loaded
30001  * @cfg {String} rotateNotify show when image too small to rotate
30002  * @cfg {Number} errorTimeout default 3000
30003  * @cfg {Number} minWidth default 300
30004  * @cfg {Number} minHeight default 300
30005  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30006  * @cfg {Boolean} isDocument (true|false) default false
30007  * @cfg {String} url action url
30008  * @cfg {String} paramName default 'imageUpload'
30009  * @cfg {String} method default POST
30010  * @cfg {Boolean} loadMask (true|false) default true
30011  * @cfg {Boolean} loadingText default 'Loading...'
30012  * 
30013  * @constructor
30014  * Create a new UploadCropbox
30015  * @param {Object} config The config object
30016  */
30017
30018 Roo.bootstrap.UploadCropbox = function(config){
30019     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30020     
30021     this.addEvents({
30022         /**
30023          * @event beforeselectfile
30024          * Fire before select file
30025          * @param {Roo.bootstrap.UploadCropbox} this
30026          */
30027         "beforeselectfile" : true,
30028         /**
30029          * @event initial
30030          * Fire after initEvent
30031          * @param {Roo.bootstrap.UploadCropbox} this
30032          */
30033         "initial" : true,
30034         /**
30035          * @event crop
30036          * Fire after initEvent
30037          * @param {Roo.bootstrap.UploadCropbox} this
30038          * @param {String} data
30039          */
30040         "crop" : true,
30041         /**
30042          * @event prepare
30043          * Fire when preparing the file data
30044          * @param {Roo.bootstrap.UploadCropbox} this
30045          * @param {Object} file
30046          */
30047         "prepare" : true,
30048         /**
30049          * @event exception
30050          * Fire when get exception
30051          * @param {Roo.bootstrap.UploadCropbox} this
30052          * @param {XMLHttpRequest} xhr
30053          */
30054         "exception" : true,
30055         /**
30056          * @event beforeloadcanvas
30057          * Fire before load the canvas
30058          * @param {Roo.bootstrap.UploadCropbox} this
30059          * @param {String} src
30060          */
30061         "beforeloadcanvas" : true,
30062         /**
30063          * @event trash
30064          * Fire when trash image
30065          * @param {Roo.bootstrap.UploadCropbox} this
30066          */
30067         "trash" : true,
30068         /**
30069          * @event download
30070          * Fire when download the image
30071          * @param {Roo.bootstrap.UploadCropbox} this
30072          */
30073         "download" : true,
30074         /**
30075          * @event footerbuttonclick
30076          * Fire when footerbuttonclick
30077          * @param {Roo.bootstrap.UploadCropbox} this
30078          * @param {String} type
30079          */
30080         "footerbuttonclick" : true,
30081         /**
30082          * @event resize
30083          * Fire when resize
30084          * @param {Roo.bootstrap.UploadCropbox} this
30085          */
30086         "resize" : true,
30087         /**
30088          * @event rotate
30089          * Fire when rotate the image
30090          * @param {Roo.bootstrap.UploadCropbox} this
30091          * @param {String} pos
30092          */
30093         "rotate" : true,
30094         /**
30095          * @event inspect
30096          * Fire when inspect the file
30097          * @param {Roo.bootstrap.UploadCropbox} this
30098          * @param {Object} file
30099          */
30100         "inspect" : true,
30101         /**
30102          * @event upload
30103          * Fire when xhr upload the file
30104          * @param {Roo.bootstrap.UploadCropbox} this
30105          * @param {Object} data
30106          */
30107         "upload" : true,
30108         /**
30109          * @event arrange
30110          * Fire when arrange the file data
30111          * @param {Roo.bootstrap.UploadCropbox} this
30112          * @param {Object} formData
30113          */
30114         "arrange" : true
30115     });
30116     
30117     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30118 };
30119
30120 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30121     
30122     emptyText : 'Click to upload image',
30123     rotateNotify : 'Image is too small to rotate',
30124     errorTimeout : 3000,
30125     scale : 0,
30126     baseScale : 1,
30127     rotate : 0,
30128     dragable : false,
30129     pinching : false,
30130     mouseX : 0,
30131     mouseY : 0,
30132     cropData : false,
30133     minWidth : 300,
30134     minHeight : 300,
30135     file : false,
30136     exif : {},
30137     baseRotate : 1,
30138     cropType : 'image/jpeg',
30139     buttons : false,
30140     canvasLoaded : false,
30141     isDocument : false,
30142     method : 'POST',
30143     paramName : 'imageUpload',
30144     loadMask : true,
30145     loadingText : 'Loading...',
30146     maskEl : false,
30147     
30148     getAutoCreate : function()
30149     {
30150         var cfg = {
30151             tag : 'div',
30152             cls : 'roo-upload-cropbox',
30153             cn : [
30154                 {
30155                     tag : 'input',
30156                     cls : 'roo-upload-cropbox-selector',
30157                     type : 'file'
30158                 },
30159                 {
30160                     tag : 'div',
30161                     cls : 'roo-upload-cropbox-body',
30162                     style : 'cursor:pointer',
30163                     cn : [
30164                         {
30165                             tag : 'div',
30166                             cls : 'roo-upload-cropbox-preview'
30167                         },
30168                         {
30169                             tag : 'div',
30170                             cls : 'roo-upload-cropbox-thumb'
30171                         },
30172                         {
30173                             tag : 'div',
30174                             cls : 'roo-upload-cropbox-empty-notify',
30175                             html : this.emptyText
30176                         },
30177                         {
30178                             tag : 'div',
30179                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30180                             html : this.rotateNotify
30181                         }
30182                     ]
30183                 },
30184                 {
30185                     tag : 'div',
30186                     cls : 'roo-upload-cropbox-footer',
30187                     cn : {
30188                         tag : 'div',
30189                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30190                         cn : []
30191                     }
30192                 }
30193             ]
30194         };
30195         
30196         return cfg;
30197     },
30198     
30199     onRender : function(ct, position)
30200     {
30201         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30202         
30203         if (this.buttons.length) {
30204             
30205             Roo.each(this.buttons, function(bb) {
30206                 
30207                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30208                 
30209                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30210                 
30211             }, this);
30212         }
30213         
30214         if(this.loadMask){
30215             this.maskEl = this.el;
30216         }
30217     },
30218     
30219     initEvents : function()
30220     {
30221         this.urlAPI = (window.createObjectURL && window) || 
30222                                 (window.URL && URL.revokeObjectURL && URL) || 
30223                                 (window.webkitURL && webkitURL);
30224                         
30225         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30226         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30227         
30228         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30229         this.selectorEl.hide();
30230         
30231         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30232         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30233         
30234         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30235         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30236         this.thumbEl.hide();
30237         
30238         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30239         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30240         
30241         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30242         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30243         this.errorEl.hide();
30244         
30245         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30246         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30247         this.footerEl.hide();
30248         
30249         this.setThumbBoxSize();
30250         
30251         this.bind();
30252         
30253         this.resize();
30254         
30255         this.fireEvent('initial', this);
30256     },
30257
30258     bind : function()
30259     {
30260         var _this = this;
30261         
30262         window.addEventListener("resize", function() { _this.resize(); } );
30263         
30264         this.bodyEl.on('click', this.beforeSelectFile, this);
30265         
30266         if(Roo.isTouch){
30267             this.bodyEl.on('touchstart', this.onTouchStart, this);
30268             this.bodyEl.on('touchmove', this.onTouchMove, this);
30269             this.bodyEl.on('touchend', this.onTouchEnd, this);
30270         }
30271         
30272         if(!Roo.isTouch){
30273             this.bodyEl.on('mousedown', this.onMouseDown, this);
30274             this.bodyEl.on('mousemove', this.onMouseMove, this);
30275             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30276             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30277             Roo.get(document).on('mouseup', this.onMouseUp, this);
30278         }
30279         
30280         this.selectorEl.on('change', this.onFileSelected, this);
30281     },
30282     
30283     reset : function()
30284     {    
30285         this.scale = 0;
30286         this.baseScale = 1;
30287         this.rotate = 0;
30288         this.baseRotate = 1;
30289         this.dragable = false;
30290         this.pinching = false;
30291         this.mouseX = 0;
30292         this.mouseY = 0;
30293         this.cropData = false;
30294         this.notifyEl.dom.innerHTML = this.emptyText;
30295         
30296         this.selectorEl.dom.value = '';
30297         
30298     },
30299     
30300     resize : function()
30301     {
30302         if(this.fireEvent('resize', this) != false){
30303             this.setThumbBoxPosition();
30304             this.setCanvasPosition();
30305         }
30306     },
30307     
30308     onFooterButtonClick : function(e, el, o, type)
30309     {
30310         switch (type) {
30311             case 'rotate-left' :
30312                 this.onRotateLeft(e);
30313                 break;
30314             case 'rotate-right' :
30315                 this.onRotateRight(e);
30316                 break;
30317             case 'picture' :
30318                 this.beforeSelectFile(e);
30319                 break;
30320             case 'trash' :
30321                 this.trash(e);
30322                 break;
30323             case 'crop' :
30324                 this.crop(e);
30325                 break;
30326             case 'download' :
30327                 this.download(e);
30328                 break;
30329             default :
30330                 break;
30331         }
30332         
30333         this.fireEvent('footerbuttonclick', this, type);
30334     },
30335     
30336     beforeSelectFile : function(e)
30337     {
30338         e.preventDefault();
30339         
30340         if(this.fireEvent('beforeselectfile', this) != false){
30341             this.selectorEl.dom.click();
30342         }
30343     },
30344     
30345     onFileSelected : function(e)
30346     {
30347         e.preventDefault();
30348         
30349         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30350             return;
30351         }
30352         
30353         var file = this.selectorEl.dom.files[0];
30354         
30355         if(this.fireEvent('inspect', this, file) != false){
30356             this.prepare(file);
30357         }
30358         
30359     },
30360     
30361     trash : function(e)
30362     {
30363         this.fireEvent('trash', this);
30364     },
30365     
30366     download : function(e)
30367     {
30368         this.fireEvent('download', this);
30369     },
30370     
30371     loadCanvas : function(src)
30372     {   
30373         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30374             
30375             this.reset();
30376             
30377             this.imageEl = document.createElement('img');
30378             
30379             var _this = this;
30380             
30381             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30382             
30383             this.imageEl.src = src;
30384         }
30385     },
30386     
30387     onLoadCanvas : function()
30388     {   
30389         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30390         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30391         
30392         this.bodyEl.un('click', this.beforeSelectFile, this);
30393         
30394         this.notifyEl.hide();
30395         this.thumbEl.show();
30396         this.footerEl.show();
30397         
30398         this.baseRotateLevel();
30399         
30400         if(this.isDocument){
30401             this.setThumbBoxSize();
30402         }
30403         
30404         this.setThumbBoxPosition();
30405         
30406         this.baseScaleLevel();
30407         
30408         this.draw();
30409         
30410         this.resize();
30411         
30412         this.canvasLoaded = true;
30413         
30414         if(this.loadMask){
30415             this.maskEl.unmask();
30416         }
30417         
30418     },
30419     
30420     setCanvasPosition : function()
30421     {   
30422         if(!this.canvasEl){
30423             return;
30424         }
30425         
30426         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30427         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30428         
30429         this.previewEl.setLeft(pw);
30430         this.previewEl.setTop(ph);
30431         
30432     },
30433     
30434     onMouseDown : function(e)
30435     {   
30436         e.stopEvent();
30437         
30438         this.dragable = true;
30439         this.pinching = false;
30440         
30441         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30442             this.dragable = false;
30443             return;
30444         }
30445         
30446         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30447         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30448         
30449     },
30450     
30451     onMouseMove : function(e)
30452     {   
30453         e.stopEvent();
30454         
30455         if(!this.canvasLoaded){
30456             return;
30457         }
30458         
30459         if (!this.dragable){
30460             return;
30461         }
30462         
30463         var minX = Math.ceil(this.thumbEl.getLeft(true));
30464         var minY = Math.ceil(this.thumbEl.getTop(true));
30465         
30466         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30467         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30468         
30469         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30470         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30471         
30472         x = x - this.mouseX;
30473         y = y - this.mouseY;
30474         
30475         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30476         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30477         
30478         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30479         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30480         
30481         this.previewEl.setLeft(bgX);
30482         this.previewEl.setTop(bgY);
30483         
30484         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30485         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30486     },
30487     
30488     onMouseUp : function(e)
30489     {   
30490         e.stopEvent();
30491         
30492         this.dragable = false;
30493     },
30494     
30495     onMouseWheel : function(e)
30496     {   
30497         e.stopEvent();
30498         
30499         this.startScale = this.scale;
30500         
30501         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30502         
30503         if(!this.zoomable()){
30504             this.scale = this.startScale;
30505             return;
30506         }
30507         
30508         this.draw();
30509         
30510         return;
30511     },
30512     
30513     zoomable : function()
30514     {
30515         var minScale = this.thumbEl.getWidth() / this.minWidth;
30516         
30517         if(this.minWidth < this.minHeight){
30518             minScale = this.thumbEl.getHeight() / this.minHeight;
30519         }
30520         
30521         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30522         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30523         
30524         if(
30525                 this.isDocument &&
30526                 (this.rotate == 0 || this.rotate == 180) && 
30527                 (
30528                     width > this.imageEl.OriginWidth || 
30529                     height > this.imageEl.OriginHeight ||
30530                     (width < this.minWidth && height < this.minHeight)
30531                 )
30532         ){
30533             return false;
30534         }
30535         
30536         if(
30537                 this.isDocument &&
30538                 (this.rotate == 90 || this.rotate == 270) && 
30539                 (
30540                     width > this.imageEl.OriginWidth || 
30541                     height > this.imageEl.OriginHeight ||
30542                     (width < this.minHeight && height < this.minWidth)
30543                 )
30544         ){
30545             return false;
30546         }
30547         
30548         if(
30549                 !this.isDocument &&
30550                 (this.rotate == 0 || this.rotate == 180) && 
30551                 (
30552                     width < this.minWidth || 
30553                     width > this.imageEl.OriginWidth || 
30554                     height < this.minHeight || 
30555                     height > this.imageEl.OriginHeight
30556                 )
30557         ){
30558             return false;
30559         }
30560         
30561         if(
30562                 !this.isDocument &&
30563                 (this.rotate == 90 || this.rotate == 270) && 
30564                 (
30565                     width < this.minHeight || 
30566                     width > this.imageEl.OriginWidth || 
30567                     height < this.minWidth || 
30568                     height > this.imageEl.OriginHeight
30569                 )
30570         ){
30571             return false;
30572         }
30573         
30574         return true;
30575         
30576     },
30577     
30578     onRotateLeft : function(e)
30579     {   
30580         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30581             
30582             var minScale = this.thumbEl.getWidth() / this.minWidth;
30583             
30584             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30585             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30586             
30587             this.startScale = this.scale;
30588             
30589             while (this.getScaleLevel() < minScale){
30590             
30591                 this.scale = this.scale + 1;
30592                 
30593                 if(!this.zoomable()){
30594                     break;
30595                 }
30596                 
30597                 if(
30598                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30599                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30600                 ){
30601                     continue;
30602                 }
30603                 
30604                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30605
30606                 this.draw();
30607                 
30608                 return;
30609             }
30610             
30611             this.scale = this.startScale;
30612             
30613             this.onRotateFail();
30614             
30615             return false;
30616         }
30617         
30618         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30619
30620         if(this.isDocument){
30621             this.setThumbBoxSize();
30622             this.setThumbBoxPosition();
30623             this.setCanvasPosition();
30624         }
30625         
30626         this.draw();
30627         
30628         this.fireEvent('rotate', this, 'left');
30629         
30630     },
30631     
30632     onRotateRight : function(e)
30633     {
30634         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30635             
30636             var minScale = this.thumbEl.getWidth() / this.minWidth;
30637         
30638             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30639             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30640             
30641             this.startScale = this.scale;
30642             
30643             while (this.getScaleLevel() < minScale){
30644             
30645                 this.scale = this.scale + 1;
30646                 
30647                 if(!this.zoomable()){
30648                     break;
30649                 }
30650                 
30651                 if(
30652                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30653                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30654                 ){
30655                     continue;
30656                 }
30657                 
30658                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30659
30660                 this.draw();
30661                 
30662                 return;
30663             }
30664             
30665             this.scale = this.startScale;
30666             
30667             this.onRotateFail();
30668             
30669             return false;
30670         }
30671         
30672         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30673
30674         if(this.isDocument){
30675             this.setThumbBoxSize();
30676             this.setThumbBoxPosition();
30677             this.setCanvasPosition();
30678         }
30679         
30680         this.draw();
30681         
30682         this.fireEvent('rotate', this, 'right');
30683     },
30684     
30685     onRotateFail : function()
30686     {
30687         this.errorEl.show(true);
30688         
30689         var _this = this;
30690         
30691         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30692     },
30693     
30694     draw : function()
30695     {
30696         this.previewEl.dom.innerHTML = '';
30697         
30698         var canvasEl = document.createElement("canvas");
30699         
30700         var contextEl = canvasEl.getContext("2d");
30701         
30702         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30703         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30704         var center = this.imageEl.OriginWidth / 2;
30705         
30706         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30707             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30708             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30709             center = this.imageEl.OriginHeight / 2;
30710         }
30711         
30712         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30713         
30714         contextEl.translate(center, center);
30715         contextEl.rotate(this.rotate * Math.PI / 180);
30716
30717         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30718         
30719         this.canvasEl = document.createElement("canvas");
30720         
30721         this.contextEl = this.canvasEl.getContext("2d");
30722         
30723         switch (this.rotate) {
30724             case 0 :
30725                 
30726                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30727                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30728                 
30729                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30730                 
30731                 break;
30732             case 90 : 
30733                 
30734                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30735                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30736                 
30737                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30738                     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);
30739                     break;
30740                 }
30741                 
30742                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30743                 
30744                 break;
30745             case 180 :
30746                 
30747                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30748                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30749                 
30750                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30751                     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);
30752                     break;
30753                 }
30754                 
30755                 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);
30756                 
30757                 break;
30758             case 270 :
30759                 
30760                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30761                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30762         
30763                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30764                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30765                     break;
30766                 }
30767                 
30768                 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);
30769                 
30770                 break;
30771             default : 
30772                 break;
30773         }
30774         
30775         this.previewEl.appendChild(this.canvasEl);
30776         
30777         this.setCanvasPosition();
30778     },
30779     
30780     crop : function()
30781     {
30782         if(!this.canvasLoaded){
30783             return;
30784         }
30785         
30786         var imageCanvas = document.createElement("canvas");
30787         
30788         var imageContext = imageCanvas.getContext("2d");
30789         
30790         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30791         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30792         
30793         var center = imageCanvas.width / 2;
30794         
30795         imageContext.translate(center, center);
30796         
30797         imageContext.rotate(this.rotate * Math.PI / 180);
30798         
30799         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30800         
30801         var canvas = document.createElement("canvas");
30802         
30803         var context = canvas.getContext("2d");
30804                 
30805         canvas.width = this.minWidth;
30806         canvas.height = this.minHeight;
30807
30808         switch (this.rotate) {
30809             case 0 :
30810                 
30811                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30812                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30813                 
30814                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30815                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30816                 
30817                 var targetWidth = this.minWidth - 2 * x;
30818                 var targetHeight = this.minHeight - 2 * y;
30819                 
30820                 var scale = 1;
30821                 
30822                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30823                     scale = targetWidth / width;
30824                 }
30825                 
30826                 if(x > 0 && y == 0){
30827                     scale = targetHeight / height;
30828                 }
30829                 
30830                 if(x > 0 && y > 0){
30831                     scale = targetWidth / width;
30832                     
30833                     if(width < height){
30834                         scale = targetHeight / height;
30835                     }
30836                 }
30837                 
30838                 context.scale(scale, scale);
30839                 
30840                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30841                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30842
30843                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30844                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30845
30846                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30847                 
30848                 break;
30849             case 90 : 
30850                 
30851                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30852                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30853                 
30854                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30855                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30856                 
30857                 var targetWidth = this.minWidth - 2 * x;
30858                 var targetHeight = this.minHeight - 2 * y;
30859                 
30860                 var scale = 1;
30861                 
30862                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30863                     scale = targetWidth / width;
30864                 }
30865                 
30866                 if(x > 0 && y == 0){
30867                     scale = targetHeight / height;
30868                 }
30869                 
30870                 if(x > 0 && y > 0){
30871                     scale = targetWidth / width;
30872                     
30873                     if(width < height){
30874                         scale = targetHeight / height;
30875                     }
30876                 }
30877                 
30878                 context.scale(scale, scale);
30879                 
30880                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30881                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30882
30883                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30884                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30885                 
30886                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30887                 
30888                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30889                 
30890                 break;
30891             case 180 :
30892                 
30893                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30894                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30895                 
30896                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30897                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30898                 
30899                 var targetWidth = this.minWidth - 2 * x;
30900                 var targetHeight = this.minHeight - 2 * y;
30901                 
30902                 var scale = 1;
30903                 
30904                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30905                     scale = targetWidth / width;
30906                 }
30907                 
30908                 if(x > 0 && y == 0){
30909                     scale = targetHeight / height;
30910                 }
30911                 
30912                 if(x > 0 && y > 0){
30913                     scale = targetWidth / width;
30914                     
30915                     if(width < height){
30916                         scale = targetHeight / height;
30917                     }
30918                 }
30919                 
30920                 context.scale(scale, scale);
30921                 
30922                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30923                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30924
30925                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30926                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30927
30928                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30929                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30930                 
30931                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30932                 
30933                 break;
30934             case 270 :
30935                 
30936                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30937                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30938                 
30939                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30940                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30941                 
30942                 var targetWidth = this.minWidth - 2 * x;
30943                 var targetHeight = this.minHeight - 2 * y;
30944                 
30945                 var scale = 1;
30946                 
30947                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30948                     scale = targetWidth / width;
30949                 }
30950                 
30951                 if(x > 0 && y == 0){
30952                     scale = targetHeight / height;
30953                 }
30954                 
30955                 if(x > 0 && y > 0){
30956                     scale = targetWidth / width;
30957                     
30958                     if(width < height){
30959                         scale = targetHeight / height;
30960                     }
30961                 }
30962                 
30963                 context.scale(scale, scale);
30964                 
30965                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30966                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30967
30968                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30969                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30970                 
30971                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30972                 
30973                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30974                 
30975                 break;
30976             default : 
30977                 break;
30978         }
30979         
30980         this.cropData = canvas.toDataURL(this.cropType);
30981         
30982         if(this.fireEvent('crop', this, this.cropData) !== false){
30983             this.process(this.file, this.cropData);
30984         }
30985         
30986         return;
30987         
30988     },
30989     
30990     setThumbBoxSize : function()
30991     {
30992         var width, height;
30993         
30994         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30995             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30996             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30997             
30998             this.minWidth = width;
30999             this.minHeight = height;
31000             
31001             if(this.rotate == 90 || this.rotate == 270){
31002                 this.minWidth = height;
31003                 this.minHeight = width;
31004             }
31005         }
31006         
31007         height = 300;
31008         width = Math.ceil(this.minWidth * height / this.minHeight);
31009         
31010         if(this.minWidth > this.minHeight){
31011             width = 300;
31012             height = Math.ceil(this.minHeight * width / this.minWidth);
31013         }
31014         
31015         this.thumbEl.setStyle({
31016             width : width + 'px',
31017             height : height + 'px'
31018         });
31019
31020         return;
31021             
31022     },
31023     
31024     setThumbBoxPosition : function()
31025     {
31026         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31027         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31028         
31029         this.thumbEl.setLeft(x);
31030         this.thumbEl.setTop(y);
31031         
31032     },
31033     
31034     baseRotateLevel : function()
31035     {
31036         this.baseRotate = 1;
31037         
31038         if(
31039                 typeof(this.exif) != 'undefined' &&
31040                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31041                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31042         ){
31043             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31044         }
31045         
31046         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31047         
31048     },
31049     
31050     baseScaleLevel : function()
31051     {
31052         var width, height;
31053         
31054         if(this.isDocument){
31055             
31056             if(this.baseRotate == 6 || this.baseRotate == 8){
31057             
31058                 height = this.thumbEl.getHeight();
31059                 this.baseScale = height / this.imageEl.OriginWidth;
31060
31061                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31062                     width = this.thumbEl.getWidth();
31063                     this.baseScale = width / this.imageEl.OriginHeight;
31064                 }
31065
31066                 return;
31067             }
31068
31069             height = this.thumbEl.getHeight();
31070             this.baseScale = height / this.imageEl.OriginHeight;
31071
31072             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31073                 width = this.thumbEl.getWidth();
31074                 this.baseScale = width / this.imageEl.OriginWidth;
31075             }
31076
31077             return;
31078         }
31079         
31080         if(this.baseRotate == 6 || this.baseRotate == 8){
31081             
31082             width = this.thumbEl.getHeight();
31083             this.baseScale = width / this.imageEl.OriginHeight;
31084             
31085             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31086                 height = this.thumbEl.getWidth();
31087                 this.baseScale = height / this.imageEl.OriginHeight;
31088             }
31089             
31090             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31091                 height = this.thumbEl.getWidth();
31092                 this.baseScale = height / this.imageEl.OriginHeight;
31093                 
31094                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31095                     width = this.thumbEl.getHeight();
31096                     this.baseScale = width / this.imageEl.OriginWidth;
31097                 }
31098             }
31099             
31100             return;
31101         }
31102         
31103         width = this.thumbEl.getWidth();
31104         this.baseScale = width / this.imageEl.OriginWidth;
31105         
31106         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31107             height = this.thumbEl.getHeight();
31108             this.baseScale = height / this.imageEl.OriginHeight;
31109         }
31110         
31111         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31112             
31113             height = this.thumbEl.getHeight();
31114             this.baseScale = height / this.imageEl.OriginHeight;
31115             
31116             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31117                 width = this.thumbEl.getWidth();
31118                 this.baseScale = width / this.imageEl.OriginWidth;
31119             }
31120             
31121         }
31122         
31123         return;
31124     },
31125     
31126     getScaleLevel : function()
31127     {
31128         return this.baseScale * Math.pow(1.1, this.scale);
31129     },
31130     
31131     onTouchStart : function(e)
31132     {
31133         if(!this.canvasLoaded){
31134             this.beforeSelectFile(e);
31135             return;
31136         }
31137         
31138         var touches = e.browserEvent.touches;
31139         
31140         if(!touches){
31141             return;
31142         }
31143         
31144         if(touches.length == 1){
31145             this.onMouseDown(e);
31146             return;
31147         }
31148         
31149         if(touches.length != 2){
31150             return;
31151         }
31152         
31153         var coords = [];
31154         
31155         for(var i = 0, finger; finger = touches[i]; i++){
31156             coords.push(finger.pageX, finger.pageY);
31157         }
31158         
31159         var x = Math.pow(coords[0] - coords[2], 2);
31160         var y = Math.pow(coords[1] - coords[3], 2);
31161         
31162         this.startDistance = Math.sqrt(x + y);
31163         
31164         this.startScale = this.scale;
31165         
31166         this.pinching = true;
31167         this.dragable = false;
31168         
31169     },
31170     
31171     onTouchMove : function(e)
31172     {
31173         if(!this.pinching && !this.dragable){
31174             return;
31175         }
31176         
31177         var touches = e.browserEvent.touches;
31178         
31179         if(!touches){
31180             return;
31181         }
31182         
31183         if(this.dragable){
31184             this.onMouseMove(e);
31185             return;
31186         }
31187         
31188         var coords = [];
31189         
31190         for(var i = 0, finger; finger = touches[i]; i++){
31191             coords.push(finger.pageX, finger.pageY);
31192         }
31193         
31194         var x = Math.pow(coords[0] - coords[2], 2);
31195         var y = Math.pow(coords[1] - coords[3], 2);
31196         
31197         this.endDistance = Math.sqrt(x + y);
31198         
31199         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31200         
31201         if(!this.zoomable()){
31202             this.scale = this.startScale;
31203             return;
31204         }
31205         
31206         this.draw();
31207         
31208     },
31209     
31210     onTouchEnd : function(e)
31211     {
31212         this.pinching = false;
31213         this.dragable = false;
31214         
31215     },
31216     
31217     process : function(file, crop)
31218     {
31219         if(this.loadMask){
31220             this.maskEl.mask(this.loadingText);
31221         }
31222         
31223         this.xhr = new XMLHttpRequest();
31224         
31225         file.xhr = this.xhr;
31226
31227         this.xhr.open(this.method, this.url, true);
31228         
31229         var headers = {
31230             "Accept": "application/json",
31231             "Cache-Control": "no-cache",
31232             "X-Requested-With": "XMLHttpRequest"
31233         };
31234         
31235         for (var headerName in headers) {
31236             var headerValue = headers[headerName];
31237             if (headerValue) {
31238                 this.xhr.setRequestHeader(headerName, headerValue);
31239             }
31240         }
31241         
31242         var _this = this;
31243         
31244         this.xhr.onload = function()
31245         {
31246             _this.xhrOnLoad(_this.xhr);
31247         }
31248         
31249         this.xhr.onerror = function()
31250         {
31251             _this.xhrOnError(_this.xhr);
31252         }
31253         
31254         var formData = new FormData();
31255
31256         formData.append('returnHTML', 'NO');
31257         
31258         if(crop){
31259             formData.append('crop', crop);
31260         }
31261         
31262         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31263             formData.append(this.paramName, file, file.name);
31264         }
31265         
31266         if(typeof(file.filename) != 'undefined'){
31267             formData.append('filename', file.filename);
31268         }
31269         
31270         if(typeof(file.mimetype) != 'undefined'){
31271             formData.append('mimetype', file.mimetype);
31272         }
31273         
31274         if(this.fireEvent('arrange', this, formData) != false){
31275             this.xhr.send(formData);
31276         };
31277     },
31278     
31279     xhrOnLoad : function(xhr)
31280     {
31281         if(this.loadMask){
31282             this.maskEl.unmask();
31283         }
31284         
31285         if (xhr.readyState !== 4) {
31286             this.fireEvent('exception', this, xhr);
31287             return;
31288         }
31289
31290         var response = Roo.decode(xhr.responseText);
31291         
31292         if(!response.success){
31293             this.fireEvent('exception', this, xhr);
31294             return;
31295         }
31296         
31297         var response = Roo.decode(xhr.responseText);
31298         
31299         this.fireEvent('upload', this, response);
31300         
31301     },
31302     
31303     xhrOnError : function()
31304     {
31305         if(this.loadMask){
31306             this.maskEl.unmask();
31307         }
31308         
31309         Roo.log('xhr on error');
31310         
31311         var response = Roo.decode(xhr.responseText);
31312           
31313         Roo.log(response);
31314         
31315     },
31316     
31317     prepare : function(file)
31318     {   
31319         if(this.loadMask){
31320             this.maskEl.mask(this.loadingText);
31321         }
31322         
31323         this.file = false;
31324         this.exif = {};
31325         
31326         if(typeof(file) === 'string'){
31327             this.loadCanvas(file);
31328             return;
31329         }
31330         
31331         if(!file || !this.urlAPI){
31332             return;
31333         }
31334         
31335         this.file = file;
31336         this.cropType = file.type;
31337         
31338         var _this = this;
31339         
31340         if(this.fireEvent('prepare', this, this.file) != false){
31341             
31342             var reader = new FileReader();
31343             
31344             reader.onload = function (e) {
31345                 if (e.target.error) {
31346                     Roo.log(e.target.error);
31347                     return;
31348                 }
31349                 
31350                 var buffer = e.target.result,
31351                     dataView = new DataView(buffer),
31352                     offset = 2,
31353                     maxOffset = dataView.byteLength - 4,
31354                     markerBytes,
31355                     markerLength;
31356                 
31357                 if (dataView.getUint16(0) === 0xffd8) {
31358                     while (offset < maxOffset) {
31359                         markerBytes = dataView.getUint16(offset);
31360                         
31361                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31362                             markerLength = dataView.getUint16(offset + 2) + 2;
31363                             if (offset + markerLength > dataView.byteLength) {
31364                                 Roo.log('Invalid meta data: Invalid segment size.');
31365                                 break;
31366                             }
31367                             
31368                             if(markerBytes == 0xffe1){
31369                                 _this.parseExifData(
31370                                     dataView,
31371                                     offset,
31372                                     markerLength
31373                                 );
31374                             }
31375                             
31376                             offset += markerLength;
31377                             
31378                             continue;
31379                         }
31380                         
31381                         break;
31382                     }
31383                     
31384                 }
31385                 
31386                 var url = _this.urlAPI.createObjectURL(_this.file);
31387                 
31388                 _this.loadCanvas(url);
31389                 
31390                 return;
31391             }
31392             
31393             reader.readAsArrayBuffer(this.file);
31394             
31395         }
31396         
31397     },
31398     
31399     parseExifData : function(dataView, offset, length)
31400     {
31401         var tiffOffset = offset + 10,
31402             littleEndian,
31403             dirOffset;
31404     
31405         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31406             // No Exif data, might be XMP data instead
31407             return;
31408         }
31409         
31410         // Check for the ASCII code for "Exif" (0x45786966):
31411         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31412             // No Exif data, might be XMP data instead
31413             return;
31414         }
31415         if (tiffOffset + 8 > dataView.byteLength) {
31416             Roo.log('Invalid Exif data: Invalid segment size.');
31417             return;
31418         }
31419         // Check for the two null bytes:
31420         if (dataView.getUint16(offset + 8) !== 0x0000) {
31421             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31422             return;
31423         }
31424         // Check the byte alignment:
31425         switch (dataView.getUint16(tiffOffset)) {
31426         case 0x4949:
31427             littleEndian = true;
31428             break;
31429         case 0x4D4D:
31430             littleEndian = false;
31431             break;
31432         default:
31433             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31434             return;
31435         }
31436         // Check for the TIFF tag marker (0x002A):
31437         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31438             Roo.log('Invalid Exif data: Missing TIFF marker.');
31439             return;
31440         }
31441         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31442         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31443         
31444         this.parseExifTags(
31445             dataView,
31446             tiffOffset,
31447             tiffOffset + dirOffset,
31448             littleEndian
31449         );
31450     },
31451     
31452     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31453     {
31454         var tagsNumber,
31455             dirEndOffset,
31456             i;
31457         if (dirOffset + 6 > dataView.byteLength) {
31458             Roo.log('Invalid Exif data: Invalid directory offset.');
31459             return;
31460         }
31461         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31462         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31463         if (dirEndOffset + 4 > dataView.byteLength) {
31464             Roo.log('Invalid Exif data: Invalid directory size.');
31465             return;
31466         }
31467         for (i = 0; i < tagsNumber; i += 1) {
31468             this.parseExifTag(
31469                 dataView,
31470                 tiffOffset,
31471                 dirOffset + 2 + 12 * i, // tag offset
31472                 littleEndian
31473             );
31474         }
31475         // Return the offset to the next directory:
31476         return dataView.getUint32(dirEndOffset, littleEndian);
31477     },
31478     
31479     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31480     {
31481         var tag = dataView.getUint16(offset, littleEndian);
31482         
31483         this.exif[tag] = this.getExifValue(
31484             dataView,
31485             tiffOffset,
31486             offset,
31487             dataView.getUint16(offset + 2, littleEndian), // tag type
31488             dataView.getUint32(offset + 4, littleEndian), // tag length
31489             littleEndian
31490         );
31491     },
31492     
31493     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31494     {
31495         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31496             tagSize,
31497             dataOffset,
31498             values,
31499             i,
31500             str,
31501             c;
31502     
31503         if (!tagType) {
31504             Roo.log('Invalid Exif data: Invalid tag type.');
31505             return;
31506         }
31507         
31508         tagSize = tagType.size * length;
31509         // Determine if the value is contained in the dataOffset bytes,
31510         // or if the value at the dataOffset is a pointer to the actual data:
31511         dataOffset = tagSize > 4 ?
31512                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31513         if (dataOffset + tagSize > dataView.byteLength) {
31514             Roo.log('Invalid Exif data: Invalid data offset.');
31515             return;
31516         }
31517         if (length === 1) {
31518             return tagType.getValue(dataView, dataOffset, littleEndian);
31519         }
31520         values = [];
31521         for (i = 0; i < length; i += 1) {
31522             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31523         }
31524         
31525         if (tagType.ascii) {
31526             str = '';
31527             // Concatenate the chars:
31528             for (i = 0; i < values.length; i += 1) {
31529                 c = values[i];
31530                 // Ignore the terminating NULL byte(s):
31531                 if (c === '\u0000') {
31532                     break;
31533                 }
31534                 str += c;
31535             }
31536             return str;
31537         }
31538         return values;
31539     }
31540     
31541 });
31542
31543 Roo.apply(Roo.bootstrap.UploadCropbox, {
31544     tags : {
31545         'Orientation': 0x0112
31546     },
31547     
31548     Orientation: {
31549             1: 0, //'top-left',
31550 //            2: 'top-right',
31551             3: 180, //'bottom-right',
31552 //            4: 'bottom-left',
31553 //            5: 'left-top',
31554             6: 90, //'right-top',
31555 //            7: 'right-bottom',
31556             8: 270 //'left-bottom'
31557     },
31558     
31559     exifTagTypes : {
31560         // byte, 8-bit unsigned int:
31561         1: {
31562             getValue: function (dataView, dataOffset) {
31563                 return dataView.getUint8(dataOffset);
31564             },
31565             size: 1
31566         },
31567         // ascii, 8-bit byte:
31568         2: {
31569             getValue: function (dataView, dataOffset) {
31570                 return String.fromCharCode(dataView.getUint8(dataOffset));
31571             },
31572             size: 1,
31573             ascii: true
31574         },
31575         // short, 16 bit int:
31576         3: {
31577             getValue: function (dataView, dataOffset, littleEndian) {
31578                 return dataView.getUint16(dataOffset, littleEndian);
31579             },
31580             size: 2
31581         },
31582         // long, 32 bit int:
31583         4: {
31584             getValue: function (dataView, dataOffset, littleEndian) {
31585                 return dataView.getUint32(dataOffset, littleEndian);
31586             },
31587             size: 4
31588         },
31589         // rational = two long values, first is numerator, second is denominator:
31590         5: {
31591             getValue: function (dataView, dataOffset, littleEndian) {
31592                 return dataView.getUint32(dataOffset, littleEndian) /
31593                     dataView.getUint32(dataOffset + 4, littleEndian);
31594             },
31595             size: 8
31596         },
31597         // slong, 32 bit signed int:
31598         9: {
31599             getValue: function (dataView, dataOffset, littleEndian) {
31600                 return dataView.getInt32(dataOffset, littleEndian);
31601             },
31602             size: 4
31603         },
31604         // srational, two slongs, first is numerator, second is denominator:
31605         10: {
31606             getValue: function (dataView, dataOffset, littleEndian) {
31607                 return dataView.getInt32(dataOffset, littleEndian) /
31608                     dataView.getInt32(dataOffset + 4, littleEndian);
31609             },
31610             size: 8
31611         }
31612     },
31613     
31614     footer : {
31615         STANDARD : [
31616             {
31617                 tag : 'div',
31618                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31619                 action : 'rotate-left',
31620                 cn : [
31621                     {
31622                         tag : 'button',
31623                         cls : 'btn btn-default',
31624                         html : '<i class="fa fa-undo"></i>'
31625                     }
31626                 ]
31627             },
31628             {
31629                 tag : 'div',
31630                 cls : 'btn-group roo-upload-cropbox-picture',
31631                 action : 'picture',
31632                 cn : [
31633                     {
31634                         tag : 'button',
31635                         cls : 'btn btn-default',
31636                         html : '<i class="fa fa-picture-o"></i>'
31637                     }
31638                 ]
31639             },
31640             {
31641                 tag : 'div',
31642                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31643                 action : 'rotate-right',
31644                 cn : [
31645                     {
31646                         tag : 'button',
31647                         cls : 'btn btn-default',
31648                         html : '<i class="fa fa-repeat"></i>'
31649                     }
31650                 ]
31651             }
31652         ],
31653         DOCUMENT : [
31654             {
31655                 tag : 'div',
31656                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31657                 action : 'rotate-left',
31658                 cn : [
31659                     {
31660                         tag : 'button',
31661                         cls : 'btn btn-default',
31662                         html : '<i class="fa fa-undo"></i>'
31663                     }
31664                 ]
31665             },
31666             {
31667                 tag : 'div',
31668                 cls : 'btn-group roo-upload-cropbox-download',
31669                 action : 'download',
31670                 cn : [
31671                     {
31672                         tag : 'button',
31673                         cls : 'btn btn-default',
31674                         html : '<i class="fa fa-download"></i>'
31675                     }
31676                 ]
31677             },
31678             {
31679                 tag : 'div',
31680                 cls : 'btn-group roo-upload-cropbox-crop',
31681                 action : 'crop',
31682                 cn : [
31683                     {
31684                         tag : 'button',
31685                         cls : 'btn btn-default',
31686                         html : '<i class="fa fa-crop"></i>'
31687                     }
31688                 ]
31689             },
31690             {
31691                 tag : 'div',
31692                 cls : 'btn-group roo-upload-cropbox-trash',
31693                 action : 'trash',
31694                 cn : [
31695                     {
31696                         tag : 'button',
31697                         cls : 'btn btn-default',
31698                         html : '<i class="fa fa-trash"></i>'
31699                     }
31700                 ]
31701             },
31702             {
31703                 tag : 'div',
31704                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31705                 action : 'rotate-right',
31706                 cn : [
31707                     {
31708                         tag : 'button',
31709                         cls : 'btn btn-default',
31710                         html : '<i class="fa fa-repeat"></i>'
31711                     }
31712                 ]
31713             }
31714         ],
31715         ROTATOR : [
31716             {
31717                 tag : 'div',
31718                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31719                 action : 'rotate-left',
31720                 cn : [
31721                     {
31722                         tag : 'button',
31723                         cls : 'btn btn-default',
31724                         html : '<i class="fa fa-undo"></i>'
31725                     }
31726                 ]
31727             },
31728             {
31729                 tag : 'div',
31730                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31731                 action : 'rotate-right',
31732                 cn : [
31733                     {
31734                         tag : 'button',
31735                         cls : 'btn btn-default',
31736                         html : '<i class="fa fa-repeat"></i>'
31737                     }
31738                 ]
31739             }
31740         ]
31741     }
31742 });
31743
31744 /*
31745 * Licence: LGPL
31746 */
31747
31748 /**
31749  * @class Roo.bootstrap.DocumentManager
31750  * @extends Roo.bootstrap.Component
31751  * Bootstrap DocumentManager class
31752  * @cfg {String} paramName default 'imageUpload'
31753  * @cfg {String} toolTipName default 'filename'
31754  * @cfg {String} method default POST
31755  * @cfg {String} url action url
31756  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31757  * @cfg {Boolean} multiple multiple upload default true
31758  * @cfg {Number} thumbSize default 300
31759  * @cfg {String} fieldLabel
31760  * @cfg {Number} labelWidth default 4
31761  * @cfg {String} labelAlign (left|top) default left
31762  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31763 * @cfg {Number} labellg set the width of label (1-12)
31764  * @cfg {Number} labelmd set the width of label (1-12)
31765  * @cfg {Number} labelsm set the width of label (1-12)
31766  * @cfg {Number} labelxs set the width of label (1-12)
31767  * 
31768  * @constructor
31769  * Create a new DocumentManager
31770  * @param {Object} config The config object
31771  */
31772
31773 Roo.bootstrap.DocumentManager = function(config){
31774     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31775     
31776     this.files = [];
31777     this.delegates = [];
31778     
31779     this.addEvents({
31780         /**
31781          * @event initial
31782          * Fire when initial the DocumentManager
31783          * @param {Roo.bootstrap.DocumentManager} this
31784          */
31785         "initial" : true,
31786         /**
31787          * @event inspect
31788          * inspect selected file
31789          * @param {Roo.bootstrap.DocumentManager} this
31790          * @param {File} file
31791          */
31792         "inspect" : true,
31793         /**
31794          * @event exception
31795          * Fire when xhr load exception
31796          * @param {Roo.bootstrap.DocumentManager} this
31797          * @param {XMLHttpRequest} xhr
31798          */
31799         "exception" : true,
31800         /**
31801          * @event afterupload
31802          * Fire when xhr load exception
31803          * @param {Roo.bootstrap.DocumentManager} this
31804          * @param {XMLHttpRequest} xhr
31805          */
31806         "afterupload" : true,
31807         /**
31808          * @event prepare
31809          * prepare the form data
31810          * @param {Roo.bootstrap.DocumentManager} this
31811          * @param {Object} formData
31812          */
31813         "prepare" : true,
31814         /**
31815          * @event remove
31816          * Fire when remove the file
31817          * @param {Roo.bootstrap.DocumentManager} this
31818          * @param {Object} file
31819          */
31820         "remove" : true,
31821         /**
31822          * @event refresh
31823          * Fire after refresh the file
31824          * @param {Roo.bootstrap.DocumentManager} this
31825          */
31826         "refresh" : true,
31827         /**
31828          * @event click
31829          * Fire after click the image
31830          * @param {Roo.bootstrap.DocumentManager} this
31831          * @param {Object} file
31832          */
31833         "click" : true,
31834         /**
31835          * @event edit
31836          * Fire when upload a image and editable set to true
31837          * @param {Roo.bootstrap.DocumentManager} this
31838          * @param {Object} file
31839          */
31840         "edit" : true,
31841         /**
31842          * @event beforeselectfile
31843          * Fire before select file
31844          * @param {Roo.bootstrap.DocumentManager} this
31845          */
31846         "beforeselectfile" : true,
31847         /**
31848          * @event process
31849          * Fire before process file
31850          * @param {Roo.bootstrap.DocumentManager} this
31851          * @param {Object} file
31852          */
31853         "process" : true,
31854         /**
31855          * @event previewrendered
31856          * Fire when preview rendered
31857          * @param {Roo.bootstrap.DocumentManager} this
31858          * @param {Object} file
31859          */
31860         "previewrendered" : true,
31861         /**
31862          */
31863         "previewResize" : true
31864         
31865     });
31866 };
31867
31868 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31869     
31870     boxes : 0,
31871     inputName : '',
31872     thumbSize : 300,
31873     multiple : true,
31874     files : false,
31875     method : 'POST',
31876     url : '',
31877     paramName : 'imageUpload',
31878     toolTipName : 'filename',
31879     fieldLabel : '',
31880     labelWidth : 4,
31881     labelAlign : 'left',
31882     editable : true,
31883     delegates : false,
31884     xhr : false, 
31885     
31886     labellg : 0,
31887     labelmd : 0,
31888     labelsm : 0,
31889     labelxs : 0,
31890     
31891     getAutoCreate : function()
31892     {   
31893         var managerWidget = {
31894             tag : 'div',
31895             cls : 'roo-document-manager',
31896             cn : [
31897                 {
31898                     tag : 'input',
31899                     cls : 'roo-document-manager-selector',
31900                     type : 'file'
31901                 },
31902                 {
31903                     tag : 'div',
31904                     cls : 'roo-document-manager-uploader',
31905                     cn : [
31906                         {
31907                             tag : 'div',
31908                             cls : 'roo-document-manager-upload-btn',
31909                             html : '<i class="fa fa-plus"></i>'
31910                         }
31911                     ]
31912                     
31913                 }
31914             ]
31915         };
31916         
31917         var content = [
31918             {
31919                 tag : 'div',
31920                 cls : 'column col-md-12',
31921                 cn : managerWidget
31922             }
31923         ];
31924         
31925         if(this.fieldLabel.length){
31926             
31927             content = [
31928                 {
31929                     tag : 'div',
31930                     cls : 'column col-md-12',
31931                     html : this.fieldLabel
31932                 },
31933                 {
31934                     tag : 'div',
31935                     cls : 'column col-md-12',
31936                     cn : managerWidget
31937                 }
31938             ];
31939
31940             if(this.labelAlign == 'left'){
31941                 content = [
31942                     {
31943                         tag : 'div',
31944                         cls : 'column',
31945                         html : this.fieldLabel
31946                     },
31947                     {
31948                         tag : 'div',
31949                         cls : 'column',
31950                         cn : managerWidget
31951                     }
31952                 ];
31953                 
31954                 if(this.labelWidth > 12){
31955                     content[0].style = "width: " + this.labelWidth + 'px';
31956                 }
31957
31958                 if(this.labelWidth < 13 && this.labelmd == 0){
31959                     this.labelmd = this.labelWidth;
31960                 }
31961
31962                 if(this.labellg > 0){
31963                     content[0].cls += ' col-lg-' + this.labellg;
31964                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31965                 }
31966
31967                 if(this.labelmd > 0){
31968                     content[0].cls += ' col-md-' + this.labelmd;
31969                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31970                 }
31971
31972                 if(this.labelsm > 0){
31973                     content[0].cls += ' col-sm-' + this.labelsm;
31974                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31975                 }
31976
31977                 if(this.labelxs > 0){
31978                     content[0].cls += ' col-xs-' + this.labelxs;
31979                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31980                 }
31981                 
31982             }
31983         }
31984         
31985         var cfg = {
31986             tag : 'div',
31987             cls : 'row clearfix',
31988             cn : content
31989         };
31990         
31991         return cfg;
31992         
31993     },
31994     
31995     initEvents : function()
31996     {
31997         this.managerEl = this.el.select('.roo-document-manager', true).first();
31998         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31999         
32000         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32001         this.selectorEl.hide();
32002         
32003         if(this.multiple){
32004             this.selectorEl.attr('multiple', 'multiple');
32005         }
32006         
32007         this.selectorEl.on('change', this.onFileSelected, this);
32008         
32009         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32010         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32011         
32012         this.uploader.on('click', this.onUploaderClick, this);
32013         
32014         this.renderProgressDialog();
32015         
32016         var _this = this;
32017         
32018         window.addEventListener("resize", function() { _this.refresh(); } );
32019         
32020         this.fireEvent('initial', this);
32021     },
32022     
32023     renderProgressDialog : function()
32024     {
32025         var _this = this;
32026         
32027         this.progressDialog = new Roo.bootstrap.Modal({
32028             cls : 'roo-document-manager-progress-dialog',
32029             allow_close : false,
32030             animate : false,
32031             title : '',
32032             buttons : [
32033                 {
32034                     name  :'cancel',
32035                     weight : 'danger',
32036                     html : 'Cancel'
32037                 }
32038             ], 
32039             listeners : { 
32040                 btnclick : function() {
32041                     _this.uploadCancel();
32042                     this.hide();
32043                 }
32044             }
32045         });
32046          
32047         this.progressDialog.render(Roo.get(document.body));
32048          
32049         this.progress = new Roo.bootstrap.Progress({
32050             cls : 'roo-document-manager-progress',
32051             active : true,
32052             striped : true
32053         });
32054         
32055         this.progress.render(this.progressDialog.getChildContainer());
32056         
32057         this.progressBar = new Roo.bootstrap.ProgressBar({
32058             cls : 'roo-document-manager-progress-bar',
32059             aria_valuenow : 0,
32060             aria_valuemin : 0,
32061             aria_valuemax : 12,
32062             panel : 'success'
32063         });
32064         
32065         this.progressBar.render(this.progress.getChildContainer());
32066     },
32067     
32068     onUploaderClick : function(e)
32069     {
32070         e.preventDefault();
32071      
32072         if(this.fireEvent('beforeselectfile', this) != false){
32073             this.selectorEl.dom.click();
32074         }
32075         
32076     },
32077     
32078     onFileSelected : function(e)
32079     {
32080         e.preventDefault();
32081         
32082         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32083             return;
32084         }
32085         
32086         Roo.each(this.selectorEl.dom.files, function(file){
32087             if(this.fireEvent('inspect', this, file) != false){
32088                 this.files.push(file);
32089             }
32090         }, this);
32091         
32092         this.queue();
32093         
32094     },
32095     
32096     queue : function()
32097     {
32098         this.selectorEl.dom.value = '';
32099         
32100         if(!this.files || !this.files.length){
32101             return;
32102         }
32103         
32104         if(this.boxes > 0 && this.files.length > this.boxes){
32105             this.files = this.files.slice(0, this.boxes);
32106         }
32107         
32108         this.uploader.show();
32109         
32110         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32111             this.uploader.hide();
32112         }
32113         
32114         var _this = this;
32115         
32116         var files = [];
32117         
32118         var docs = [];
32119         
32120         Roo.each(this.files, function(file){
32121             
32122             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32123                 var f = this.renderPreview(file);
32124                 files.push(f);
32125                 return;
32126             }
32127             
32128             if(file.type.indexOf('image') != -1){
32129                 this.delegates.push(
32130                     (function(){
32131                         _this.process(file);
32132                     }).createDelegate(this)
32133                 );
32134         
32135                 return;
32136             }
32137             
32138             docs.push(
32139                 (function(){
32140                     _this.process(file);
32141                 }).createDelegate(this)
32142             );
32143             
32144         }, this);
32145         
32146         this.files = files;
32147         
32148         this.delegates = this.delegates.concat(docs);
32149         
32150         if(!this.delegates.length){
32151             this.refresh();
32152             return;
32153         }
32154         
32155         this.progressBar.aria_valuemax = this.delegates.length;
32156         
32157         this.arrange();
32158         
32159         return;
32160     },
32161     
32162     arrange : function()
32163     {
32164         if(!this.delegates.length){
32165             this.progressDialog.hide();
32166             this.refresh();
32167             return;
32168         }
32169         
32170         var delegate = this.delegates.shift();
32171         
32172         this.progressDialog.show();
32173         
32174         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32175         
32176         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32177         
32178         delegate();
32179     },
32180     
32181     refresh : function()
32182     {
32183         this.uploader.show();
32184         
32185         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32186             this.uploader.hide();
32187         }
32188         
32189         Roo.isTouch ? this.closable(false) : this.closable(true);
32190         
32191         this.fireEvent('refresh', this);
32192     },
32193     
32194     onRemove : function(e, el, o)
32195     {
32196         e.preventDefault();
32197         
32198         this.fireEvent('remove', this, o);
32199         
32200     },
32201     
32202     remove : function(o)
32203     {
32204         var files = [];
32205         
32206         Roo.each(this.files, function(file){
32207             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32208                 files.push(file);
32209                 return;
32210             }
32211
32212             o.target.remove();
32213
32214         }, this);
32215         
32216         this.files = files;
32217         
32218         this.refresh();
32219     },
32220     
32221     clear : function()
32222     {
32223         Roo.each(this.files, function(file){
32224             if(!file.target){
32225                 return;
32226             }
32227             
32228             file.target.remove();
32229
32230         }, this);
32231         
32232         this.files = [];
32233         
32234         this.refresh();
32235     },
32236     
32237     onClick : function(e, el, o)
32238     {
32239         e.preventDefault();
32240         
32241         this.fireEvent('click', this, o);
32242         
32243     },
32244     
32245     closable : function(closable)
32246     {
32247         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32248             
32249             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32250             
32251             if(closable){
32252                 el.show();
32253                 return;
32254             }
32255             
32256             el.hide();
32257             
32258         }, this);
32259     },
32260     
32261     xhrOnLoad : function(xhr)
32262     {
32263         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32264             el.remove();
32265         }, this);
32266         
32267         if (xhr.readyState !== 4) {
32268             this.arrange();
32269             this.fireEvent('exception', this, xhr);
32270             return;
32271         }
32272
32273         var response = Roo.decode(xhr.responseText);
32274         
32275         if(!response.success){
32276             this.arrange();
32277             this.fireEvent('exception', this, xhr);
32278             return;
32279         }
32280         
32281         var file = this.renderPreview(response.data);
32282         
32283         this.files.push(file);
32284         
32285         this.arrange();
32286         
32287         this.fireEvent('afterupload', this, xhr);
32288         
32289     },
32290     
32291     xhrOnError : function(xhr)
32292     {
32293         Roo.log('xhr on error');
32294         
32295         var response = Roo.decode(xhr.responseText);
32296           
32297         Roo.log(response);
32298         
32299         this.arrange();
32300     },
32301     
32302     process : function(file)
32303     {
32304         if(this.fireEvent('process', this, file) !== false){
32305             if(this.editable && file.type.indexOf('image') != -1){
32306                 this.fireEvent('edit', this, file);
32307                 return;
32308             }
32309
32310             this.uploadStart(file, false);
32311
32312             return;
32313         }
32314         
32315     },
32316     
32317     uploadStart : function(file, crop)
32318     {
32319         this.xhr = new XMLHttpRequest();
32320         
32321         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32322             this.arrange();
32323             return;
32324         }
32325         
32326         file.xhr = this.xhr;
32327             
32328         this.managerEl.createChild({
32329             tag : 'div',
32330             cls : 'roo-document-manager-loading',
32331             cn : [
32332                 {
32333                     tag : 'div',
32334                     tooltip : file.name,
32335                     cls : 'roo-document-manager-thumb',
32336                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32337                 }
32338             ]
32339
32340         });
32341
32342         this.xhr.open(this.method, this.url, true);
32343         
32344         var headers = {
32345             "Accept": "application/json",
32346             "Cache-Control": "no-cache",
32347             "X-Requested-With": "XMLHttpRequest"
32348         };
32349         
32350         for (var headerName in headers) {
32351             var headerValue = headers[headerName];
32352             if (headerValue) {
32353                 this.xhr.setRequestHeader(headerName, headerValue);
32354             }
32355         }
32356         
32357         var _this = this;
32358         
32359         this.xhr.onload = function()
32360         {
32361             _this.xhrOnLoad(_this.xhr);
32362         }
32363         
32364         this.xhr.onerror = function()
32365         {
32366             _this.xhrOnError(_this.xhr);
32367         }
32368         
32369         var formData = new FormData();
32370
32371         formData.append('returnHTML', 'NO');
32372         
32373         if(crop){
32374             formData.append('crop', crop);
32375         }
32376         
32377         formData.append(this.paramName, file, file.name);
32378         
32379         var options = {
32380             file : file, 
32381             manually : false
32382         };
32383         
32384         if(this.fireEvent('prepare', this, formData, options) != false){
32385             
32386             if(options.manually){
32387                 return;
32388             }
32389             
32390             this.xhr.send(formData);
32391             return;
32392         };
32393         
32394         this.uploadCancel();
32395     },
32396     
32397     uploadCancel : function()
32398     {
32399         if (this.xhr) {
32400             this.xhr.abort();
32401         }
32402         
32403         this.delegates = [];
32404         
32405         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32406             el.remove();
32407         }, this);
32408         
32409         this.arrange();
32410     },
32411     
32412     renderPreview : function(file)
32413     {
32414         if(typeof(file.target) != 'undefined' && file.target){
32415             return file;
32416         }
32417         
32418         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32419         
32420         var previewEl = this.managerEl.createChild({
32421             tag : 'div',
32422             cls : 'roo-document-manager-preview',
32423             cn : [
32424                 {
32425                     tag : 'div',
32426                     tooltip : file[this.toolTipName],
32427                     cls : 'roo-document-manager-thumb',
32428                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32429                 },
32430                 {
32431                     tag : 'button',
32432                     cls : 'close',
32433                     html : '<i class="fa fa-times-circle"></i>'
32434                 }
32435             ]
32436         });
32437
32438         var close = previewEl.select('button.close', true).first();
32439
32440         close.on('click', this.onRemove, this, file);
32441
32442         file.target = previewEl;
32443
32444         var image = previewEl.select('img', true).first();
32445         
32446         var _this = this;
32447         
32448         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32449         
32450         image.on('click', this.onClick, this, file);
32451         
32452         this.fireEvent('previewrendered', this, file);
32453         
32454         return file;
32455         
32456     },
32457     
32458     onPreviewLoad : function(file, image)
32459     {
32460         if(typeof(file.target) == 'undefined' || !file.target){
32461             return;
32462         }
32463         
32464         var width = image.dom.naturalWidth || image.dom.width;
32465         var height = image.dom.naturalHeight || image.dom.height;
32466         
32467         if(!this.previewResize) {
32468             return;
32469         }
32470         
32471         if(width > height){
32472             file.target.addClass('wide');
32473             return;
32474         }
32475         
32476         file.target.addClass('tall');
32477         return;
32478         
32479     },
32480     
32481     uploadFromSource : function(file, crop)
32482     {
32483         this.xhr = new XMLHttpRequest();
32484         
32485         this.managerEl.createChild({
32486             tag : 'div',
32487             cls : 'roo-document-manager-loading',
32488             cn : [
32489                 {
32490                     tag : 'div',
32491                     tooltip : file.name,
32492                     cls : 'roo-document-manager-thumb',
32493                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32494                 }
32495             ]
32496
32497         });
32498
32499         this.xhr.open(this.method, this.url, true);
32500         
32501         var headers = {
32502             "Accept": "application/json",
32503             "Cache-Control": "no-cache",
32504             "X-Requested-With": "XMLHttpRequest"
32505         };
32506         
32507         for (var headerName in headers) {
32508             var headerValue = headers[headerName];
32509             if (headerValue) {
32510                 this.xhr.setRequestHeader(headerName, headerValue);
32511             }
32512         }
32513         
32514         var _this = this;
32515         
32516         this.xhr.onload = function()
32517         {
32518             _this.xhrOnLoad(_this.xhr);
32519         }
32520         
32521         this.xhr.onerror = function()
32522         {
32523             _this.xhrOnError(_this.xhr);
32524         }
32525         
32526         var formData = new FormData();
32527
32528         formData.append('returnHTML', 'NO');
32529         
32530         formData.append('crop', crop);
32531         
32532         if(typeof(file.filename) != 'undefined'){
32533             formData.append('filename', file.filename);
32534         }
32535         
32536         if(typeof(file.mimetype) != 'undefined'){
32537             formData.append('mimetype', file.mimetype);
32538         }
32539         
32540         Roo.log(formData);
32541         
32542         if(this.fireEvent('prepare', this, formData) != false){
32543             this.xhr.send(formData);
32544         };
32545     }
32546 });
32547
32548 /*
32549 * Licence: LGPL
32550 */
32551
32552 /**
32553  * @class Roo.bootstrap.DocumentViewer
32554  * @extends Roo.bootstrap.Component
32555  * Bootstrap DocumentViewer class
32556  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32557  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32558  * 
32559  * @constructor
32560  * Create a new DocumentViewer
32561  * @param {Object} config The config object
32562  */
32563
32564 Roo.bootstrap.DocumentViewer = function(config){
32565     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32566     
32567     this.addEvents({
32568         /**
32569          * @event initial
32570          * Fire after initEvent
32571          * @param {Roo.bootstrap.DocumentViewer} this
32572          */
32573         "initial" : true,
32574         /**
32575          * @event click
32576          * Fire after click
32577          * @param {Roo.bootstrap.DocumentViewer} this
32578          */
32579         "click" : true,
32580         /**
32581          * @event download
32582          * Fire after download button
32583          * @param {Roo.bootstrap.DocumentViewer} this
32584          */
32585         "download" : true,
32586         /**
32587          * @event trash
32588          * Fire after trash button
32589          * @param {Roo.bootstrap.DocumentViewer} this
32590          */
32591         "trash" : true
32592         
32593     });
32594 };
32595
32596 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32597     
32598     showDownload : true,
32599     
32600     showTrash : true,
32601     
32602     getAutoCreate : function()
32603     {
32604         var cfg = {
32605             tag : 'div',
32606             cls : 'roo-document-viewer',
32607             cn : [
32608                 {
32609                     tag : 'div',
32610                     cls : 'roo-document-viewer-body',
32611                     cn : [
32612                         {
32613                             tag : 'div',
32614                             cls : 'roo-document-viewer-thumb',
32615                             cn : [
32616                                 {
32617                                     tag : 'img',
32618                                     cls : 'roo-document-viewer-image'
32619                                 }
32620                             ]
32621                         }
32622                     ]
32623                 },
32624                 {
32625                     tag : 'div',
32626                     cls : 'roo-document-viewer-footer',
32627                     cn : {
32628                         tag : 'div',
32629                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32630                         cn : [
32631                             {
32632                                 tag : 'div',
32633                                 cls : 'btn-group roo-document-viewer-download',
32634                                 cn : [
32635                                     {
32636                                         tag : 'button',
32637                                         cls : 'btn btn-default',
32638                                         html : '<i class="fa fa-download"></i>'
32639                                     }
32640                                 ]
32641                             },
32642                             {
32643                                 tag : 'div',
32644                                 cls : 'btn-group roo-document-viewer-trash',
32645                                 cn : [
32646                                     {
32647                                         tag : 'button',
32648                                         cls : 'btn btn-default',
32649                                         html : '<i class="fa fa-trash"></i>'
32650                                     }
32651                                 ]
32652                             }
32653                         ]
32654                     }
32655                 }
32656             ]
32657         };
32658         
32659         return cfg;
32660     },
32661     
32662     initEvents : function()
32663     {
32664         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32665         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32666         
32667         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32668         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32669         
32670         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32671         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32672         
32673         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32674         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32675         
32676         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32677         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32678         
32679         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32680         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32681         
32682         this.bodyEl.on('click', this.onClick, this);
32683         this.downloadBtn.on('click', this.onDownload, this);
32684         this.trashBtn.on('click', this.onTrash, this);
32685         
32686         this.downloadBtn.hide();
32687         this.trashBtn.hide();
32688         
32689         if(this.showDownload){
32690             this.downloadBtn.show();
32691         }
32692         
32693         if(this.showTrash){
32694             this.trashBtn.show();
32695         }
32696         
32697         if(!this.showDownload && !this.showTrash) {
32698             this.footerEl.hide();
32699         }
32700         
32701     },
32702     
32703     initial : function()
32704     {
32705         this.fireEvent('initial', this);
32706         
32707     },
32708     
32709     onClick : function(e)
32710     {
32711         e.preventDefault();
32712         
32713         this.fireEvent('click', this);
32714     },
32715     
32716     onDownload : function(e)
32717     {
32718         e.preventDefault();
32719         
32720         this.fireEvent('download', this);
32721     },
32722     
32723     onTrash : function(e)
32724     {
32725         e.preventDefault();
32726         
32727         this.fireEvent('trash', this);
32728     }
32729     
32730 });
32731 /*
32732  * - LGPL
32733  *
32734  * nav progress bar
32735  * 
32736  */
32737
32738 /**
32739  * @class Roo.bootstrap.NavProgressBar
32740  * @extends Roo.bootstrap.Component
32741  * Bootstrap NavProgressBar class
32742  * 
32743  * @constructor
32744  * Create a new nav progress bar
32745  * @param {Object} config The config object
32746  */
32747
32748 Roo.bootstrap.NavProgressBar = function(config){
32749     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32750
32751     this.bullets = this.bullets || [];
32752    
32753 //    Roo.bootstrap.NavProgressBar.register(this);
32754      this.addEvents({
32755         /**
32756              * @event changed
32757              * Fires when the active item changes
32758              * @param {Roo.bootstrap.NavProgressBar} this
32759              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32760              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32761          */
32762         'changed': true
32763      });
32764     
32765 };
32766
32767 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32768     
32769     bullets : [],
32770     barItems : [],
32771     
32772     getAutoCreate : function()
32773     {
32774         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32775         
32776         cfg = {
32777             tag : 'div',
32778             cls : 'roo-navigation-bar-group',
32779             cn : [
32780                 {
32781                     tag : 'div',
32782                     cls : 'roo-navigation-top-bar'
32783                 },
32784                 {
32785                     tag : 'div',
32786                     cls : 'roo-navigation-bullets-bar',
32787                     cn : [
32788                         {
32789                             tag : 'ul',
32790                             cls : 'roo-navigation-bar'
32791                         }
32792                     ]
32793                 },
32794                 
32795                 {
32796                     tag : 'div',
32797                     cls : 'roo-navigation-bottom-bar'
32798                 }
32799             ]
32800             
32801         };
32802         
32803         return cfg;
32804         
32805     },
32806     
32807     initEvents: function() 
32808     {
32809         
32810     },
32811     
32812     onRender : function(ct, position) 
32813     {
32814         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32815         
32816         if(this.bullets.length){
32817             Roo.each(this.bullets, function(b){
32818                this.addItem(b);
32819             }, this);
32820         }
32821         
32822         this.format();
32823         
32824     },
32825     
32826     addItem : function(cfg)
32827     {
32828         var item = new Roo.bootstrap.NavProgressItem(cfg);
32829         
32830         item.parentId = this.id;
32831         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32832         
32833         if(cfg.html){
32834             var top = new Roo.bootstrap.Element({
32835                 tag : 'div',
32836                 cls : 'roo-navigation-bar-text'
32837             });
32838             
32839             var bottom = new Roo.bootstrap.Element({
32840                 tag : 'div',
32841                 cls : 'roo-navigation-bar-text'
32842             });
32843             
32844             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32845             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32846             
32847             var topText = new Roo.bootstrap.Element({
32848                 tag : 'span',
32849                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32850             });
32851             
32852             var bottomText = new Roo.bootstrap.Element({
32853                 tag : 'span',
32854                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32855             });
32856             
32857             topText.onRender(top.el, null);
32858             bottomText.onRender(bottom.el, null);
32859             
32860             item.topEl = top;
32861             item.bottomEl = bottom;
32862         }
32863         
32864         this.barItems.push(item);
32865         
32866         return item;
32867     },
32868     
32869     getActive : function()
32870     {
32871         var active = false;
32872         
32873         Roo.each(this.barItems, function(v){
32874             
32875             if (!v.isActive()) {
32876                 return;
32877             }
32878             
32879             active = v;
32880             return false;
32881             
32882         });
32883         
32884         return active;
32885     },
32886     
32887     setActiveItem : function(item)
32888     {
32889         var prev = false;
32890         
32891         Roo.each(this.barItems, function(v){
32892             if (v.rid == item.rid) {
32893                 return ;
32894             }
32895             
32896             if (v.isActive()) {
32897                 v.setActive(false);
32898                 prev = v;
32899             }
32900         });
32901
32902         item.setActive(true);
32903         
32904         this.fireEvent('changed', this, item, prev);
32905     },
32906     
32907     getBarItem: function(rid)
32908     {
32909         var ret = false;
32910         
32911         Roo.each(this.barItems, function(e) {
32912             if (e.rid != rid) {
32913                 return;
32914             }
32915             
32916             ret =  e;
32917             return false;
32918         });
32919         
32920         return ret;
32921     },
32922     
32923     indexOfItem : function(item)
32924     {
32925         var index = false;
32926         
32927         Roo.each(this.barItems, function(v, i){
32928             
32929             if (v.rid != item.rid) {
32930                 return;
32931             }
32932             
32933             index = i;
32934             return false
32935         });
32936         
32937         return index;
32938     },
32939     
32940     setActiveNext : function()
32941     {
32942         var i = this.indexOfItem(this.getActive());
32943         
32944         if (i > this.barItems.length) {
32945             return;
32946         }
32947         
32948         this.setActiveItem(this.barItems[i+1]);
32949     },
32950     
32951     setActivePrev : function()
32952     {
32953         var i = this.indexOfItem(this.getActive());
32954         
32955         if (i  < 1) {
32956             return;
32957         }
32958         
32959         this.setActiveItem(this.barItems[i-1]);
32960     },
32961     
32962     format : function()
32963     {
32964         if(!this.barItems.length){
32965             return;
32966         }
32967      
32968         var width = 100 / this.barItems.length;
32969         
32970         Roo.each(this.barItems, function(i){
32971             i.el.setStyle('width', width + '%');
32972             i.topEl.el.setStyle('width', width + '%');
32973             i.bottomEl.el.setStyle('width', width + '%');
32974         }, this);
32975         
32976     }
32977     
32978 });
32979 /*
32980  * - LGPL
32981  *
32982  * Nav Progress Item
32983  * 
32984  */
32985
32986 /**
32987  * @class Roo.bootstrap.NavProgressItem
32988  * @extends Roo.bootstrap.Component
32989  * Bootstrap NavProgressItem class
32990  * @cfg {String} rid the reference id
32991  * @cfg {Boolean} active (true|false) Is item active default false
32992  * @cfg {Boolean} disabled (true|false) Is item active default false
32993  * @cfg {String} html
32994  * @cfg {String} position (top|bottom) text position default bottom
32995  * @cfg {String} icon show icon instead of number
32996  * 
32997  * @constructor
32998  * Create a new NavProgressItem
32999  * @param {Object} config The config object
33000  */
33001 Roo.bootstrap.NavProgressItem = function(config){
33002     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33003     this.addEvents({
33004         // raw events
33005         /**
33006          * @event click
33007          * The raw click event for the entire grid.
33008          * @param {Roo.bootstrap.NavProgressItem} this
33009          * @param {Roo.EventObject} e
33010          */
33011         "click" : true
33012     });
33013    
33014 };
33015
33016 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33017     
33018     rid : '',
33019     active : false,
33020     disabled : false,
33021     html : '',
33022     position : 'bottom',
33023     icon : false,
33024     
33025     getAutoCreate : function()
33026     {
33027         var iconCls = 'roo-navigation-bar-item-icon';
33028         
33029         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33030         
33031         var cfg = {
33032             tag: 'li',
33033             cls: 'roo-navigation-bar-item',
33034             cn : [
33035                 {
33036                     tag : 'i',
33037                     cls : iconCls
33038                 }
33039             ]
33040         };
33041         
33042         if(this.active){
33043             cfg.cls += ' active';
33044         }
33045         if(this.disabled){
33046             cfg.cls += ' disabled';
33047         }
33048         
33049         return cfg;
33050     },
33051     
33052     disable : function()
33053     {
33054         this.setDisabled(true);
33055     },
33056     
33057     enable : function()
33058     {
33059         this.setDisabled(false);
33060     },
33061     
33062     initEvents: function() 
33063     {
33064         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33065         
33066         this.iconEl.on('click', this.onClick, this);
33067     },
33068     
33069     onClick : function(e)
33070     {
33071         e.preventDefault();
33072         
33073         if(this.disabled){
33074             return;
33075         }
33076         
33077         if(this.fireEvent('click', this, e) === false){
33078             return;
33079         };
33080         
33081         this.parent().setActiveItem(this);
33082     },
33083     
33084     isActive: function () 
33085     {
33086         return this.active;
33087     },
33088     
33089     setActive : function(state)
33090     {
33091         if(this.active == state){
33092             return;
33093         }
33094         
33095         this.active = state;
33096         
33097         if (state) {
33098             this.el.addClass('active');
33099             return;
33100         }
33101         
33102         this.el.removeClass('active');
33103         
33104         return;
33105     },
33106     
33107     setDisabled : function(state)
33108     {
33109         if(this.disabled == state){
33110             return;
33111         }
33112         
33113         this.disabled = state;
33114         
33115         if (state) {
33116             this.el.addClass('disabled');
33117             return;
33118         }
33119         
33120         this.el.removeClass('disabled');
33121     },
33122     
33123     tooltipEl : function()
33124     {
33125         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33126     }
33127 });
33128  
33129
33130  /*
33131  * - LGPL
33132  *
33133  * FieldLabel
33134  * 
33135  */
33136
33137 /**
33138  * @class Roo.bootstrap.FieldLabel
33139  * @extends Roo.bootstrap.Component
33140  * Bootstrap FieldLabel class
33141  * @cfg {String} html contents of the element
33142  * @cfg {String} tag tag of the element default label
33143  * @cfg {String} cls class of the element
33144  * @cfg {String} target label target 
33145  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33146  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33147  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33148  * @cfg {String} iconTooltip default "This field is required"
33149  * @cfg {String} indicatorpos (left|right) default left
33150  * 
33151  * @constructor
33152  * Create a new FieldLabel
33153  * @param {Object} config The config object
33154  */
33155
33156 Roo.bootstrap.FieldLabel = function(config){
33157     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33158     
33159     this.addEvents({
33160             /**
33161              * @event invalid
33162              * Fires after the field has been marked as invalid.
33163              * @param {Roo.form.FieldLabel} this
33164              * @param {String} msg The validation message
33165              */
33166             invalid : true,
33167             /**
33168              * @event valid
33169              * Fires after the field has been validated with no errors.
33170              * @param {Roo.form.FieldLabel} this
33171              */
33172             valid : true
33173         });
33174 };
33175
33176 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33177     
33178     tag: 'label',
33179     cls: '',
33180     html: '',
33181     target: '',
33182     allowBlank : true,
33183     invalidClass : 'has-warning',
33184     validClass : 'has-success',
33185     iconTooltip : 'This field is required',
33186     indicatorpos : 'left',
33187     
33188     getAutoCreate : function(){
33189         
33190         var cls = "";
33191         if (!this.allowBlank) {
33192             cls  = "visible";
33193         }
33194         
33195         var cfg = {
33196             tag : this.tag,
33197             cls : 'roo-bootstrap-field-label ' + this.cls,
33198             for : this.target,
33199             cn : [
33200                 {
33201                     tag : 'i',
33202                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33203                     tooltip : this.iconTooltip
33204                 },
33205                 {
33206                     tag : 'span',
33207                     html : this.html
33208                 }
33209             ] 
33210         };
33211         
33212         if(this.indicatorpos == 'right'){
33213             var cfg = {
33214                 tag : this.tag,
33215                 cls : 'roo-bootstrap-field-label ' + this.cls,
33216                 for : this.target,
33217                 cn : [
33218                     {
33219                         tag : 'span',
33220                         html : this.html
33221                     },
33222                     {
33223                         tag : 'i',
33224                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33225                         tooltip : this.iconTooltip
33226                     }
33227                 ] 
33228             };
33229         }
33230         
33231         return cfg;
33232     },
33233     
33234     initEvents: function() 
33235     {
33236         Roo.bootstrap.Element.superclass.initEvents.call(this);
33237         
33238         this.indicator = this.indicatorEl();
33239         
33240         if(this.indicator){
33241             this.indicator.removeClass('visible');
33242             this.indicator.addClass('invisible');
33243         }
33244         
33245         Roo.bootstrap.FieldLabel.register(this);
33246     },
33247     
33248     indicatorEl : function()
33249     {
33250         var indicator = this.el.select('i.roo-required-indicator',true).first();
33251         
33252         if(!indicator){
33253             return false;
33254         }
33255         
33256         return indicator;
33257         
33258     },
33259     
33260     /**
33261      * Mark this field as valid
33262      */
33263     markValid : function()
33264     {
33265         if(this.indicator){
33266             this.indicator.removeClass('visible');
33267             this.indicator.addClass('invisible');
33268         }
33269         if (Roo.bootstrap.version == 3) {
33270             this.el.removeClass(this.invalidClass);
33271             this.el.addClass(this.validClass);
33272         } else {
33273             this.el.removeClass('is-invalid');
33274             this.el.addClass('is-valid');
33275         }
33276         
33277         
33278         this.fireEvent('valid', this);
33279     },
33280     
33281     /**
33282      * Mark this field as invalid
33283      * @param {String} msg The validation message
33284      */
33285     markInvalid : function(msg)
33286     {
33287         if(this.indicator){
33288             this.indicator.removeClass('invisible');
33289             this.indicator.addClass('visible');
33290         }
33291           if (Roo.bootstrap.version == 3) {
33292             this.el.removeClass(this.validClass);
33293             this.el.addClass(this.invalidClass);
33294         } else {
33295             this.el.removeClass('is-valid');
33296             this.el.addClass('is-invalid');
33297         }
33298         
33299         
33300         this.fireEvent('invalid', this, msg);
33301     }
33302     
33303    
33304 });
33305
33306 Roo.apply(Roo.bootstrap.FieldLabel, {
33307     
33308     groups: {},
33309     
33310      /**
33311     * register a FieldLabel Group
33312     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33313     */
33314     register : function(label)
33315     {
33316         if(this.groups.hasOwnProperty(label.target)){
33317             return;
33318         }
33319      
33320         this.groups[label.target] = label;
33321         
33322     },
33323     /**
33324     * fetch a FieldLabel Group based on the target
33325     * @param {string} target
33326     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33327     */
33328     get: function(target) {
33329         if (typeof(this.groups[target]) == 'undefined') {
33330             return false;
33331         }
33332         
33333         return this.groups[target] ;
33334     }
33335 });
33336
33337  
33338
33339  /*
33340  * - LGPL
33341  *
33342  * page DateSplitField.
33343  * 
33344  */
33345
33346
33347 /**
33348  * @class Roo.bootstrap.DateSplitField
33349  * @extends Roo.bootstrap.Component
33350  * Bootstrap DateSplitField class
33351  * @cfg {string} fieldLabel - the label associated
33352  * @cfg {Number} labelWidth set the width of label (0-12)
33353  * @cfg {String} labelAlign (top|left)
33354  * @cfg {Boolean} dayAllowBlank (true|false) default false
33355  * @cfg {Boolean} monthAllowBlank (true|false) default false
33356  * @cfg {Boolean} yearAllowBlank (true|false) default false
33357  * @cfg {string} dayPlaceholder 
33358  * @cfg {string} monthPlaceholder
33359  * @cfg {string} yearPlaceholder
33360  * @cfg {string} dayFormat default 'd'
33361  * @cfg {string} monthFormat default 'm'
33362  * @cfg {string} yearFormat default 'Y'
33363  * @cfg {Number} labellg set the width of label (1-12)
33364  * @cfg {Number} labelmd set the width of label (1-12)
33365  * @cfg {Number} labelsm set the width of label (1-12)
33366  * @cfg {Number} labelxs set the width of label (1-12)
33367
33368  *     
33369  * @constructor
33370  * Create a new DateSplitField
33371  * @param {Object} config The config object
33372  */
33373
33374 Roo.bootstrap.DateSplitField = function(config){
33375     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33376     
33377     this.addEvents({
33378         // raw events
33379          /**
33380          * @event years
33381          * getting the data of years
33382          * @param {Roo.bootstrap.DateSplitField} this
33383          * @param {Object} years
33384          */
33385         "years" : true,
33386         /**
33387          * @event days
33388          * getting the data of days
33389          * @param {Roo.bootstrap.DateSplitField} this
33390          * @param {Object} days
33391          */
33392         "days" : true,
33393         /**
33394          * @event invalid
33395          * Fires after the field has been marked as invalid.
33396          * @param {Roo.form.Field} this
33397          * @param {String} msg The validation message
33398          */
33399         invalid : true,
33400        /**
33401          * @event valid
33402          * Fires after the field has been validated with no errors.
33403          * @param {Roo.form.Field} this
33404          */
33405         valid : true
33406     });
33407 };
33408
33409 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33410     
33411     fieldLabel : '',
33412     labelAlign : 'top',
33413     labelWidth : 3,
33414     dayAllowBlank : false,
33415     monthAllowBlank : false,
33416     yearAllowBlank : false,
33417     dayPlaceholder : '',
33418     monthPlaceholder : '',
33419     yearPlaceholder : '',
33420     dayFormat : 'd',
33421     monthFormat : 'm',
33422     yearFormat : 'Y',
33423     isFormField : true,
33424     labellg : 0,
33425     labelmd : 0,
33426     labelsm : 0,
33427     labelxs : 0,
33428     
33429     getAutoCreate : function()
33430     {
33431         var cfg = {
33432             tag : 'div',
33433             cls : 'row roo-date-split-field-group',
33434             cn : [
33435                 {
33436                     tag : 'input',
33437                     type : 'hidden',
33438                     cls : 'form-hidden-field roo-date-split-field-group-value',
33439                     name : this.name
33440                 }
33441             ]
33442         };
33443         
33444         var labelCls = 'col-md-12';
33445         var contentCls = 'col-md-4';
33446         
33447         if(this.fieldLabel){
33448             
33449             var label = {
33450                 tag : 'div',
33451                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33452                 cn : [
33453                     {
33454                         tag : 'label',
33455                         html : this.fieldLabel
33456                     }
33457                 ]
33458             };
33459             
33460             if(this.labelAlign == 'left'){
33461             
33462                 if(this.labelWidth > 12){
33463                     label.style = "width: " + this.labelWidth + 'px';
33464                 }
33465
33466                 if(this.labelWidth < 13 && this.labelmd == 0){
33467                     this.labelmd = this.labelWidth;
33468                 }
33469
33470                 if(this.labellg > 0){
33471                     labelCls = ' col-lg-' + this.labellg;
33472                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33473                 }
33474
33475                 if(this.labelmd > 0){
33476                     labelCls = ' col-md-' + this.labelmd;
33477                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33478                 }
33479
33480                 if(this.labelsm > 0){
33481                     labelCls = ' col-sm-' + this.labelsm;
33482                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33483                 }
33484
33485                 if(this.labelxs > 0){
33486                     labelCls = ' col-xs-' + this.labelxs;
33487                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33488                 }
33489             }
33490             
33491             label.cls += ' ' + labelCls;
33492             
33493             cfg.cn.push(label);
33494         }
33495         
33496         Roo.each(['day', 'month', 'year'], function(t){
33497             cfg.cn.push({
33498                 tag : 'div',
33499                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33500             });
33501         }, this);
33502         
33503         return cfg;
33504     },
33505     
33506     inputEl: function ()
33507     {
33508         return this.el.select('.roo-date-split-field-group-value', true).first();
33509     },
33510     
33511     onRender : function(ct, position) 
33512     {
33513         var _this = this;
33514         
33515         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33516         
33517         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33518         
33519         this.dayField = new Roo.bootstrap.ComboBox({
33520             allowBlank : this.dayAllowBlank,
33521             alwaysQuery : true,
33522             displayField : 'value',
33523             editable : false,
33524             fieldLabel : '',
33525             forceSelection : true,
33526             mode : 'local',
33527             placeholder : this.dayPlaceholder,
33528             selectOnFocus : true,
33529             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33530             triggerAction : 'all',
33531             typeAhead : true,
33532             valueField : 'value',
33533             store : new Roo.data.SimpleStore({
33534                 data : (function() {    
33535                     var days = [];
33536                     _this.fireEvent('days', _this, days);
33537                     return days;
33538                 })(),
33539                 fields : [ 'value' ]
33540             }),
33541             listeners : {
33542                 select : function (_self, record, index)
33543                 {
33544                     _this.setValue(_this.getValue());
33545                 }
33546             }
33547         });
33548
33549         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33550         
33551         this.monthField = new Roo.bootstrap.MonthField({
33552             after : '<i class=\"fa fa-calendar\"></i>',
33553             allowBlank : this.monthAllowBlank,
33554             placeholder : this.monthPlaceholder,
33555             readOnly : true,
33556             listeners : {
33557                 render : function (_self)
33558                 {
33559                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33560                         e.preventDefault();
33561                         _self.focus();
33562                     });
33563                 },
33564                 select : function (_self, oldvalue, newvalue)
33565                 {
33566                     _this.setValue(_this.getValue());
33567                 }
33568             }
33569         });
33570         
33571         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33572         
33573         this.yearField = new Roo.bootstrap.ComboBox({
33574             allowBlank : this.yearAllowBlank,
33575             alwaysQuery : true,
33576             displayField : 'value',
33577             editable : false,
33578             fieldLabel : '',
33579             forceSelection : true,
33580             mode : 'local',
33581             placeholder : this.yearPlaceholder,
33582             selectOnFocus : true,
33583             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33584             triggerAction : 'all',
33585             typeAhead : true,
33586             valueField : 'value',
33587             store : new Roo.data.SimpleStore({
33588                 data : (function() {
33589                     var years = [];
33590                     _this.fireEvent('years', _this, years);
33591                     return years;
33592                 })(),
33593                 fields : [ 'value' ]
33594             }),
33595             listeners : {
33596                 select : function (_self, record, index)
33597                 {
33598                     _this.setValue(_this.getValue());
33599                 }
33600             }
33601         });
33602
33603         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33604     },
33605     
33606     setValue : function(v, format)
33607     {
33608         this.inputEl.dom.value = v;
33609         
33610         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33611         
33612         var d = Date.parseDate(v, f);
33613         
33614         if(!d){
33615             this.validate();
33616             return;
33617         }
33618         
33619         this.setDay(d.format(this.dayFormat));
33620         this.setMonth(d.format(this.monthFormat));
33621         this.setYear(d.format(this.yearFormat));
33622         
33623         this.validate();
33624         
33625         return;
33626     },
33627     
33628     setDay : function(v)
33629     {
33630         this.dayField.setValue(v);
33631         this.inputEl.dom.value = this.getValue();
33632         this.validate();
33633         return;
33634     },
33635     
33636     setMonth : function(v)
33637     {
33638         this.monthField.setValue(v, true);
33639         this.inputEl.dom.value = this.getValue();
33640         this.validate();
33641         return;
33642     },
33643     
33644     setYear : function(v)
33645     {
33646         this.yearField.setValue(v);
33647         this.inputEl.dom.value = this.getValue();
33648         this.validate();
33649         return;
33650     },
33651     
33652     getDay : function()
33653     {
33654         return this.dayField.getValue();
33655     },
33656     
33657     getMonth : function()
33658     {
33659         return this.monthField.getValue();
33660     },
33661     
33662     getYear : function()
33663     {
33664         return this.yearField.getValue();
33665     },
33666     
33667     getValue : function()
33668     {
33669         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33670         
33671         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33672         
33673         return date;
33674     },
33675     
33676     reset : function()
33677     {
33678         this.setDay('');
33679         this.setMonth('');
33680         this.setYear('');
33681         this.inputEl.dom.value = '';
33682         this.validate();
33683         return;
33684     },
33685     
33686     validate : function()
33687     {
33688         var d = this.dayField.validate();
33689         var m = this.monthField.validate();
33690         var y = this.yearField.validate();
33691         
33692         var valid = true;
33693         
33694         if(
33695                 (!this.dayAllowBlank && !d) ||
33696                 (!this.monthAllowBlank && !m) ||
33697                 (!this.yearAllowBlank && !y)
33698         ){
33699             valid = false;
33700         }
33701         
33702         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33703             return valid;
33704         }
33705         
33706         if(valid){
33707             this.markValid();
33708             return valid;
33709         }
33710         
33711         this.markInvalid();
33712         
33713         return valid;
33714     },
33715     
33716     markValid : function()
33717     {
33718         
33719         var label = this.el.select('label', true).first();
33720         var icon = this.el.select('i.fa-star', true).first();
33721
33722         if(label && icon){
33723             icon.remove();
33724         }
33725         
33726         this.fireEvent('valid', this);
33727     },
33728     
33729      /**
33730      * Mark this field as invalid
33731      * @param {String} msg The validation message
33732      */
33733     markInvalid : function(msg)
33734     {
33735         
33736         var label = this.el.select('label', true).first();
33737         var icon = this.el.select('i.fa-star', true).first();
33738
33739         if(label && !icon){
33740             this.el.select('.roo-date-split-field-label', true).createChild({
33741                 tag : 'i',
33742                 cls : 'text-danger fa fa-lg fa-star',
33743                 tooltip : 'This field is required',
33744                 style : 'margin-right:5px;'
33745             }, label, true);
33746         }
33747         
33748         this.fireEvent('invalid', this, msg);
33749     },
33750     
33751     clearInvalid : function()
33752     {
33753         var label = this.el.select('label', true).first();
33754         var icon = this.el.select('i.fa-star', true).first();
33755
33756         if(label && icon){
33757             icon.remove();
33758         }
33759         
33760         this.fireEvent('valid', this);
33761     },
33762     
33763     getName: function()
33764     {
33765         return this.name;
33766     }
33767     
33768 });
33769
33770  /**
33771  *
33772  * This is based on 
33773  * http://masonry.desandro.com
33774  *
33775  * The idea is to render all the bricks based on vertical width...
33776  *
33777  * The original code extends 'outlayer' - we might need to use that....
33778  * 
33779  */
33780
33781
33782 /**
33783  * @class Roo.bootstrap.LayoutMasonry
33784  * @extends Roo.bootstrap.Component
33785  * Bootstrap Layout Masonry class
33786  * 
33787  * @constructor
33788  * Create a new Element
33789  * @param {Object} config The config object
33790  */
33791
33792 Roo.bootstrap.LayoutMasonry = function(config){
33793     
33794     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33795     
33796     this.bricks = [];
33797     
33798     Roo.bootstrap.LayoutMasonry.register(this);
33799     
33800     this.addEvents({
33801         // raw events
33802         /**
33803          * @event layout
33804          * Fire after layout the items
33805          * @param {Roo.bootstrap.LayoutMasonry} this
33806          * @param {Roo.EventObject} e
33807          */
33808         "layout" : true
33809     });
33810     
33811 };
33812
33813 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33814     
33815     /**
33816      * @cfg {Boolean} isLayoutInstant = no animation?
33817      */   
33818     isLayoutInstant : false, // needed?
33819    
33820     /**
33821      * @cfg {Number} boxWidth  width of the columns
33822      */   
33823     boxWidth : 450,
33824     
33825       /**
33826      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33827      */   
33828     boxHeight : 0,
33829     
33830     /**
33831      * @cfg {Number} padWidth padding below box..
33832      */   
33833     padWidth : 10, 
33834     
33835     /**
33836      * @cfg {Number} gutter gutter width..
33837      */   
33838     gutter : 10,
33839     
33840      /**
33841      * @cfg {Number} maxCols maximum number of columns
33842      */   
33843     
33844     maxCols: 0,
33845     
33846     /**
33847      * @cfg {Boolean} isAutoInitial defalut true
33848      */   
33849     isAutoInitial : true, 
33850     
33851     containerWidth: 0,
33852     
33853     /**
33854      * @cfg {Boolean} isHorizontal defalut false
33855      */   
33856     isHorizontal : false, 
33857
33858     currentSize : null,
33859     
33860     tag: 'div',
33861     
33862     cls: '',
33863     
33864     bricks: null, //CompositeElement
33865     
33866     cols : 1,
33867     
33868     _isLayoutInited : false,
33869     
33870 //    isAlternative : false, // only use for vertical layout...
33871     
33872     /**
33873      * @cfg {Number} alternativePadWidth padding below box..
33874      */   
33875     alternativePadWidth : 50,
33876     
33877     selectedBrick : [],
33878     
33879     getAutoCreate : function(){
33880         
33881         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33882         
33883         var cfg = {
33884             tag: this.tag,
33885             cls: 'blog-masonary-wrapper ' + this.cls,
33886             cn : {
33887                 cls : 'mas-boxes masonary'
33888             }
33889         };
33890         
33891         return cfg;
33892     },
33893     
33894     getChildContainer: function( )
33895     {
33896         if (this.boxesEl) {
33897             return this.boxesEl;
33898         }
33899         
33900         this.boxesEl = this.el.select('.mas-boxes').first();
33901         
33902         return this.boxesEl;
33903     },
33904     
33905     
33906     initEvents : function()
33907     {
33908         var _this = this;
33909         
33910         if(this.isAutoInitial){
33911             Roo.log('hook children rendered');
33912             this.on('childrenrendered', function() {
33913                 Roo.log('children rendered');
33914                 _this.initial();
33915             } ,this);
33916         }
33917     },
33918     
33919     initial : function()
33920     {
33921         this.selectedBrick = [];
33922         
33923         this.currentSize = this.el.getBox(true);
33924         
33925         Roo.EventManager.onWindowResize(this.resize, this); 
33926
33927         if(!this.isAutoInitial){
33928             this.layout();
33929             return;
33930         }
33931         
33932         this.layout();
33933         
33934         return;
33935         //this.layout.defer(500,this);
33936         
33937     },
33938     
33939     resize : function()
33940     {
33941         var cs = this.el.getBox(true);
33942         
33943         if (
33944                 this.currentSize.width == cs.width && 
33945                 this.currentSize.x == cs.x && 
33946                 this.currentSize.height == cs.height && 
33947                 this.currentSize.y == cs.y 
33948         ) {
33949             Roo.log("no change in with or X or Y");
33950             return;
33951         }
33952         
33953         this.currentSize = cs;
33954         
33955         this.layout();
33956         
33957     },
33958     
33959     layout : function()
33960     {   
33961         this._resetLayout();
33962         
33963         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33964         
33965         this.layoutItems( isInstant );
33966       
33967         this._isLayoutInited = true;
33968         
33969         this.fireEvent('layout', this);
33970         
33971     },
33972     
33973     _resetLayout : function()
33974     {
33975         if(this.isHorizontal){
33976             this.horizontalMeasureColumns();
33977             return;
33978         }
33979         
33980         this.verticalMeasureColumns();
33981         
33982     },
33983     
33984     verticalMeasureColumns : function()
33985     {
33986         this.getContainerWidth();
33987         
33988 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33989 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33990 //            return;
33991 //        }
33992         
33993         var boxWidth = this.boxWidth + this.padWidth;
33994         
33995         if(this.containerWidth < this.boxWidth){
33996             boxWidth = this.containerWidth
33997         }
33998         
33999         var containerWidth = this.containerWidth;
34000         
34001         var cols = Math.floor(containerWidth / boxWidth);
34002         
34003         this.cols = Math.max( cols, 1 );
34004         
34005         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34006         
34007         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34008         
34009         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34010         
34011         this.colWidth = boxWidth + avail - this.padWidth;
34012         
34013         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34014         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34015     },
34016     
34017     horizontalMeasureColumns : function()
34018     {
34019         this.getContainerWidth();
34020         
34021         var boxWidth = this.boxWidth;
34022         
34023         if(this.containerWidth < boxWidth){
34024             boxWidth = this.containerWidth;
34025         }
34026         
34027         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34028         
34029         this.el.setHeight(boxWidth);
34030         
34031     },
34032     
34033     getContainerWidth : function()
34034     {
34035         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34036     },
34037     
34038     layoutItems : function( isInstant )
34039     {
34040         Roo.log(this.bricks);
34041         
34042         var items = Roo.apply([], this.bricks);
34043         
34044         if(this.isHorizontal){
34045             this._horizontalLayoutItems( items , isInstant );
34046             return;
34047         }
34048         
34049 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34050 //            this._verticalAlternativeLayoutItems( items , isInstant );
34051 //            return;
34052 //        }
34053         
34054         this._verticalLayoutItems( items , isInstant );
34055         
34056     },
34057     
34058     _verticalLayoutItems : function ( items , isInstant)
34059     {
34060         if ( !items || !items.length ) {
34061             return;
34062         }
34063         
34064         var standard = [
34065             ['xs', 'xs', 'xs', 'tall'],
34066             ['xs', 'xs', 'tall'],
34067             ['xs', 'xs', 'sm'],
34068             ['xs', 'xs', 'xs'],
34069             ['xs', 'tall'],
34070             ['xs', 'sm'],
34071             ['xs', 'xs'],
34072             ['xs'],
34073             
34074             ['sm', 'xs', 'xs'],
34075             ['sm', 'xs'],
34076             ['sm'],
34077             
34078             ['tall', 'xs', 'xs', 'xs'],
34079             ['tall', 'xs', 'xs'],
34080             ['tall', 'xs'],
34081             ['tall']
34082             
34083         ];
34084         
34085         var queue = [];
34086         
34087         var boxes = [];
34088         
34089         var box = [];
34090         
34091         Roo.each(items, function(item, k){
34092             
34093             switch (item.size) {
34094                 // these layouts take up a full box,
34095                 case 'md' :
34096                 case 'md-left' :
34097                 case 'md-right' :
34098                 case 'wide' :
34099                     
34100                     if(box.length){
34101                         boxes.push(box);
34102                         box = [];
34103                     }
34104                     
34105                     boxes.push([item]);
34106                     
34107                     break;
34108                     
34109                 case 'xs' :
34110                 case 'sm' :
34111                 case 'tall' :
34112                     
34113                     box.push(item);
34114                     
34115                     break;
34116                 default :
34117                     break;
34118                     
34119             }
34120             
34121         }, this);
34122         
34123         if(box.length){
34124             boxes.push(box);
34125             box = [];
34126         }
34127         
34128         var filterPattern = function(box, length)
34129         {
34130             if(!box.length){
34131                 return;
34132             }
34133             
34134             var match = false;
34135             
34136             var pattern = box.slice(0, length);
34137             
34138             var format = [];
34139             
34140             Roo.each(pattern, function(i){
34141                 format.push(i.size);
34142             }, this);
34143             
34144             Roo.each(standard, function(s){
34145                 
34146                 if(String(s) != String(format)){
34147                     return;
34148                 }
34149                 
34150                 match = true;
34151                 return false;
34152                 
34153             }, this);
34154             
34155             if(!match && length == 1){
34156                 return;
34157             }
34158             
34159             if(!match){
34160                 filterPattern(box, length - 1);
34161                 return;
34162             }
34163                 
34164             queue.push(pattern);
34165
34166             box = box.slice(length, box.length);
34167
34168             filterPattern(box, 4);
34169
34170             return;
34171             
34172         }
34173         
34174         Roo.each(boxes, function(box, k){
34175             
34176             if(!box.length){
34177                 return;
34178             }
34179             
34180             if(box.length == 1){
34181                 queue.push(box);
34182                 return;
34183             }
34184             
34185             filterPattern(box, 4);
34186             
34187         }, this);
34188         
34189         this._processVerticalLayoutQueue( queue, isInstant );
34190         
34191     },
34192     
34193 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34194 //    {
34195 //        if ( !items || !items.length ) {
34196 //            return;
34197 //        }
34198 //
34199 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34200 //        
34201 //    },
34202     
34203     _horizontalLayoutItems : function ( items , isInstant)
34204     {
34205         if ( !items || !items.length || items.length < 3) {
34206             return;
34207         }
34208         
34209         items.reverse();
34210         
34211         var eItems = items.slice(0, 3);
34212         
34213         items = items.slice(3, items.length);
34214         
34215         var standard = [
34216             ['xs', 'xs', 'xs', 'wide'],
34217             ['xs', 'xs', 'wide'],
34218             ['xs', 'xs', 'sm'],
34219             ['xs', 'xs', 'xs'],
34220             ['xs', 'wide'],
34221             ['xs', 'sm'],
34222             ['xs', 'xs'],
34223             ['xs'],
34224             
34225             ['sm', 'xs', 'xs'],
34226             ['sm', 'xs'],
34227             ['sm'],
34228             
34229             ['wide', 'xs', 'xs', 'xs'],
34230             ['wide', 'xs', 'xs'],
34231             ['wide', 'xs'],
34232             ['wide'],
34233             
34234             ['wide-thin']
34235         ];
34236         
34237         var queue = [];
34238         
34239         var boxes = [];
34240         
34241         var box = [];
34242         
34243         Roo.each(items, function(item, k){
34244             
34245             switch (item.size) {
34246                 case 'md' :
34247                 case 'md-left' :
34248                 case 'md-right' :
34249                 case 'tall' :
34250                     
34251                     if(box.length){
34252                         boxes.push(box);
34253                         box = [];
34254                     }
34255                     
34256                     boxes.push([item]);
34257                     
34258                     break;
34259                     
34260                 case 'xs' :
34261                 case 'sm' :
34262                 case 'wide' :
34263                 case 'wide-thin' :
34264                     
34265                     box.push(item);
34266                     
34267                     break;
34268                 default :
34269                     break;
34270                     
34271             }
34272             
34273         }, this);
34274         
34275         if(box.length){
34276             boxes.push(box);
34277             box = [];
34278         }
34279         
34280         var filterPattern = function(box, length)
34281         {
34282             if(!box.length){
34283                 return;
34284             }
34285             
34286             var match = false;
34287             
34288             var pattern = box.slice(0, length);
34289             
34290             var format = [];
34291             
34292             Roo.each(pattern, function(i){
34293                 format.push(i.size);
34294             }, this);
34295             
34296             Roo.each(standard, function(s){
34297                 
34298                 if(String(s) != String(format)){
34299                     return;
34300                 }
34301                 
34302                 match = true;
34303                 return false;
34304                 
34305             }, this);
34306             
34307             if(!match && length == 1){
34308                 return;
34309             }
34310             
34311             if(!match){
34312                 filterPattern(box, length - 1);
34313                 return;
34314             }
34315                 
34316             queue.push(pattern);
34317
34318             box = box.slice(length, box.length);
34319
34320             filterPattern(box, 4);
34321
34322             return;
34323             
34324         }
34325         
34326         Roo.each(boxes, function(box, k){
34327             
34328             if(!box.length){
34329                 return;
34330             }
34331             
34332             if(box.length == 1){
34333                 queue.push(box);
34334                 return;
34335             }
34336             
34337             filterPattern(box, 4);
34338             
34339         }, this);
34340         
34341         
34342         var prune = [];
34343         
34344         var pos = this.el.getBox(true);
34345         
34346         var minX = pos.x;
34347         
34348         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34349         
34350         var hit_end = false;
34351         
34352         Roo.each(queue, function(box){
34353             
34354             if(hit_end){
34355                 
34356                 Roo.each(box, function(b){
34357                 
34358                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34359                     b.el.hide();
34360
34361                 }, this);
34362
34363                 return;
34364             }
34365             
34366             var mx = 0;
34367             
34368             Roo.each(box, function(b){
34369                 
34370                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34371                 b.el.show();
34372
34373                 mx = Math.max(mx, b.x);
34374                 
34375             }, this);
34376             
34377             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34378             
34379             if(maxX < minX){
34380                 
34381                 Roo.each(box, function(b){
34382                 
34383                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34384                     b.el.hide();
34385                     
34386                 }, this);
34387                 
34388                 hit_end = true;
34389                 
34390                 return;
34391             }
34392             
34393             prune.push(box);
34394             
34395         }, this);
34396         
34397         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34398     },
34399     
34400     /** Sets position of item in DOM
34401     * @param {Element} item
34402     * @param {Number} x - horizontal position
34403     * @param {Number} y - vertical position
34404     * @param {Boolean} isInstant - disables transitions
34405     */
34406     _processVerticalLayoutQueue : function( queue, isInstant )
34407     {
34408         var pos = this.el.getBox(true);
34409         var x = pos.x;
34410         var y = pos.y;
34411         var maxY = [];
34412         
34413         for (var i = 0; i < this.cols; i++){
34414             maxY[i] = pos.y;
34415         }
34416         
34417         Roo.each(queue, function(box, k){
34418             
34419             var col = k % this.cols;
34420             
34421             Roo.each(box, function(b,kk){
34422                 
34423                 b.el.position('absolute');
34424                 
34425                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34426                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34427                 
34428                 if(b.size == 'md-left' || b.size == 'md-right'){
34429                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34430                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34431                 }
34432                 
34433                 b.el.setWidth(width);
34434                 b.el.setHeight(height);
34435                 // iframe?
34436                 b.el.select('iframe',true).setSize(width,height);
34437                 
34438             }, this);
34439             
34440             for (var i = 0; i < this.cols; i++){
34441                 
34442                 if(maxY[i] < maxY[col]){
34443                     col = i;
34444                     continue;
34445                 }
34446                 
34447                 col = Math.min(col, i);
34448                 
34449             }
34450             
34451             x = pos.x + col * (this.colWidth + this.padWidth);
34452             
34453             y = maxY[col];
34454             
34455             var positions = [];
34456             
34457             switch (box.length){
34458                 case 1 :
34459                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34460                     break;
34461                 case 2 :
34462                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34463                     break;
34464                 case 3 :
34465                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34466                     break;
34467                 case 4 :
34468                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34469                     break;
34470                 default :
34471                     break;
34472             }
34473             
34474             Roo.each(box, function(b,kk){
34475                 
34476                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34477                 
34478                 var sz = b.el.getSize();
34479                 
34480                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34481                 
34482             }, this);
34483             
34484         }, this);
34485         
34486         var mY = 0;
34487         
34488         for (var i = 0; i < this.cols; i++){
34489             mY = Math.max(mY, maxY[i]);
34490         }
34491         
34492         this.el.setHeight(mY - pos.y);
34493         
34494     },
34495     
34496 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34497 //    {
34498 //        var pos = this.el.getBox(true);
34499 //        var x = pos.x;
34500 //        var y = pos.y;
34501 //        var maxX = pos.right;
34502 //        
34503 //        var maxHeight = 0;
34504 //        
34505 //        Roo.each(items, function(item, k){
34506 //            
34507 //            var c = k % 2;
34508 //            
34509 //            item.el.position('absolute');
34510 //                
34511 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34512 //
34513 //            item.el.setWidth(width);
34514 //
34515 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34516 //
34517 //            item.el.setHeight(height);
34518 //            
34519 //            if(c == 0){
34520 //                item.el.setXY([x, y], isInstant ? false : true);
34521 //            } else {
34522 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34523 //            }
34524 //            
34525 //            y = y + height + this.alternativePadWidth;
34526 //            
34527 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34528 //            
34529 //        }, this);
34530 //        
34531 //        this.el.setHeight(maxHeight);
34532 //        
34533 //    },
34534     
34535     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34536     {
34537         var pos = this.el.getBox(true);
34538         
34539         var minX = pos.x;
34540         var minY = pos.y;
34541         
34542         var maxX = pos.right;
34543         
34544         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34545         
34546         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34547         
34548         Roo.each(queue, function(box, k){
34549             
34550             Roo.each(box, function(b, kk){
34551                 
34552                 b.el.position('absolute');
34553                 
34554                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34555                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34556                 
34557                 if(b.size == 'md-left' || b.size == 'md-right'){
34558                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34559                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34560                 }
34561                 
34562                 b.el.setWidth(width);
34563                 b.el.setHeight(height);
34564                 
34565             }, this);
34566             
34567             if(!box.length){
34568                 return;
34569             }
34570             
34571             var positions = [];
34572             
34573             switch (box.length){
34574                 case 1 :
34575                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34576                     break;
34577                 case 2 :
34578                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34579                     break;
34580                 case 3 :
34581                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34582                     break;
34583                 case 4 :
34584                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34585                     break;
34586                 default :
34587                     break;
34588             }
34589             
34590             Roo.each(box, function(b,kk){
34591                 
34592                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34593                 
34594                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34595                 
34596             }, this);
34597             
34598         }, this);
34599         
34600     },
34601     
34602     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34603     {
34604         Roo.each(eItems, function(b,k){
34605             
34606             b.size = (k == 0) ? 'sm' : 'xs';
34607             b.x = (k == 0) ? 2 : 1;
34608             b.y = (k == 0) ? 2 : 1;
34609             
34610             b.el.position('absolute');
34611             
34612             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34613                 
34614             b.el.setWidth(width);
34615             
34616             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34617             
34618             b.el.setHeight(height);
34619             
34620         }, this);
34621
34622         var positions = [];
34623         
34624         positions.push({
34625             x : maxX - this.unitWidth * 2 - this.gutter,
34626             y : minY
34627         });
34628         
34629         positions.push({
34630             x : maxX - this.unitWidth,
34631             y : minY + (this.unitWidth + this.gutter) * 2
34632         });
34633         
34634         positions.push({
34635             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34636             y : minY
34637         });
34638         
34639         Roo.each(eItems, function(b,k){
34640             
34641             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34642
34643         }, this);
34644         
34645     },
34646     
34647     getVerticalOneBoxColPositions : function(x, y, box)
34648     {
34649         var pos = [];
34650         
34651         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34652         
34653         if(box[0].size == 'md-left'){
34654             rand = 0;
34655         }
34656         
34657         if(box[0].size == 'md-right'){
34658             rand = 1;
34659         }
34660         
34661         pos.push({
34662             x : x + (this.unitWidth + this.gutter) * rand,
34663             y : y
34664         });
34665         
34666         return pos;
34667     },
34668     
34669     getVerticalTwoBoxColPositions : function(x, y, box)
34670     {
34671         var pos = [];
34672         
34673         if(box[0].size == 'xs'){
34674             
34675             pos.push({
34676                 x : x,
34677                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34678             });
34679
34680             pos.push({
34681                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34682                 y : y
34683             });
34684             
34685             return pos;
34686             
34687         }
34688         
34689         pos.push({
34690             x : x,
34691             y : y
34692         });
34693
34694         pos.push({
34695             x : x + (this.unitWidth + this.gutter) * 2,
34696             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34697         });
34698         
34699         return pos;
34700         
34701     },
34702     
34703     getVerticalThreeBoxColPositions : function(x, y, box)
34704     {
34705         var pos = [];
34706         
34707         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34708             
34709             pos.push({
34710                 x : x,
34711                 y : y
34712             });
34713
34714             pos.push({
34715                 x : x + (this.unitWidth + this.gutter) * 1,
34716                 y : y
34717             });
34718             
34719             pos.push({
34720                 x : x + (this.unitWidth + this.gutter) * 2,
34721                 y : y
34722             });
34723             
34724             return pos;
34725             
34726         }
34727         
34728         if(box[0].size == 'xs' && box[1].size == 'xs'){
34729             
34730             pos.push({
34731                 x : x,
34732                 y : y
34733             });
34734
34735             pos.push({
34736                 x : x,
34737                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34738             });
34739             
34740             pos.push({
34741                 x : x + (this.unitWidth + this.gutter) * 1,
34742                 y : y
34743             });
34744             
34745             return pos;
34746             
34747         }
34748         
34749         pos.push({
34750             x : x,
34751             y : y
34752         });
34753
34754         pos.push({
34755             x : x + (this.unitWidth + this.gutter) * 2,
34756             y : y
34757         });
34758
34759         pos.push({
34760             x : x + (this.unitWidth + this.gutter) * 2,
34761             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34762         });
34763             
34764         return pos;
34765         
34766     },
34767     
34768     getVerticalFourBoxColPositions : function(x, y, box)
34769     {
34770         var pos = [];
34771         
34772         if(box[0].size == 'xs'){
34773             
34774             pos.push({
34775                 x : x,
34776                 y : y
34777             });
34778
34779             pos.push({
34780                 x : x,
34781                 y : y + (this.unitHeight + this.gutter) * 1
34782             });
34783             
34784             pos.push({
34785                 x : x,
34786                 y : y + (this.unitHeight + this.gutter) * 2
34787             });
34788             
34789             pos.push({
34790                 x : x + (this.unitWidth + this.gutter) * 1,
34791                 y : y
34792             });
34793             
34794             return pos;
34795             
34796         }
34797         
34798         pos.push({
34799             x : x,
34800             y : y
34801         });
34802
34803         pos.push({
34804             x : x + (this.unitWidth + this.gutter) * 2,
34805             y : y
34806         });
34807
34808         pos.push({
34809             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34810             y : y + (this.unitHeight + this.gutter) * 1
34811         });
34812
34813         pos.push({
34814             x : x + (this.unitWidth + this.gutter) * 2,
34815             y : y + (this.unitWidth + this.gutter) * 2
34816         });
34817
34818         return pos;
34819         
34820     },
34821     
34822     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34823     {
34824         var pos = [];
34825         
34826         if(box[0].size == 'md-left'){
34827             pos.push({
34828                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34829                 y : minY
34830             });
34831             
34832             return pos;
34833         }
34834         
34835         if(box[0].size == 'md-right'){
34836             pos.push({
34837                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34838                 y : minY + (this.unitWidth + this.gutter) * 1
34839             });
34840             
34841             return pos;
34842         }
34843         
34844         var rand = Math.floor(Math.random() * (4 - box[0].y));
34845         
34846         pos.push({
34847             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34848             y : minY + (this.unitWidth + this.gutter) * rand
34849         });
34850         
34851         return pos;
34852         
34853     },
34854     
34855     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34856     {
34857         var pos = [];
34858         
34859         if(box[0].size == 'xs'){
34860             
34861             pos.push({
34862                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34863                 y : minY
34864             });
34865
34866             pos.push({
34867                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34868                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34869             });
34870             
34871             return pos;
34872             
34873         }
34874         
34875         pos.push({
34876             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34877             y : minY
34878         });
34879
34880         pos.push({
34881             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34882             y : minY + (this.unitWidth + this.gutter) * 2
34883         });
34884         
34885         return pos;
34886         
34887     },
34888     
34889     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34890     {
34891         var pos = [];
34892         
34893         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34894             
34895             pos.push({
34896                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34897                 y : minY
34898             });
34899
34900             pos.push({
34901                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34902                 y : minY + (this.unitWidth + this.gutter) * 1
34903             });
34904             
34905             pos.push({
34906                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34907                 y : minY + (this.unitWidth + this.gutter) * 2
34908             });
34909             
34910             return pos;
34911             
34912         }
34913         
34914         if(box[0].size == 'xs' && box[1].size == 'xs'){
34915             
34916             pos.push({
34917                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34918                 y : minY
34919             });
34920
34921             pos.push({
34922                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34923                 y : minY
34924             });
34925             
34926             pos.push({
34927                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34928                 y : minY + (this.unitWidth + this.gutter) * 1
34929             });
34930             
34931             return pos;
34932             
34933         }
34934         
34935         pos.push({
34936             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34937             y : minY
34938         });
34939
34940         pos.push({
34941             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34942             y : minY + (this.unitWidth + this.gutter) * 2
34943         });
34944
34945         pos.push({
34946             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34947             y : minY + (this.unitWidth + this.gutter) * 2
34948         });
34949             
34950         return pos;
34951         
34952     },
34953     
34954     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34955     {
34956         var pos = [];
34957         
34958         if(box[0].size == 'xs'){
34959             
34960             pos.push({
34961                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34962                 y : minY
34963             });
34964
34965             pos.push({
34966                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34967                 y : minY
34968             });
34969             
34970             pos.push({
34971                 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),
34972                 y : minY
34973             });
34974             
34975             pos.push({
34976                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34977                 y : minY + (this.unitWidth + this.gutter) * 1
34978             });
34979             
34980             return pos;
34981             
34982         }
34983         
34984         pos.push({
34985             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34986             y : minY
34987         });
34988         
34989         pos.push({
34990             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34991             y : minY + (this.unitWidth + this.gutter) * 2
34992         });
34993         
34994         pos.push({
34995             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34996             y : minY + (this.unitWidth + this.gutter) * 2
34997         });
34998         
34999         pos.push({
35000             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),
35001             y : minY + (this.unitWidth + this.gutter) * 2
35002         });
35003
35004         return pos;
35005         
35006     },
35007     
35008     /**
35009     * remove a Masonry Brick
35010     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35011     */
35012     removeBrick : function(brick_id)
35013     {
35014         if (!brick_id) {
35015             return;
35016         }
35017         
35018         for (var i = 0; i<this.bricks.length; i++) {
35019             if (this.bricks[i].id == brick_id) {
35020                 this.bricks.splice(i,1);
35021                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35022                 this.initial();
35023             }
35024         }
35025     },
35026     
35027     /**
35028     * adds a Masonry Brick
35029     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35030     */
35031     addBrick : function(cfg)
35032     {
35033         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35034         //this.register(cn);
35035         cn.parentId = this.id;
35036         cn.render(this.el);
35037         return cn;
35038     },
35039     
35040     /**
35041     * register a Masonry Brick
35042     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35043     */
35044     
35045     register : function(brick)
35046     {
35047         this.bricks.push(brick);
35048         brick.masonryId = this.id;
35049     },
35050     
35051     /**
35052     * clear all the Masonry Brick
35053     */
35054     clearAll : function()
35055     {
35056         this.bricks = [];
35057         //this.getChildContainer().dom.innerHTML = "";
35058         this.el.dom.innerHTML = '';
35059     },
35060     
35061     getSelected : function()
35062     {
35063         if (!this.selectedBrick) {
35064             return false;
35065         }
35066         
35067         return this.selectedBrick;
35068     }
35069 });
35070
35071 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35072     
35073     groups: {},
35074      /**
35075     * register a Masonry Layout
35076     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35077     */
35078     
35079     register : function(layout)
35080     {
35081         this.groups[layout.id] = layout;
35082     },
35083     /**
35084     * fetch a  Masonry Layout based on the masonry layout ID
35085     * @param {string} the masonry layout to add
35086     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35087     */
35088     
35089     get: function(layout_id) {
35090         if (typeof(this.groups[layout_id]) == 'undefined') {
35091             return false;
35092         }
35093         return this.groups[layout_id] ;
35094     }
35095     
35096     
35097     
35098 });
35099
35100  
35101
35102  /**
35103  *
35104  * This is based on 
35105  * http://masonry.desandro.com
35106  *
35107  * The idea is to render all the bricks based on vertical width...
35108  *
35109  * The original code extends 'outlayer' - we might need to use that....
35110  * 
35111  */
35112
35113
35114 /**
35115  * @class Roo.bootstrap.LayoutMasonryAuto
35116  * @extends Roo.bootstrap.Component
35117  * Bootstrap Layout Masonry class
35118  * 
35119  * @constructor
35120  * Create a new Element
35121  * @param {Object} config The config object
35122  */
35123
35124 Roo.bootstrap.LayoutMasonryAuto = function(config){
35125     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35126 };
35127
35128 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35129     
35130       /**
35131      * @cfg {Boolean} isFitWidth  - resize the width..
35132      */   
35133     isFitWidth : false,  // options..
35134     /**
35135      * @cfg {Boolean} isOriginLeft = left align?
35136      */   
35137     isOriginLeft : true,
35138     /**
35139      * @cfg {Boolean} isOriginTop = top align?
35140      */   
35141     isOriginTop : false,
35142     /**
35143      * @cfg {Boolean} isLayoutInstant = no animation?
35144      */   
35145     isLayoutInstant : false, // needed?
35146     /**
35147      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35148      */   
35149     isResizingContainer : true,
35150     /**
35151      * @cfg {Number} columnWidth  width of the columns 
35152      */   
35153     
35154     columnWidth : 0,
35155     
35156     /**
35157      * @cfg {Number} maxCols maximum number of columns
35158      */   
35159     
35160     maxCols: 0,
35161     /**
35162      * @cfg {Number} padHeight padding below box..
35163      */   
35164     
35165     padHeight : 10, 
35166     
35167     /**
35168      * @cfg {Boolean} isAutoInitial defalut true
35169      */   
35170     
35171     isAutoInitial : true, 
35172     
35173     // private?
35174     gutter : 0,
35175     
35176     containerWidth: 0,
35177     initialColumnWidth : 0,
35178     currentSize : null,
35179     
35180     colYs : null, // array.
35181     maxY : 0,
35182     padWidth: 10,
35183     
35184     
35185     tag: 'div',
35186     cls: '',
35187     bricks: null, //CompositeElement
35188     cols : 0, // array?
35189     // element : null, // wrapped now this.el
35190     _isLayoutInited : null, 
35191     
35192     
35193     getAutoCreate : function(){
35194         
35195         var cfg = {
35196             tag: this.tag,
35197             cls: 'blog-masonary-wrapper ' + this.cls,
35198             cn : {
35199                 cls : 'mas-boxes masonary'
35200             }
35201         };
35202         
35203         return cfg;
35204     },
35205     
35206     getChildContainer: function( )
35207     {
35208         if (this.boxesEl) {
35209             return this.boxesEl;
35210         }
35211         
35212         this.boxesEl = this.el.select('.mas-boxes').first();
35213         
35214         return this.boxesEl;
35215     },
35216     
35217     
35218     initEvents : function()
35219     {
35220         var _this = this;
35221         
35222         if(this.isAutoInitial){
35223             Roo.log('hook children rendered');
35224             this.on('childrenrendered', function() {
35225                 Roo.log('children rendered');
35226                 _this.initial();
35227             } ,this);
35228         }
35229         
35230     },
35231     
35232     initial : function()
35233     {
35234         this.reloadItems();
35235
35236         this.currentSize = this.el.getBox(true);
35237
35238         /// was window resize... - let's see if this works..
35239         Roo.EventManager.onWindowResize(this.resize, this); 
35240
35241         if(!this.isAutoInitial){
35242             this.layout();
35243             return;
35244         }
35245         
35246         this.layout.defer(500,this);
35247     },
35248     
35249     reloadItems: function()
35250     {
35251         this.bricks = this.el.select('.masonry-brick', true);
35252         
35253         this.bricks.each(function(b) {
35254             //Roo.log(b.getSize());
35255             if (!b.attr('originalwidth')) {
35256                 b.attr('originalwidth',  b.getSize().width);
35257             }
35258             
35259         });
35260         
35261         Roo.log(this.bricks.elements.length);
35262     },
35263     
35264     resize : function()
35265     {
35266         Roo.log('resize');
35267         var cs = this.el.getBox(true);
35268         
35269         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35270             Roo.log("no change in with or X");
35271             return;
35272         }
35273         this.currentSize = cs;
35274         this.layout();
35275     },
35276     
35277     layout : function()
35278     {
35279          Roo.log('layout');
35280         this._resetLayout();
35281         //this._manageStamps();
35282       
35283         // don't animate first layout
35284         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35285         this.layoutItems( isInstant );
35286       
35287         // flag for initalized
35288         this._isLayoutInited = true;
35289     },
35290     
35291     layoutItems : function( isInstant )
35292     {
35293         //var items = this._getItemsForLayout( this.items );
35294         // original code supports filtering layout items.. we just ignore it..
35295         
35296         this._layoutItems( this.bricks , isInstant );
35297       
35298         this._postLayout();
35299     },
35300     _layoutItems : function ( items , isInstant)
35301     {
35302        //this.fireEvent( 'layout', this, items );
35303     
35304
35305         if ( !items || !items.elements.length ) {
35306           // no items, emit event with empty array
35307             return;
35308         }
35309
35310         var queue = [];
35311         items.each(function(item) {
35312             Roo.log("layout item");
35313             Roo.log(item);
35314             // get x/y object from method
35315             var position = this._getItemLayoutPosition( item );
35316             // enqueue
35317             position.item = item;
35318             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35319             queue.push( position );
35320         }, this);
35321       
35322         this._processLayoutQueue( queue );
35323     },
35324     /** Sets position of item in DOM
35325     * @param {Element} item
35326     * @param {Number} x - horizontal position
35327     * @param {Number} y - vertical position
35328     * @param {Boolean} isInstant - disables transitions
35329     */
35330     _processLayoutQueue : function( queue )
35331     {
35332         for ( var i=0, len = queue.length; i < len; i++ ) {
35333             var obj = queue[i];
35334             obj.item.position('absolute');
35335             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35336         }
35337     },
35338       
35339     
35340     /**
35341     * Any logic you want to do after each layout,
35342     * i.e. size the container
35343     */
35344     _postLayout : function()
35345     {
35346         this.resizeContainer();
35347     },
35348     
35349     resizeContainer : function()
35350     {
35351         if ( !this.isResizingContainer ) {
35352             return;
35353         }
35354         var size = this._getContainerSize();
35355         if ( size ) {
35356             this.el.setSize(size.width,size.height);
35357             this.boxesEl.setSize(size.width,size.height);
35358         }
35359     },
35360     
35361     
35362     
35363     _resetLayout : function()
35364     {
35365         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35366         this.colWidth = this.el.getWidth();
35367         //this.gutter = this.el.getWidth(); 
35368         
35369         this.measureColumns();
35370
35371         // reset column Y
35372         var i = this.cols;
35373         this.colYs = [];
35374         while (i--) {
35375             this.colYs.push( 0 );
35376         }
35377     
35378         this.maxY = 0;
35379     },
35380
35381     measureColumns : function()
35382     {
35383         this.getContainerWidth();
35384       // if columnWidth is 0, default to outerWidth of first item
35385         if ( !this.columnWidth ) {
35386             var firstItem = this.bricks.first();
35387             Roo.log(firstItem);
35388             this.columnWidth  = this.containerWidth;
35389             if (firstItem && firstItem.attr('originalwidth') ) {
35390                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35391             }
35392             // columnWidth fall back to item of first element
35393             Roo.log("set column width?");
35394                         this.initialColumnWidth = this.columnWidth  ;
35395
35396             // if first elem has no width, default to size of container
35397             
35398         }
35399         
35400         
35401         if (this.initialColumnWidth) {
35402             this.columnWidth = this.initialColumnWidth;
35403         }
35404         
35405         
35406             
35407         // column width is fixed at the top - however if container width get's smaller we should
35408         // reduce it...
35409         
35410         // this bit calcs how man columns..
35411             
35412         var columnWidth = this.columnWidth += this.gutter;
35413       
35414         // calculate columns
35415         var containerWidth = this.containerWidth + this.gutter;
35416         
35417         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35418         // fix rounding errors, typically with gutters
35419         var excess = columnWidth - containerWidth % columnWidth;
35420         
35421         
35422         // if overshoot is less than a pixel, round up, otherwise floor it
35423         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35424         cols = Math[ mathMethod ]( cols );
35425         this.cols = Math.max( cols, 1 );
35426         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35427         
35428          // padding positioning..
35429         var totalColWidth = this.cols * this.columnWidth;
35430         var padavail = this.containerWidth - totalColWidth;
35431         // so for 2 columns - we need 3 'pads'
35432         
35433         var padNeeded = (1+this.cols) * this.padWidth;
35434         
35435         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35436         
35437         this.columnWidth += padExtra
35438         //this.padWidth = Math.floor(padavail /  ( this.cols));
35439         
35440         // adjust colum width so that padding is fixed??
35441         
35442         // we have 3 columns ... total = width * 3
35443         // we have X left over... that should be used by 
35444         
35445         //if (this.expandC) {
35446             
35447         //}
35448         
35449         
35450         
35451     },
35452     
35453     getContainerWidth : function()
35454     {
35455        /* // container is parent if fit width
35456         var container = this.isFitWidth ? this.element.parentNode : this.element;
35457         // check that this.size and size are there
35458         // IE8 triggers resize on body size change, so they might not be
35459         
35460         var size = getSize( container );  //FIXME
35461         this.containerWidth = size && size.innerWidth; //FIXME
35462         */
35463          
35464         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35465         
35466     },
35467     
35468     _getItemLayoutPosition : function( item )  // what is item?
35469     {
35470         // we resize the item to our columnWidth..
35471       
35472         item.setWidth(this.columnWidth);
35473         item.autoBoxAdjust  = false;
35474         
35475         var sz = item.getSize();
35476  
35477         // how many columns does this brick span
35478         var remainder = this.containerWidth % this.columnWidth;
35479         
35480         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35481         // round if off by 1 pixel, otherwise use ceil
35482         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35483         colSpan = Math.min( colSpan, this.cols );
35484         
35485         // normally this should be '1' as we dont' currently allow multi width columns..
35486         
35487         var colGroup = this._getColGroup( colSpan );
35488         // get the minimum Y value from the columns
35489         var minimumY = Math.min.apply( Math, colGroup );
35490         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35491         
35492         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35493          
35494         // position the brick
35495         var position = {
35496             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35497             y: this.currentSize.y + minimumY + this.padHeight
35498         };
35499         
35500         Roo.log(position);
35501         // apply setHeight to necessary columns
35502         var setHeight = minimumY + sz.height + this.padHeight;
35503         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35504         
35505         var setSpan = this.cols + 1 - colGroup.length;
35506         for ( var i = 0; i < setSpan; i++ ) {
35507           this.colYs[ shortColIndex + i ] = setHeight ;
35508         }
35509       
35510         return position;
35511     },
35512     
35513     /**
35514      * @param {Number} colSpan - number of columns the element spans
35515      * @returns {Array} colGroup
35516      */
35517     _getColGroup : function( colSpan )
35518     {
35519         if ( colSpan < 2 ) {
35520           // if brick spans only one column, use all the column Ys
35521           return this.colYs;
35522         }
35523       
35524         var colGroup = [];
35525         // how many different places could this brick fit horizontally
35526         var groupCount = this.cols + 1 - colSpan;
35527         // for each group potential horizontal position
35528         for ( var i = 0; i < groupCount; i++ ) {
35529           // make an array of colY values for that one group
35530           var groupColYs = this.colYs.slice( i, i + colSpan );
35531           // and get the max value of the array
35532           colGroup[i] = Math.max.apply( Math, groupColYs );
35533         }
35534         return colGroup;
35535     },
35536     /*
35537     _manageStamp : function( stamp )
35538     {
35539         var stampSize =  stamp.getSize();
35540         var offset = stamp.getBox();
35541         // get the columns that this stamp affects
35542         var firstX = this.isOriginLeft ? offset.x : offset.right;
35543         var lastX = firstX + stampSize.width;
35544         var firstCol = Math.floor( firstX / this.columnWidth );
35545         firstCol = Math.max( 0, firstCol );
35546         
35547         var lastCol = Math.floor( lastX / this.columnWidth );
35548         // lastCol should not go over if multiple of columnWidth #425
35549         lastCol -= lastX % this.columnWidth ? 0 : 1;
35550         lastCol = Math.min( this.cols - 1, lastCol );
35551         
35552         // set colYs to bottom of the stamp
35553         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35554             stampSize.height;
35555             
35556         for ( var i = firstCol; i <= lastCol; i++ ) {
35557           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35558         }
35559     },
35560     */
35561     
35562     _getContainerSize : function()
35563     {
35564         this.maxY = Math.max.apply( Math, this.colYs );
35565         var size = {
35566             height: this.maxY
35567         };
35568       
35569         if ( this.isFitWidth ) {
35570             size.width = this._getContainerFitWidth();
35571         }
35572       
35573         return size;
35574     },
35575     
35576     _getContainerFitWidth : function()
35577     {
35578         var unusedCols = 0;
35579         // count unused columns
35580         var i = this.cols;
35581         while ( --i ) {
35582           if ( this.colYs[i] !== 0 ) {
35583             break;
35584           }
35585           unusedCols++;
35586         }
35587         // fit container to columns that have been used
35588         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35589     },
35590     
35591     needsResizeLayout : function()
35592     {
35593         var previousWidth = this.containerWidth;
35594         this.getContainerWidth();
35595         return previousWidth !== this.containerWidth;
35596     }
35597  
35598 });
35599
35600  
35601
35602  /*
35603  * - LGPL
35604  *
35605  * element
35606  * 
35607  */
35608
35609 /**
35610  * @class Roo.bootstrap.MasonryBrick
35611  * @extends Roo.bootstrap.Component
35612  * Bootstrap MasonryBrick class
35613  * 
35614  * @constructor
35615  * Create a new MasonryBrick
35616  * @param {Object} config The config object
35617  */
35618
35619 Roo.bootstrap.MasonryBrick = function(config){
35620     
35621     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35622     
35623     Roo.bootstrap.MasonryBrick.register(this);
35624     
35625     this.addEvents({
35626         // raw events
35627         /**
35628          * @event click
35629          * When a MasonryBrick is clcik
35630          * @param {Roo.bootstrap.MasonryBrick} this
35631          * @param {Roo.EventObject} e
35632          */
35633         "click" : true
35634     });
35635 };
35636
35637 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35638     
35639     /**
35640      * @cfg {String} title
35641      */   
35642     title : '',
35643     /**
35644      * @cfg {String} html
35645      */   
35646     html : '',
35647     /**
35648      * @cfg {String} bgimage
35649      */   
35650     bgimage : '',
35651     /**
35652      * @cfg {String} videourl
35653      */   
35654     videourl : '',
35655     /**
35656      * @cfg {String} cls
35657      */   
35658     cls : '',
35659     /**
35660      * @cfg {String} href
35661      */   
35662     href : '',
35663     /**
35664      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35665      */   
35666     size : 'xs',
35667     
35668     /**
35669      * @cfg {String} placetitle (center|bottom)
35670      */   
35671     placetitle : '',
35672     
35673     /**
35674      * @cfg {Boolean} isFitContainer defalut true
35675      */   
35676     isFitContainer : true, 
35677     
35678     /**
35679      * @cfg {Boolean} preventDefault defalut false
35680      */   
35681     preventDefault : false, 
35682     
35683     /**
35684      * @cfg {Boolean} inverse defalut false
35685      */   
35686     maskInverse : false, 
35687     
35688     getAutoCreate : function()
35689     {
35690         if(!this.isFitContainer){
35691             return this.getSplitAutoCreate();
35692         }
35693         
35694         var cls = 'masonry-brick masonry-brick-full';
35695         
35696         if(this.href.length){
35697             cls += ' masonry-brick-link';
35698         }
35699         
35700         if(this.bgimage.length){
35701             cls += ' masonry-brick-image';
35702         }
35703         
35704         if(this.maskInverse){
35705             cls += ' mask-inverse';
35706         }
35707         
35708         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35709             cls += ' enable-mask';
35710         }
35711         
35712         if(this.size){
35713             cls += ' masonry-' + this.size + '-brick';
35714         }
35715         
35716         if(this.placetitle.length){
35717             
35718             switch (this.placetitle) {
35719                 case 'center' :
35720                     cls += ' masonry-center-title';
35721                     break;
35722                 case 'bottom' :
35723                     cls += ' masonry-bottom-title';
35724                     break;
35725                 default:
35726                     break;
35727             }
35728             
35729         } else {
35730             if(!this.html.length && !this.bgimage.length){
35731                 cls += ' masonry-center-title';
35732             }
35733
35734             if(!this.html.length && this.bgimage.length){
35735                 cls += ' masonry-bottom-title';
35736             }
35737         }
35738         
35739         if(this.cls){
35740             cls += ' ' + this.cls;
35741         }
35742         
35743         var cfg = {
35744             tag: (this.href.length) ? 'a' : 'div',
35745             cls: cls,
35746             cn: [
35747                 {
35748                     tag: 'div',
35749                     cls: 'masonry-brick-mask'
35750                 },
35751                 {
35752                     tag: 'div',
35753                     cls: 'masonry-brick-paragraph',
35754                     cn: []
35755                 }
35756             ]
35757         };
35758         
35759         if(this.href.length){
35760             cfg.href = this.href;
35761         }
35762         
35763         var cn = cfg.cn[1].cn;
35764         
35765         if(this.title.length){
35766             cn.push({
35767                 tag: 'h4',
35768                 cls: 'masonry-brick-title',
35769                 html: this.title
35770             });
35771         }
35772         
35773         if(this.html.length){
35774             cn.push({
35775                 tag: 'p',
35776                 cls: 'masonry-brick-text',
35777                 html: this.html
35778             });
35779         }
35780         
35781         if (!this.title.length && !this.html.length) {
35782             cfg.cn[1].cls += ' hide';
35783         }
35784         
35785         if(this.bgimage.length){
35786             cfg.cn.push({
35787                 tag: 'img',
35788                 cls: 'masonry-brick-image-view',
35789                 src: this.bgimage
35790             });
35791         }
35792         
35793         if(this.videourl.length){
35794             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35795             // youtube support only?
35796             cfg.cn.push({
35797                 tag: 'iframe',
35798                 cls: 'masonry-brick-image-view',
35799                 src: vurl,
35800                 frameborder : 0,
35801                 allowfullscreen : true
35802             });
35803         }
35804         
35805         return cfg;
35806         
35807     },
35808     
35809     getSplitAutoCreate : function()
35810     {
35811         var cls = 'masonry-brick masonry-brick-split';
35812         
35813         if(this.href.length){
35814             cls += ' masonry-brick-link';
35815         }
35816         
35817         if(this.bgimage.length){
35818             cls += ' masonry-brick-image';
35819         }
35820         
35821         if(this.size){
35822             cls += ' masonry-' + this.size + '-brick';
35823         }
35824         
35825         switch (this.placetitle) {
35826             case 'center' :
35827                 cls += ' masonry-center-title';
35828                 break;
35829             case 'bottom' :
35830                 cls += ' masonry-bottom-title';
35831                 break;
35832             default:
35833                 if(!this.bgimage.length){
35834                     cls += ' masonry-center-title';
35835                 }
35836
35837                 if(this.bgimage.length){
35838                     cls += ' masonry-bottom-title';
35839                 }
35840                 break;
35841         }
35842         
35843         if(this.cls){
35844             cls += ' ' + this.cls;
35845         }
35846         
35847         var cfg = {
35848             tag: (this.href.length) ? 'a' : 'div',
35849             cls: cls,
35850             cn: [
35851                 {
35852                     tag: 'div',
35853                     cls: 'masonry-brick-split-head',
35854                     cn: [
35855                         {
35856                             tag: 'div',
35857                             cls: 'masonry-brick-paragraph',
35858                             cn: []
35859                         }
35860                     ]
35861                 },
35862                 {
35863                     tag: 'div',
35864                     cls: 'masonry-brick-split-body',
35865                     cn: []
35866                 }
35867             ]
35868         };
35869         
35870         if(this.href.length){
35871             cfg.href = this.href;
35872         }
35873         
35874         if(this.title.length){
35875             cfg.cn[0].cn[0].cn.push({
35876                 tag: 'h4',
35877                 cls: 'masonry-brick-title',
35878                 html: this.title
35879             });
35880         }
35881         
35882         if(this.html.length){
35883             cfg.cn[1].cn.push({
35884                 tag: 'p',
35885                 cls: 'masonry-brick-text',
35886                 html: this.html
35887             });
35888         }
35889
35890         if(this.bgimage.length){
35891             cfg.cn[0].cn.push({
35892                 tag: 'img',
35893                 cls: 'masonry-brick-image-view',
35894                 src: this.bgimage
35895             });
35896         }
35897         
35898         if(this.videourl.length){
35899             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35900             // youtube support only?
35901             cfg.cn[0].cn.cn.push({
35902                 tag: 'iframe',
35903                 cls: 'masonry-brick-image-view',
35904                 src: vurl,
35905                 frameborder : 0,
35906                 allowfullscreen : true
35907             });
35908         }
35909         
35910         return cfg;
35911     },
35912     
35913     initEvents: function() 
35914     {
35915         switch (this.size) {
35916             case 'xs' :
35917                 this.x = 1;
35918                 this.y = 1;
35919                 break;
35920             case 'sm' :
35921                 this.x = 2;
35922                 this.y = 2;
35923                 break;
35924             case 'md' :
35925             case 'md-left' :
35926             case 'md-right' :
35927                 this.x = 3;
35928                 this.y = 3;
35929                 break;
35930             case 'tall' :
35931                 this.x = 2;
35932                 this.y = 3;
35933                 break;
35934             case 'wide' :
35935                 this.x = 3;
35936                 this.y = 2;
35937                 break;
35938             case 'wide-thin' :
35939                 this.x = 3;
35940                 this.y = 1;
35941                 break;
35942                         
35943             default :
35944                 break;
35945         }
35946         
35947         if(Roo.isTouch){
35948             this.el.on('touchstart', this.onTouchStart, this);
35949             this.el.on('touchmove', this.onTouchMove, this);
35950             this.el.on('touchend', this.onTouchEnd, this);
35951             this.el.on('contextmenu', this.onContextMenu, this);
35952         } else {
35953             this.el.on('mouseenter'  ,this.enter, this);
35954             this.el.on('mouseleave', this.leave, this);
35955             this.el.on('click', this.onClick, this);
35956         }
35957         
35958         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35959             this.parent().bricks.push(this);   
35960         }
35961         
35962     },
35963     
35964     onClick: function(e, el)
35965     {
35966         var time = this.endTimer - this.startTimer;
35967         // Roo.log(e.preventDefault());
35968         if(Roo.isTouch){
35969             if(time > 1000){
35970                 e.preventDefault();
35971                 return;
35972             }
35973         }
35974         
35975         if(!this.preventDefault){
35976             return;
35977         }
35978         
35979         e.preventDefault();
35980         
35981         if (this.activeClass != '') {
35982             this.selectBrick();
35983         }
35984         
35985         this.fireEvent('click', this, e);
35986     },
35987     
35988     enter: function(e, el)
35989     {
35990         e.preventDefault();
35991         
35992         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35993             return;
35994         }
35995         
35996         if(this.bgimage.length && this.html.length){
35997             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35998         }
35999     },
36000     
36001     leave: function(e, el)
36002     {
36003         e.preventDefault();
36004         
36005         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36006             return;
36007         }
36008         
36009         if(this.bgimage.length && this.html.length){
36010             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36011         }
36012     },
36013     
36014     onTouchStart: function(e, el)
36015     {
36016 //        e.preventDefault();
36017         
36018         this.touchmoved = false;
36019         
36020         if(!this.isFitContainer){
36021             return;
36022         }
36023         
36024         if(!this.bgimage.length || !this.html.length){
36025             return;
36026         }
36027         
36028         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36029         
36030         this.timer = new Date().getTime();
36031         
36032     },
36033     
36034     onTouchMove: function(e, el)
36035     {
36036         this.touchmoved = true;
36037     },
36038     
36039     onContextMenu : function(e,el)
36040     {
36041         e.preventDefault();
36042         e.stopPropagation();
36043         return false;
36044     },
36045     
36046     onTouchEnd: function(e, el)
36047     {
36048 //        e.preventDefault();
36049         
36050         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36051         
36052             this.leave(e,el);
36053             
36054             return;
36055         }
36056         
36057         if(!this.bgimage.length || !this.html.length){
36058             
36059             if(this.href.length){
36060                 window.location.href = this.href;
36061             }
36062             
36063             return;
36064         }
36065         
36066         if(!this.isFitContainer){
36067             return;
36068         }
36069         
36070         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36071         
36072         window.location.href = this.href;
36073     },
36074     
36075     //selection on single brick only
36076     selectBrick : function() {
36077         
36078         if (!this.parentId) {
36079             return;
36080         }
36081         
36082         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36083         var index = m.selectedBrick.indexOf(this.id);
36084         
36085         if ( index > -1) {
36086             m.selectedBrick.splice(index,1);
36087             this.el.removeClass(this.activeClass);
36088             return;
36089         }
36090         
36091         for(var i = 0; i < m.selectedBrick.length; i++) {
36092             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36093             b.el.removeClass(b.activeClass);
36094         }
36095         
36096         m.selectedBrick = [];
36097         
36098         m.selectedBrick.push(this.id);
36099         this.el.addClass(this.activeClass);
36100         return;
36101     },
36102     
36103     isSelected : function(){
36104         return this.el.hasClass(this.activeClass);
36105         
36106     }
36107 });
36108
36109 Roo.apply(Roo.bootstrap.MasonryBrick, {
36110     
36111     //groups: {},
36112     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36113      /**
36114     * register a Masonry Brick
36115     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36116     */
36117     
36118     register : function(brick)
36119     {
36120         //this.groups[brick.id] = brick;
36121         this.groups.add(brick.id, brick);
36122     },
36123     /**
36124     * fetch a  masonry brick based on the masonry brick ID
36125     * @param {string} the masonry brick to add
36126     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36127     */
36128     
36129     get: function(brick_id) 
36130     {
36131         // if (typeof(this.groups[brick_id]) == 'undefined') {
36132         //     return false;
36133         // }
36134         // return this.groups[brick_id] ;
36135         
36136         if(this.groups.key(brick_id)) {
36137             return this.groups.key(brick_id);
36138         }
36139         
36140         return false;
36141     }
36142     
36143     
36144     
36145 });
36146
36147  /*
36148  * - LGPL
36149  *
36150  * element
36151  * 
36152  */
36153
36154 /**
36155  * @class Roo.bootstrap.Brick
36156  * @extends Roo.bootstrap.Component
36157  * Bootstrap Brick class
36158  * 
36159  * @constructor
36160  * Create a new Brick
36161  * @param {Object} config The config object
36162  */
36163
36164 Roo.bootstrap.Brick = function(config){
36165     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36166     
36167     this.addEvents({
36168         // raw events
36169         /**
36170          * @event click
36171          * When a Brick is click
36172          * @param {Roo.bootstrap.Brick} this
36173          * @param {Roo.EventObject} e
36174          */
36175         "click" : true
36176     });
36177 };
36178
36179 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36180     
36181     /**
36182      * @cfg {String} title
36183      */   
36184     title : '',
36185     /**
36186      * @cfg {String} html
36187      */   
36188     html : '',
36189     /**
36190      * @cfg {String} bgimage
36191      */   
36192     bgimage : '',
36193     /**
36194      * @cfg {String} cls
36195      */   
36196     cls : '',
36197     /**
36198      * @cfg {String} href
36199      */   
36200     href : '',
36201     /**
36202      * @cfg {String} video
36203      */   
36204     video : '',
36205     /**
36206      * @cfg {Boolean} square
36207      */   
36208     square : true,
36209     
36210     getAutoCreate : function()
36211     {
36212         var cls = 'roo-brick';
36213         
36214         if(this.href.length){
36215             cls += ' roo-brick-link';
36216         }
36217         
36218         if(this.bgimage.length){
36219             cls += ' roo-brick-image';
36220         }
36221         
36222         if(!this.html.length && !this.bgimage.length){
36223             cls += ' roo-brick-center-title';
36224         }
36225         
36226         if(!this.html.length && this.bgimage.length){
36227             cls += ' roo-brick-bottom-title';
36228         }
36229         
36230         if(this.cls){
36231             cls += ' ' + this.cls;
36232         }
36233         
36234         var cfg = {
36235             tag: (this.href.length) ? 'a' : 'div',
36236             cls: cls,
36237             cn: [
36238                 {
36239                     tag: 'div',
36240                     cls: 'roo-brick-paragraph',
36241                     cn: []
36242                 }
36243             ]
36244         };
36245         
36246         if(this.href.length){
36247             cfg.href = this.href;
36248         }
36249         
36250         var cn = cfg.cn[0].cn;
36251         
36252         if(this.title.length){
36253             cn.push({
36254                 tag: 'h4',
36255                 cls: 'roo-brick-title',
36256                 html: this.title
36257             });
36258         }
36259         
36260         if(this.html.length){
36261             cn.push({
36262                 tag: 'p',
36263                 cls: 'roo-brick-text',
36264                 html: this.html
36265             });
36266         } else {
36267             cn.cls += ' hide';
36268         }
36269         
36270         if(this.bgimage.length){
36271             cfg.cn.push({
36272                 tag: 'img',
36273                 cls: 'roo-brick-image-view',
36274                 src: this.bgimage
36275             });
36276         }
36277         
36278         return cfg;
36279     },
36280     
36281     initEvents: function() 
36282     {
36283         if(this.title.length || this.html.length){
36284             this.el.on('mouseenter'  ,this.enter, this);
36285             this.el.on('mouseleave', this.leave, this);
36286         }
36287         
36288         Roo.EventManager.onWindowResize(this.resize, this); 
36289         
36290         if(this.bgimage.length){
36291             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36292             this.imageEl.on('load', this.onImageLoad, this);
36293             return;
36294         }
36295         
36296         this.resize();
36297     },
36298     
36299     onImageLoad : function()
36300     {
36301         this.resize();
36302     },
36303     
36304     resize : function()
36305     {
36306         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36307         
36308         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36309         
36310         if(this.bgimage.length){
36311             var image = this.el.select('.roo-brick-image-view', true).first();
36312             
36313             image.setWidth(paragraph.getWidth());
36314             
36315             if(this.square){
36316                 image.setHeight(paragraph.getWidth());
36317             }
36318             
36319             this.el.setHeight(image.getHeight());
36320             paragraph.setHeight(image.getHeight());
36321             
36322         }
36323         
36324     },
36325     
36326     enter: function(e, el)
36327     {
36328         e.preventDefault();
36329         
36330         if(this.bgimage.length){
36331             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36332             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36333         }
36334     },
36335     
36336     leave: function(e, el)
36337     {
36338         e.preventDefault();
36339         
36340         if(this.bgimage.length){
36341             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36342             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36343         }
36344     }
36345     
36346 });
36347
36348  
36349
36350  /*
36351  * - LGPL
36352  *
36353  * Number field 
36354  */
36355
36356 /**
36357  * @class Roo.bootstrap.NumberField
36358  * @extends Roo.bootstrap.Input
36359  * Bootstrap NumberField class
36360  * 
36361  * 
36362  * 
36363  * 
36364  * @constructor
36365  * Create a new NumberField
36366  * @param {Object} config The config object
36367  */
36368
36369 Roo.bootstrap.NumberField = function(config){
36370     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36371 };
36372
36373 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36374     
36375     /**
36376      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36377      */
36378     allowDecimals : true,
36379     /**
36380      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36381      */
36382     decimalSeparator : ".",
36383     /**
36384      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36385      */
36386     decimalPrecision : 2,
36387     /**
36388      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36389      */
36390     allowNegative : true,
36391     
36392     /**
36393      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36394      */
36395     allowZero: true,
36396     /**
36397      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36398      */
36399     minValue : Number.NEGATIVE_INFINITY,
36400     /**
36401      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36402      */
36403     maxValue : Number.MAX_VALUE,
36404     /**
36405      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36406      */
36407     minText : "The minimum value for this field is {0}",
36408     /**
36409      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36410      */
36411     maxText : "The maximum value for this field is {0}",
36412     /**
36413      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36414      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36415      */
36416     nanText : "{0} is not a valid number",
36417     /**
36418      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36419      */
36420     thousandsDelimiter : false,
36421     /**
36422      * @cfg {String} valueAlign alignment of value
36423      */
36424     valueAlign : "left",
36425
36426     getAutoCreate : function()
36427     {
36428         var hiddenInput = {
36429             tag: 'input',
36430             type: 'hidden',
36431             id: Roo.id(),
36432             cls: 'hidden-number-input'
36433         };
36434         
36435         if (this.name) {
36436             hiddenInput.name = this.name;
36437         }
36438         
36439         this.name = '';
36440         
36441         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36442         
36443         this.name = hiddenInput.name;
36444         
36445         if(cfg.cn.length > 0) {
36446             cfg.cn.push(hiddenInput);
36447         }
36448         
36449         return cfg;
36450     },
36451
36452     // private
36453     initEvents : function()
36454     {   
36455         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36456         
36457         var allowed = "0123456789";
36458         
36459         if(this.allowDecimals){
36460             allowed += this.decimalSeparator;
36461         }
36462         
36463         if(this.allowNegative){
36464             allowed += "-";
36465         }
36466         
36467         if(this.thousandsDelimiter) {
36468             allowed += ",";
36469         }
36470         
36471         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36472         
36473         var keyPress = function(e){
36474             
36475             var k = e.getKey();
36476             
36477             var c = e.getCharCode();
36478             
36479             if(
36480                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36481                     allowed.indexOf(String.fromCharCode(c)) === -1
36482             ){
36483                 e.stopEvent();
36484                 return;
36485             }
36486             
36487             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36488                 return;
36489             }
36490             
36491             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36492                 e.stopEvent();
36493             }
36494         };
36495         
36496         this.el.on("keypress", keyPress, this);
36497     },
36498     
36499     validateValue : function(value)
36500     {
36501         
36502         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36503             return false;
36504         }
36505         
36506         var num = this.parseValue(value);
36507         
36508         if(isNaN(num)){
36509             this.markInvalid(String.format(this.nanText, value));
36510             return false;
36511         }
36512         
36513         if(num < this.minValue){
36514             this.markInvalid(String.format(this.minText, this.minValue));
36515             return false;
36516         }
36517         
36518         if(num > this.maxValue){
36519             this.markInvalid(String.format(this.maxText, this.maxValue));
36520             return false;
36521         }
36522         
36523         return true;
36524     },
36525
36526     getValue : function()
36527     {
36528         var v = this.hiddenEl().getValue();
36529         
36530         return this.fixPrecision(this.parseValue(v));
36531     },
36532
36533     parseValue : function(value)
36534     {
36535         if(this.thousandsDelimiter) {
36536             value += "";
36537             r = new RegExp(",", "g");
36538             value = value.replace(r, "");
36539         }
36540         
36541         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36542         return isNaN(value) ? '' : value;
36543     },
36544
36545     fixPrecision : function(value)
36546     {
36547         if(this.thousandsDelimiter) {
36548             value += "";
36549             r = new RegExp(",", "g");
36550             value = value.replace(r, "");
36551         }
36552         
36553         var nan = isNaN(value);
36554         
36555         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36556             return nan ? '' : value;
36557         }
36558         return parseFloat(value).toFixed(this.decimalPrecision);
36559     },
36560
36561     setValue : function(v)
36562     {
36563         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36564         
36565         this.value = v;
36566         
36567         if(this.rendered){
36568             
36569             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36570             
36571             this.inputEl().dom.value = (v == '') ? '' :
36572                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36573             
36574             if(!this.allowZero && v === '0') {
36575                 this.hiddenEl().dom.value = '';
36576                 this.inputEl().dom.value = '';
36577             }
36578             
36579             this.validate();
36580         }
36581     },
36582
36583     decimalPrecisionFcn : function(v)
36584     {
36585         return Math.floor(v);
36586     },
36587
36588     beforeBlur : function()
36589     {
36590         var v = this.parseValue(this.getRawValue());
36591         
36592         if(v || v === 0 || v === ''){
36593             this.setValue(v);
36594         }
36595     },
36596     
36597     hiddenEl : function()
36598     {
36599         return this.el.select('input.hidden-number-input',true).first();
36600     }
36601     
36602 });
36603
36604  
36605
36606 /*
36607 * Licence: LGPL
36608 */
36609
36610 /**
36611  * @class Roo.bootstrap.DocumentSlider
36612  * @extends Roo.bootstrap.Component
36613  * Bootstrap DocumentSlider class
36614  * 
36615  * @constructor
36616  * Create a new DocumentViewer
36617  * @param {Object} config The config object
36618  */
36619
36620 Roo.bootstrap.DocumentSlider = function(config){
36621     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36622     
36623     this.files = [];
36624     
36625     this.addEvents({
36626         /**
36627          * @event initial
36628          * Fire after initEvent
36629          * @param {Roo.bootstrap.DocumentSlider} this
36630          */
36631         "initial" : true,
36632         /**
36633          * @event update
36634          * Fire after update
36635          * @param {Roo.bootstrap.DocumentSlider} this
36636          */
36637         "update" : true,
36638         /**
36639          * @event click
36640          * Fire after click
36641          * @param {Roo.bootstrap.DocumentSlider} this
36642          */
36643         "click" : true
36644     });
36645 };
36646
36647 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36648     
36649     files : false,
36650     
36651     indicator : 0,
36652     
36653     getAutoCreate : function()
36654     {
36655         var cfg = {
36656             tag : 'div',
36657             cls : 'roo-document-slider',
36658             cn : [
36659                 {
36660                     tag : 'div',
36661                     cls : 'roo-document-slider-header',
36662                     cn : [
36663                         {
36664                             tag : 'div',
36665                             cls : 'roo-document-slider-header-title'
36666                         }
36667                     ]
36668                 },
36669                 {
36670                     tag : 'div',
36671                     cls : 'roo-document-slider-body',
36672                     cn : [
36673                         {
36674                             tag : 'div',
36675                             cls : 'roo-document-slider-prev',
36676                             cn : [
36677                                 {
36678                                     tag : 'i',
36679                                     cls : 'fa fa-chevron-left'
36680                                 }
36681                             ]
36682                         },
36683                         {
36684                             tag : 'div',
36685                             cls : 'roo-document-slider-thumb',
36686                             cn : [
36687                                 {
36688                                     tag : 'img',
36689                                     cls : 'roo-document-slider-image'
36690                                 }
36691                             ]
36692                         },
36693                         {
36694                             tag : 'div',
36695                             cls : 'roo-document-slider-next',
36696                             cn : [
36697                                 {
36698                                     tag : 'i',
36699                                     cls : 'fa fa-chevron-right'
36700                                 }
36701                             ]
36702                         }
36703                     ]
36704                 }
36705             ]
36706         };
36707         
36708         return cfg;
36709     },
36710     
36711     initEvents : function()
36712     {
36713         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36714         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36715         
36716         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36717         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36718         
36719         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36720         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36721         
36722         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36723         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36724         
36725         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36726         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36727         
36728         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36729         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36730         
36731         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36732         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36733         
36734         this.thumbEl.on('click', this.onClick, this);
36735         
36736         this.prevIndicator.on('click', this.prev, this);
36737         
36738         this.nextIndicator.on('click', this.next, this);
36739         
36740     },
36741     
36742     initial : function()
36743     {
36744         if(this.files.length){
36745             this.indicator = 1;
36746             this.update()
36747         }
36748         
36749         this.fireEvent('initial', this);
36750     },
36751     
36752     update : function()
36753     {
36754         this.imageEl.attr('src', this.files[this.indicator - 1]);
36755         
36756         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36757         
36758         this.prevIndicator.show();
36759         
36760         if(this.indicator == 1){
36761             this.prevIndicator.hide();
36762         }
36763         
36764         this.nextIndicator.show();
36765         
36766         if(this.indicator == this.files.length){
36767             this.nextIndicator.hide();
36768         }
36769         
36770         this.thumbEl.scrollTo('top');
36771         
36772         this.fireEvent('update', this);
36773     },
36774     
36775     onClick : function(e)
36776     {
36777         e.preventDefault();
36778         
36779         this.fireEvent('click', this);
36780     },
36781     
36782     prev : function(e)
36783     {
36784         e.preventDefault();
36785         
36786         this.indicator = Math.max(1, this.indicator - 1);
36787         
36788         this.update();
36789     },
36790     
36791     next : function(e)
36792     {
36793         e.preventDefault();
36794         
36795         this.indicator = Math.min(this.files.length, this.indicator + 1);
36796         
36797         this.update();
36798     }
36799 });
36800 /*
36801  * - LGPL
36802  *
36803  * RadioSet
36804  *
36805  *
36806  */
36807
36808 /**
36809  * @class Roo.bootstrap.RadioSet
36810  * @extends Roo.bootstrap.Input
36811  * Bootstrap RadioSet class
36812  * @cfg {String} indicatorpos (left|right) default left
36813  * @cfg {Boolean} inline (true|false) inline the element (default true)
36814  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36815  * @constructor
36816  * Create a new RadioSet
36817  * @param {Object} config The config object
36818  */
36819
36820 Roo.bootstrap.RadioSet = function(config){
36821     
36822     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36823     
36824     this.radioes = [];
36825     
36826     Roo.bootstrap.RadioSet.register(this);
36827     
36828     this.addEvents({
36829         /**
36830         * @event check
36831         * Fires when the element is checked or unchecked.
36832         * @param {Roo.bootstrap.RadioSet} this This radio
36833         * @param {Roo.bootstrap.Radio} item The checked item
36834         */
36835        check : true,
36836        /**
36837         * @event click
36838         * Fires when the element is click.
36839         * @param {Roo.bootstrap.RadioSet} this This radio set
36840         * @param {Roo.bootstrap.Radio} item The checked item
36841         * @param {Roo.EventObject} e The event object
36842         */
36843        click : true
36844     });
36845     
36846 };
36847
36848 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36849
36850     radioes : false,
36851     
36852     inline : true,
36853     
36854     weight : '',
36855     
36856     indicatorpos : 'left',
36857     
36858     getAutoCreate : function()
36859     {
36860         var label = {
36861             tag : 'label',
36862             cls : 'roo-radio-set-label',
36863             cn : [
36864                 {
36865                     tag : 'span',
36866                     html : this.fieldLabel
36867                 }
36868             ]
36869         };
36870         if (Roo.bootstrap.version == 3) {
36871             
36872             
36873             if(this.indicatorpos == 'left'){
36874                 label.cn.unshift({
36875                     tag : 'i',
36876                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36877                     tooltip : 'This field is required'
36878                 });
36879             } else {
36880                 label.cn.push({
36881                     tag : 'i',
36882                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36883                     tooltip : 'This field is required'
36884                 });
36885             }
36886         }
36887         var items = {
36888             tag : 'div',
36889             cls : 'roo-radio-set-items'
36890         };
36891         
36892         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36893         
36894         if (align === 'left' && this.fieldLabel.length) {
36895             
36896             items = {
36897                 cls : "roo-radio-set-right", 
36898                 cn: [
36899                     items
36900                 ]
36901             };
36902             
36903             if(this.labelWidth > 12){
36904                 label.style = "width: " + this.labelWidth + 'px';
36905             }
36906             
36907             if(this.labelWidth < 13 && this.labelmd == 0){
36908                 this.labelmd = this.labelWidth;
36909             }
36910             
36911             if(this.labellg > 0){
36912                 label.cls += ' col-lg-' + this.labellg;
36913                 items.cls += ' col-lg-' + (12 - this.labellg);
36914             }
36915             
36916             if(this.labelmd > 0){
36917                 label.cls += ' col-md-' + this.labelmd;
36918                 items.cls += ' col-md-' + (12 - this.labelmd);
36919             }
36920             
36921             if(this.labelsm > 0){
36922                 label.cls += ' col-sm-' + this.labelsm;
36923                 items.cls += ' col-sm-' + (12 - this.labelsm);
36924             }
36925             
36926             if(this.labelxs > 0){
36927                 label.cls += ' col-xs-' + this.labelxs;
36928                 items.cls += ' col-xs-' + (12 - this.labelxs);
36929             }
36930         }
36931         
36932         var cfg = {
36933             tag : 'div',
36934             cls : 'roo-radio-set',
36935             cn : [
36936                 {
36937                     tag : 'input',
36938                     cls : 'roo-radio-set-input',
36939                     type : 'hidden',
36940                     name : this.name,
36941                     value : this.value ? this.value :  ''
36942                 },
36943                 label,
36944                 items
36945             ]
36946         };
36947         
36948         if(this.weight.length){
36949             cfg.cls += ' roo-radio-' + this.weight;
36950         }
36951         
36952         if(this.inline) {
36953             cfg.cls += ' roo-radio-set-inline';
36954         }
36955         
36956         var settings=this;
36957         ['xs','sm','md','lg'].map(function(size){
36958             if (settings[size]) {
36959                 cfg.cls += ' col-' + size + '-' + settings[size];
36960             }
36961         });
36962         
36963         return cfg;
36964         
36965     },
36966
36967     initEvents : function()
36968     {
36969         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36970         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36971         
36972         if(!this.fieldLabel.length){
36973             this.labelEl.hide();
36974         }
36975         
36976         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36977         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36978         
36979         this.indicator = this.indicatorEl();
36980         
36981         if(this.indicator){
36982             this.indicator.addClass('invisible');
36983         }
36984         
36985         this.originalValue = this.getValue();
36986         
36987     },
36988     
36989     inputEl: function ()
36990     {
36991         return this.el.select('.roo-radio-set-input', true).first();
36992     },
36993     
36994     getChildContainer : function()
36995     {
36996         return this.itemsEl;
36997     },
36998     
36999     register : function(item)
37000     {
37001         this.radioes.push(item);
37002         
37003     },
37004     
37005     validate : function()
37006     {   
37007         if(this.getVisibilityEl().hasClass('hidden')){
37008             return true;
37009         }
37010         
37011         var valid = false;
37012         
37013         Roo.each(this.radioes, function(i){
37014             if(!i.checked){
37015                 return;
37016             }
37017             
37018             valid = true;
37019             return false;
37020         });
37021         
37022         if(this.allowBlank) {
37023             return true;
37024         }
37025         
37026         if(this.disabled || valid){
37027             this.markValid();
37028             return true;
37029         }
37030         
37031         this.markInvalid();
37032         return false;
37033         
37034     },
37035     
37036     markValid : function()
37037     {
37038         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37039             this.indicatorEl().removeClass('visible');
37040             this.indicatorEl().addClass('invisible');
37041         }
37042         
37043         
37044         if (Roo.bootstrap.version == 3) {
37045             this.el.removeClass([this.invalidClass, this.validClass]);
37046             this.el.addClass(this.validClass);
37047         } else {
37048             this.el.removeClass(['is-invalid','is-valid']);
37049             this.el.addClass(['is-valid']);
37050         }
37051         this.fireEvent('valid', this);
37052     },
37053     
37054     markInvalid : function(msg)
37055     {
37056         if(this.allowBlank || this.disabled){
37057             return;
37058         }
37059         
37060         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37061             this.indicatorEl().removeClass('invisible');
37062             this.indicatorEl().addClass('visible');
37063         }
37064         if (Roo.bootstrap.version == 3) {
37065             this.el.removeClass([this.invalidClass, this.validClass]);
37066             this.el.addClass(this.invalidClass);
37067         } else {
37068             this.el.removeClass(['is-invalid','is-valid']);
37069             this.el.addClass(['is-invalid']);
37070         }
37071         
37072         this.fireEvent('invalid', this, msg);
37073         
37074     },
37075     
37076     setValue : function(v, suppressEvent)
37077     {   
37078         if(this.value === v){
37079             return;
37080         }
37081         
37082         this.value = v;
37083         
37084         if(this.rendered){
37085             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37086         }
37087         
37088         Roo.each(this.radioes, function(i){
37089             i.checked = false;
37090             i.el.removeClass('checked');
37091         });
37092         
37093         Roo.each(this.radioes, function(i){
37094             
37095             if(i.value === v || i.value.toString() === v.toString()){
37096                 i.checked = true;
37097                 i.el.addClass('checked');
37098                 
37099                 if(suppressEvent !== true){
37100                     this.fireEvent('check', this, i);
37101                 }
37102                 
37103                 return false;
37104             }
37105             
37106         }, this);
37107         
37108         this.validate();
37109     },
37110     
37111     clearInvalid : function(){
37112         
37113         if(!this.el || this.preventMark){
37114             return;
37115         }
37116         
37117         this.el.removeClass([this.invalidClass]);
37118         
37119         this.fireEvent('valid', this);
37120     }
37121     
37122 });
37123
37124 Roo.apply(Roo.bootstrap.RadioSet, {
37125     
37126     groups: {},
37127     
37128     register : function(set)
37129     {
37130         this.groups[set.name] = set;
37131     },
37132     
37133     get: function(name) 
37134     {
37135         if (typeof(this.groups[name]) == 'undefined') {
37136             return false;
37137         }
37138         
37139         return this.groups[name] ;
37140     }
37141     
37142 });
37143 /*
37144  * Based on:
37145  * Ext JS Library 1.1.1
37146  * Copyright(c) 2006-2007, Ext JS, LLC.
37147  *
37148  * Originally Released Under LGPL - original licence link has changed is not relivant.
37149  *
37150  * Fork - LGPL
37151  * <script type="text/javascript">
37152  */
37153
37154
37155 /**
37156  * @class Roo.bootstrap.SplitBar
37157  * @extends Roo.util.Observable
37158  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37159  * <br><br>
37160  * Usage:
37161  * <pre><code>
37162 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37163                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37164 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37165 split.minSize = 100;
37166 split.maxSize = 600;
37167 split.animate = true;
37168 split.on('moved', splitterMoved);
37169 </code></pre>
37170  * @constructor
37171  * Create a new SplitBar
37172  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37173  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37174  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37175  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37176                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37177                         position of the SplitBar).
37178  */
37179 Roo.bootstrap.SplitBar = function(cfg){
37180     
37181     /** @private */
37182     
37183     //{
37184     //  dragElement : elm
37185     //  resizingElement: el,
37186         // optional..
37187     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37188     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37189         // existingProxy ???
37190     //}
37191     
37192     this.el = Roo.get(cfg.dragElement, true);
37193     this.el.dom.unselectable = "on";
37194     /** @private */
37195     this.resizingEl = Roo.get(cfg.resizingElement, true);
37196
37197     /**
37198      * @private
37199      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37200      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37201      * @type Number
37202      */
37203     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37204     
37205     /**
37206      * The minimum size of the resizing element. (Defaults to 0)
37207      * @type Number
37208      */
37209     this.minSize = 0;
37210     
37211     /**
37212      * The maximum size of the resizing element. (Defaults to 2000)
37213      * @type Number
37214      */
37215     this.maxSize = 2000;
37216     
37217     /**
37218      * Whether to animate the transition to the new size
37219      * @type Boolean
37220      */
37221     this.animate = false;
37222     
37223     /**
37224      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37225      * @type Boolean
37226      */
37227     this.useShim = false;
37228     
37229     /** @private */
37230     this.shim = null;
37231     
37232     if(!cfg.existingProxy){
37233         /** @private */
37234         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37235     }else{
37236         this.proxy = Roo.get(cfg.existingProxy).dom;
37237     }
37238     /** @private */
37239     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37240     
37241     /** @private */
37242     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37243     
37244     /** @private */
37245     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37246     
37247     /** @private */
37248     this.dragSpecs = {};
37249     
37250     /**
37251      * @private The adapter to use to positon and resize elements
37252      */
37253     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37254     this.adapter.init(this);
37255     
37256     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37257         /** @private */
37258         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37259         this.el.addClass("roo-splitbar-h");
37260     }else{
37261         /** @private */
37262         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37263         this.el.addClass("roo-splitbar-v");
37264     }
37265     
37266     this.addEvents({
37267         /**
37268          * @event resize
37269          * Fires when the splitter is moved (alias for {@link #event-moved})
37270          * @param {Roo.bootstrap.SplitBar} this
37271          * @param {Number} newSize the new width or height
37272          */
37273         "resize" : true,
37274         /**
37275          * @event moved
37276          * Fires when the splitter is moved
37277          * @param {Roo.bootstrap.SplitBar} this
37278          * @param {Number} newSize the new width or height
37279          */
37280         "moved" : true,
37281         /**
37282          * @event beforeresize
37283          * Fires before the splitter is dragged
37284          * @param {Roo.bootstrap.SplitBar} this
37285          */
37286         "beforeresize" : true,
37287
37288         "beforeapply" : true
37289     });
37290
37291     Roo.util.Observable.call(this);
37292 };
37293
37294 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37295     onStartProxyDrag : function(x, y){
37296         this.fireEvent("beforeresize", this);
37297         if(!this.overlay){
37298             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37299             o.unselectable();
37300             o.enableDisplayMode("block");
37301             // all splitbars share the same overlay
37302             Roo.bootstrap.SplitBar.prototype.overlay = o;
37303         }
37304         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37305         this.overlay.show();
37306         Roo.get(this.proxy).setDisplayed("block");
37307         var size = this.adapter.getElementSize(this);
37308         this.activeMinSize = this.getMinimumSize();;
37309         this.activeMaxSize = this.getMaximumSize();;
37310         var c1 = size - this.activeMinSize;
37311         var c2 = Math.max(this.activeMaxSize - size, 0);
37312         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37313             this.dd.resetConstraints();
37314             this.dd.setXConstraint(
37315                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37316                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37317             );
37318             this.dd.setYConstraint(0, 0);
37319         }else{
37320             this.dd.resetConstraints();
37321             this.dd.setXConstraint(0, 0);
37322             this.dd.setYConstraint(
37323                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37324                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37325             );
37326          }
37327         this.dragSpecs.startSize = size;
37328         this.dragSpecs.startPoint = [x, y];
37329         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37330     },
37331     
37332     /** 
37333      * @private Called after the drag operation by the DDProxy
37334      */
37335     onEndProxyDrag : function(e){
37336         Roo.get(this.proxy).setDisplayed(false);
37337         var endPoint = Roo.lib.Event.getXY(e);
37338         if(this.overlay){
37339             this.overlay.hide();
37340         }
37341         var newSize;
37342         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37343             newSize = this.dragSpecs.startSize + 
37344                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37345                     endPoint[0] - this.dragSpecs.startPoint[0] :
37346                     this.dragSpecs.startPoint[0] - endPoint[0]
37347                 );
37348         }else{
37349             newSize = this.dragSpecs.startSize + 
37350                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37351                     endPoint[1] - this.dragSpecs.startPoint[1] :
37352                     this.dragSpecs.startPoint[1] - endPoint[1]
37353                 );
37354         }
37355         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37356         if(newSize != this.dragSpecs.startSize){
37357             if(this.fireEvent('beforeapply', this, newSize) !== false){
37358                 this.adapter.setElementSize(this, newSize);
37359                 this.fireEvent("moved", this, newSize);
37360                 this.fireEvent("resize", this, newSize);
37361             }
37362         }
37363     },
37364     
37365     /**
37366      * Get the adapter this SplitBar uses
37367      * @return The adapter object
37368      */
37369     getAdapter : function(){
37370         return this.adapter;
37371     },
37372     
37373     /**
37374      * Set the adapter this SplitBar uses
37375      * @param {Object} adapter A SplitBar adapter object
37376      */
37377     setAdapter : function(adapter){
37378         this.adapter = adapter;
37379         this.adapter.init(this);
37380     },
37381     
37382     /**
37383      * Gets the minimum size for the resizing element
37384      * @return {Number} The minimum size
37385      */
37386     getMinimumSize : function(){
37387         return this.minSize;
37388     },
37389     
37390     /**
37391      * Sets the minimum size for the resizing element
37392      * @param {Number} minSize The minimum size
37393      */
37394     setMinimumSize : function(minSize){
37395         this.minSize = minSize;
37396     },
37397     
37398     /**
37399      * Gets the maximum size for the resizing element
37400      * @return {Number} The maximum size
37401      */
37402     getMaximumSize : function(){
37403         return this.maxSize;
37404     },
37405     
37406     /**
37407      * Sets the maximum size for the resizing element
37408      * @param {Number} maxSize The maximum size
37409      */
37410     setMaximumSize : function(maxSize){
37411         this.maxSize = maxSize;
37412     },
37413     
37414     /**
37415      * Sets the initialize size for the resizing element
37416      * @param {Number} size The initial size
37417      */
37418     setCurrentSize : function(size){
37419         var oldAnimate = this.animate;
37420         this.animate = false;
37421         this.adapter.setElementSize(this, size);
37422         this.animate = oldAnimate;
37423     },
37424     
37425     /**
37426      * Destroy this splitbar. 
37427      * @param {Boolean} removeEl True to remove the element
37428      */
37429     destroy : function(removeEl){
37430         if(this.shim){
37431             this.shim.remove();
37432         }
37433         this.dd.unreg();
37434         this.proxy.parentNode.removeChild(this.proxy);
37435         if(removeEl){
37436             this.el.remove();
37437         }
37438     }
37439 });
37440
37441 /**
37442  * @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.
37443  */
37444 Roo.bootstrap.SplitBar.createProxy = function(dir){
37445     var proxy = new Roo.Element(document.createElement("div"));
37446     proxy.unselectable();
37447     var cls = 'roo-splitbar-proxy';
37448     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37449     document.body.appendChild(proxy.dom);
37450     return proxy.dom;
37451 };
37452
37453 /** 
37454  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37455  * Default Adapter. It assumes the splitter and resizing element are not positioned
37456  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37457  */
37458 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37459 };
37460
37461 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37462     // do nothing for now
37463     init : function(s){
37464     
37465     },
37466     /**
37467      * Called before drag operations to get the current size of the resizing element. 
37468      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37469      */
37470      getElementSize : function(s){
37471         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37472             return s.resizingEl.getWidth();
37473         }else{
37474             return s.resizingEl.getHeight();
37475         }
37476     },
37477     
37478     /**
37479      * Called after drag operations to set the size of the resizing element.
37480      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37481      * @param {Number} newSize The new size to set
37482      * @param {Function} onComplete A function to be invoked when resizing is complete
37483      */
37484     setElementSize : function(s, newSize, onComplete){
37485         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37486             if(!s.animate){
37487                 s.resizingEl.setWidth(newSize);
37488                 if(onComplete){
37489                     onComplete(s, newSize);
37490                 }
37491             }else{
37492                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37493             }
37494         }else{
37495             
37496             if(!s.animate){
37497                 s.resizingEl.setHeight(newSize);
37498                 if(onComplete){
37499                     onComplete(s, newSize);
37500                 }
37501             }else{
37502                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37503             }
37504         }
37505     }
37506 };
37507
37508 /** 
37509  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37510  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37511  * Adapter that  moves the splitter element to align with the resized sizing element. 
37512  * Used with an absolute positioned SplitBar.
37513  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37514  * document.body, make sure you assign an id to the body element.
37515  */
37516 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37517     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37518     this.container = Roo.get(container);
37519 };
37520
37521 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37522     init : function(s){
37523         this.basic.init(s);
37524     },
37525     
37526     getElementSize : function(s){
37527         return this.basic.getElementSize(s);
37528     },
37529     
37530     setElementSize : function(s, newSize, onComplete){
37531         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37532     },
37533     
37534     moveSplitter : function(s){
37535         var yes = Roo.bootstrap.SplitBar;
37536         switch(s.placement){
37537             case yes.LEFT:
37538                 s.el.setX(s.resizingEl.getRight());
37539                 break;
37540             case yes.RIGHT:
37541                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37542                 break;
37543             case yes.TOP:
37544                 s.el.setY(s.resizingEl.getBottom());
37545                 break;
37546             case yes.BOTTOM:
37547                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37548                 break;
37549         }
37550     }
37551 };
37552
37553 /**
37554  * Orientation constant - Create a vertical SplitBar
37555  * @static
37556  * @type Number
37557  */
37558 Roo.bootstrap.SplitBar.VERTICAL = 1;
37559
37560 /**
37561  * Orientation constant - Create a horizontal SplitBar
37562  * @static
37563  * @type Number
37564  */
37565 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37566
37567 /**
37568  * Placement constant - The resizing element is to the left of the splitter element
37569  * @static
37570  * @type Number
37571  */
37572 Roo.bootstrap.SplitBar.LEFT = 1;
37573
37574 /**
37575  * Placement constant - The resizing element is to the right of the splitter element
37576  * @static
37577  * @type Number
37578  */
37579 Roo.bootstrap.SplitBar.RIGHT = 2;
37580
37581 /**
37582  * Placement constant - The resizing element is positioned above the splitter element
37583  * @static
37584  * @type Number
37585  */
37586 Roo.bootstrap.SplitBar.TOP = 3;
37587
37588 /**
37589  * Placement constant - The resizing element is positioned under splitter element
37590  * @static
37591  * @type Number
37592  */
37593 Roo.bootstrap.SplitBar.BOTTOM = 4;
37594 Roo.namespace("Roo.bootstrap.layout");/*
37595  * Based on:
37596  * Ext JS Library 1.1.1
37597  * Copyright(c) 2006-2007, Ext JS, LLC.
37598  *
37599  * Originally Released Under LGPL - original licence link has changed is not relivant.
37600  *
37601  * Fork - LGPL
37602  * <script type="text/javascript">
37603  */
37604
37605 /**
37606  * @class Roo.bootstrap.layout.Manager
37607  * @extends Roo.bootstrap.Component
37608  * Base class for layout managers.
37609  */
37610 Roo.bootstrap.layout.Manager = function(config)
37611 {
37612     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37613
37614
37615
37616
37617
37618     /** false to disable window resize monitoring @type Boolean */
37619     this.monitorWindowResize = true;
37620     this.regions = {};
37621     this.addEvents({
37622         /**
37623          * @event layout
37624          * Fires when a layout is performed.
37625          * @param {Roo.LayoutManager} this
37626          */
37627         "layout" : true,
37628         /**
37629          * @event regionresized
37630          * Fires when the user resizes a region.
37631          * @param {Roo.LayoutRegion} region The resized region
37632          * @param {Number} newSize The new size (width for east/west, height for north/south)
37633          */
37634         "regionresized" : true,
37635         /**
37636          * @event regioncollapsed
37637          * Fires when a region is collapsed.
37638          * @param {Roo.LayoutRegion} region The collapsed region
37639          */
37640         "regioncollapsed" : true,
37641         /**
37642          * @event regionexpanded
37643          * Fires when a region is expanded.
37644          * @param {Roo.LayoutRegion} region The expanded region
37645          */
37646         "regionexpanded" : true
37647     });
37648     this.updating = false;
37649
37650     if (config.el) {
37651         this.el = Roo.get(config.el);
37652         this.initEvents();
37653     }
37654
37655 };
37656
37657 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37658
37659
37660     regions : null,
37661
37662     monitorWindowResize : true,
37663
37664
37665     updating : false,
37666
37667
37668     onRender : function(ct, position)
37669     {
37670         if(!this.el){
37671             this.el = Roo.get(ct);
37672             this.initEvents();
37673         }
37674         //this.fireEvent('render',this);
37675     },
37676
37677
37678     initEvents: function()
37679     {
37680
37681
37682         // ie scrollbar fix
37683         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37684             document.body.scroll = "no";
37685         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37686             this.el.position('relative');
37687         }
37688         this.id = this.el.id;
37689         this.el.addClass("roo-layout-container");
37690         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37691         if(this.el.dom != document.body ) {
37692             this.el.on('resize', this.layout,this);
37693             this.el.on('show', this.layout,this);
37694         }
37695
37696     },
37697
37698     /**
37699      * Returns true if this layout is currently being updated
37700      * @return {Boolean}
37701      */
37702     isUpdating : function(){
37703         return this.updating;
37704     },
37705
37706     /**
37707      * Suspend the LayoutManager from doing auto-layouts while
37708      * making multiple add or remove calls
37709      */
37710     beginUpdate : function(){
37711         this.updating = true;
37712     },
37713
37714     /**
37715      * Restore auto-layouts and optionally disable the manager from performing a layout
37716      * @param {Boolean} noLayout true to disable a layout update
37717      */
37718     endUpdate : function(noLayout){
37719         this.updating = false;
37720         if(!noLayout){
37721             this.layout();
37722         }
37723     },
37724
37725     layout: function(){
37726         // abstract...
37727     },
37728
37729     onRegionResized : function(region, newSize){
37730         this.fireEvent("regionresized", region, newSize);
37731         this.layout();
37732     },
37733
37734     onRegionCollapsed : function(region){
37735         this.fireEvent("regioncollapsed", region);
37736     },
37737
37738     onRegionExpanded : function(region){
37739         this.fireEvent("regionexpanded", region);
37740     },
37741
37742     /**
37743      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37744      * performs box-model adjustments.
37745      * @return {Object} The size as an object {width: (the width), height: (the height)}
37746      */
37747     getViewSize : function()
37748     {
37749         var size;
37750         if(this.el.dom != document.body){
37751             size = this.el.getSize();
37752         }else{
37753             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37754         }
37755         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37756         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37757         return size;
37758     },
37759
37760     /**
37761      * Returns the Element this layout is bound to.
37762      * @return {Roo.Element}
37763      */
37764     getEl : function(){
37765         return this.el;
37766     },
37767
37768     /**
37769      * Returns the specified region.
37770      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37771      * @return {Roo.LayoutRegion}
37772      */
37773     getRegion : function(target){
37774         return this.regions[target.toLowerCase()];
37775     },
37776
37777     onWindowResize : function(){
37778         if(this.monitorWindowResize){
37779             this.layout();
37780         }
37781     }
37782 });
37783 /*
37784  * Based on:
37785  * Ext JS Library 1.1.1
37786  * Copyright(c) 2006-2007, Ext JS, LLC.
37787  *
37788  * Originally Released Under LGPL - original licence link has changed is not relivant.
37789  *
37790  * Fork - LGPL
37791  * <script type="text/javascript">
37792  */
37793 /**
37794  * @class Roo.bootstrap.layout.Border
37795  * @extends Roo.bootstrap.layout.Manager
37796  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37797  * please see: examples/bootstrap/nested.html<br><br>
37798  
37799 <b>The container the layout is rendered into can be either the body element or any other element.
37800 If it is not the body element, the container needs to either be an absolute positioned element,
37801 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37802 the container size if it is not the body element.</b>
37803
37804 * @constructor
37805 * Create a new Border
37806 * @param {Object} config Configuration options
37807  */
37808 Roo.bootstrap.layout.Border = function(config){
37809     config = config || {};
37810     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37811     
37812     
37813     
37814     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37815         if(config[region]){
37816             config[region].region = region;
37817             this.addRegion(config[region]);
37818         }
37819     },this);
37820     
37821 };
37822
37823 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37824
37825 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37826     
37827     parent : false, // this might point to a 'nest' or a ???
37828     
37829     /**
37830      * Creates and adds a new region if it doesn't already exist.
37831      * @param {String} target The target region key (north, south, east, west or center).
37832      * @param {Object} config The regions config object
37833      * @return {BorderLayoutRegion} The new region
37834      */
37835     addRegion : function(config)
37836     {
37837         if(!this.regions[config.region]){
37838             var r = this.factory(config);
37839             this.bindRegion(r);
37840         }
37841         return this.regions[config.region];
37842     },
37843
37844     // private (kinda)
37845     bindRegion : function(r){
37846         this.regions[r.config.region] = r;
37847         
37848         r.on("visibilitychange",    this.layout, this);
37849         r.on("paneladded",          this.layout, this);
37850         r.on("panelremoved",        this.layout, this);
37851         r.on("invalidated",         this.layout, this);
37852         r.on("resized",             this.onRegionResized, this);
37853         r.on("collapsed",           this.onRegionCollapsed, this);
37854         r.on("expanded",            this.onRegionExpanded, this);
37855     },
37856
37857     /**
37858      * Performs a layout update.
37859      */
37860     layout : function()
37861     {
37862         if(this.updating) {
37863             return;
37864         }
37865         
37866         // render all the rebions if they have not been done alreayd?
37867         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37868             if(this.regions[region] && !this.regions[region].bodyEl){
37869                 this.regions[region].onRender(this.el)
37870             }
37871         },this);
37872         
37873         var size = this.getViewSize();
37874         var w = size.width;
37875         var h = size.height;
37876         var centerW = w;
37877         var centerH = h;
37878         var centerY = 0;
37879         var centerX = 0;
37880         //var x = 0, y = 0;
37881
37882         var rs = this.regions;
37883         var north = rs["north"];
37884         var south = rs["south"]; 
37885         var west = rs["west"];
37886         var east = rs["east"];
37887         var center = rs["center"];
37888         //if(this.hideOnLayout){ // not supported anymore
37889             //c.el.setStyle("display", "none");
37890         //}
37891         if(north && north.isVisible()){
37892             var b = north.getBox();
37893             var m = north.getMargins();
37894             b.width = w - (m.left+m.right);
37895             b.x = m.left;
37896             b.y = m.top;
37897             centerY = b.height + b.y + m.bottom;
37898             centerH -= centerY;
37899             north.updateBox(this.safeBox(b));
37900         }
37901         if(south && south.isVisible()){
37902             var b = south.getBox();
37903             var m = south.getMargins();
37904             b.width = w - (m.left+m.right);
37905             b.x = m.left;
37906             var totalHeight = (b.height + m.top + m.bottom);
37907             b.y = h - totalHeight + m.top;
37908             centerH -= totalHeight;
37909             south.updateBox(this.safeBox(b));
37910         }
37911         if(west && west.isVisible()){
37912             var b = west.getBox();
37913             var m = west.getMargins();
37914             b.height = centerH - (m.top+m.bottom);
37915             b.x = m.left;
37916             b.y = centerY + m.top;
37917             var totalWidth = (b.width + m.left + m.right);
37918             centerX += totalWidth;
37919             centerW -= totalWidth;
37920             west.updateBox(this.safeBox(b));
37921         }
37922         if(east && east.isVisible()){
37923             var b = east.getBox();
37924             var m = east.getMargins();
37925             b.height = centerH - (m.top+m.bottom);
37926             var totalWidth = (b.width + m.left + m.right);
37927             b.x = w - totalWidth + m.left;
37928             b.y = centerY + m.top;
37929             centerW -= totalWidth;
37930             east.updateBox(this.safeBox(b));
37931         }
37932         if(center){
37933             var m = center.getMargins();
37934             var centerBox = {
37935                 x: centerX + m.left,
37936                 y: centerY + m.top,
37937                 width: centerW - (m.left+m.right),
37938                 height: centerH - (m.top+m.bottom)
37939             };
37940             //if(this.hideOnLayout){
37941                 //center.el.setStyle("display", "block");
37942             //}
37943             center.updateBox(this.safeBox(centerBox));
37944         }
37945         this.el.repaint();
37946         this.fireEvent("layout", this);
37947     },
37948
37949     // private
37950     safeBox : function(box){
37951         box.width = Math.max(0, box.width);
37952         box.height = Math.max(0, box.height);
37953         return box;
37954     },
37955
37956     /**
37957      * Adds a ContentPanel (or subclass) to this layout.
37958      * @param {String} target The target region key (north, south, east, west or center).
37959      * @param {Roo.ContentPanel} panel The panel to add
37960      * @return {Roo.ContentPanel} The added panel
37961      */
37962     add : function(target, panel){
37963          
37964         target = target.toLowerCase();
37965         return this.regions[target].add(panel);
37966     },
37967
37968     /**
37969      * Remove a ContentPanel (or subclass) to this layout.
37970      * @param {String} target The target region key (north, south, east, west or center).
37971      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37972      * @return {Roo.ContentPanel} The removed panel
37973      */
37974     remove : function(target, panel){
37975         target = target.toLowerCase();
37976         return this.regions[target].remove(panel);
37977     },
37978
37979     /**
37980      * Searches all regions for a panel with the specified id
37981      * @param {String} panelId
37982      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37983      */
37984     findPanel : function(panelId){
37985         var rs = this.regions;
37986         for(var target in rs){
37987             if(typeof rs[target] != "function"){
37988                 var p = rs[target].getPanel(panelId);
37989                 if(p){
37990                     return p;
37991                 }
37992             }
37993         }
37994         return null;
37995     },
37996
37997     /**
37998      * Searches all regions for a panel with the specified id and activates (shows) it.
37999      * @param {String/ContentPanel} panelId The panels id or the panel itself
38000      * @return {Roo.ContentPanel} The shown panel or null
38001      */
38002     showPanel : function(panelId) {
38003       var rs = this.regions;
38004       for(var target in rs){
38005          var r = rs[target];
38006          if(typeof r != "function"){
38007             if(r.hasPanel(panelId)){
38008                return r.showPanel(panelId);
38009             }
38010          }
38011       }
38012       return null;
38013    },
38014
38015    /**
38016      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38017      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38018      */
38019    /*
38020     restoreState : function(provider){
38021         if(!provider){
38022             provider = Roo.state.Manager;
38023         }
38024         var sm = new Roo.LayoutStateManager();
38025         sm.init(this, provider);
38026     },
38027 */
38028  
38029  
38030     /**
38031      * Adds a xtype elements to the layout.
38032      * <pre><code>
38033
38034 layout.addxtype({
38035        xtype : 'ContentPanel',
38036        region: 'west',
38037        items: [ .... ]
38038    }
38039 );
38040
38041 layout.addxtype({
38042         xtype : 'NestedLayoutPanel',
38043         region: 'west',
38044         layout: {
38045            center: { },
38046            west: { }   
38047         },
38048         items : [ ... list of content panels or nested layout panels.. ]
38049    }
38050 );
38051 </code></pre>
38052      * @param {Object} cfg Xtype definition of item to add.
38053      */
38054     addxtype : function(cfg)
38055     {
38056         // basically accepts a pannel...
38057         // can accept a layout region..!?!?
38058         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38059         
38060         
38061         // theory?  children can only be panels??
38062         
38063         //if (!cfg.xtype.match(/Panel$/)) {
38064         //    return false;
38065         //}
38066         var ret = false;
38067         
38068         if (typeof(cfg.region) == 'undefined') {
38069             Roo.log("Failed to add Panel, region was not set");
38070             Roo.log(cfg);
38071             return false;
38072         }
38073         var region = cfg.region;
38074         delete cfg.region;
38075         
38076           
38077         var xitems = [];
38078         if (cfg.items) {
38079             xitems = cfg.items;
38080             delete cfg.items;
38081         }
38082         var nb = false;
38083         
38084         if ( region == 'center') {
38085             Roo.log("Center: " + cfg.title);
38086         }
38087         
38088         
38089         switch(cfg.xtype) 
38090         {
38091             case 'Content':  // ContentPanel (el, cfg)
38092             case 'Scroll':  // ContentPanel (el, cfg)
38093             case 'View': 
38094                 cfg.autoCreate = cfg.autoCreate || true;
38095                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38096                 //} else {
38097                 //    var el = this.el.createChild();
38098                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38099                 //}
38100                 
38101                 this.add(region, ret);
38102                 break;
38103             
38104             /*
38105             case 'TreePanel': // our new panel!
38106                 cfg.el = this.el.createChild();
38107                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38108                 this.add(region, ret);
38109                 break;
38110             */
38111             
38112             case 'Nest': 
38113                 // create a new Layout (which is  a Border Layout...
38114                 
38115                 var clayout = cfg.layout;
38116                 clayout.el  = this.el.createChild();
38117                 clayout.items   = clayout.items  || [];
38118                 
38119                 delete cfg.layout;
38120                 
38121                 // replace this exitems with the clayout ones..
38122                 xitems = clayout.items;
38123                  
38124                 // force background off if it's in center...
38125                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38126                     cfg.background = false;
38127                 }
38128                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38129                 
38130                 
38131                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38132                 //console.log('adding nested layout panel '  + cfg.toSource());
38133                 this.add(region, ret);
38134                 nb = {}; /// find first...
38135                 break;
38136             
38137             case 'Grid':
38138                 
38139                 // needs grid and region
38140                 
38141                 //var el = this.getRegion(region).el.createChild();
38142                 /*
38143                  *var el = this.el.createChild();
38144                 // create the grid first...
38145                 cfg.grid.container = el;
38146                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38147                 */
38148                 
38149                 if (region == 'center' && this.active ) {
38150                     cfg.background = false;
38151                 }
38152                 
38153                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38154                 
38155                 this.add(region, ret);
38156                 /*
38157                 if (cfg.background) {
38158                     // render grid on panel activation (if panel background)
38159                     ret.on('activate', function(gp) {
38160                         if (!gp.grid.rendered) {
38161                     //        gp.grid.render(el);
38162                         }
38163                     });
38164                 } else {
38165                   //  cfg.grid.render(el);
38166                 }
38167                 */
38168                 break;
38169            
38170            
38171             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38172                 // it was the old xcomponent building that caused this before.
38173                 // espeically if border is the top element in the tree.
38174                 ret = this;
38175                 break; 
38176                 
38177                     
38178                 
38179                 
38180                 
38181             default:
38182                 /*
38183                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38184                     
38185                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38186                     this.add(region, ret);
38187                 } else {
38188                 */
38189                     Roo.log(cfg);
38190                     throw "Can not add '" + cfg.xtype + "' to Border";
38191                     return null;
38192              
38193                                 
38194              
38195         }
38196         this.beginUpdate();
38197         // add children..
38198         var region = '';
38199         var abn = {};
38200         Roo.each(xitems, function(i)  {
38201             region = nb && i.region ? i.region : false;
38202             
38203             var add = ret.addxtype(i);
38204            
38205             if (region) {
38206                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38207                 if (!i.background) {
38208                     abn[region] = nb[region] ;
38209                 }
38210             }
38211             
38212         });
38213         this.endUpdate();
38214
38215         // make the last non-background panel active..
38216         //if (nb) { Roo.log(abn); }
38217         if (nb) {
38218             
38219             for(var r in abn) {
38220                 region = this.getRegion(r);
38221                 if (region) {
38222                     // tried using nb[r], but it does not work..
38223                      
38224                     region.showPanel(abn[r]);
38225                    
38226                 }
38227             }
38228         }
38229         return ret;
38230         
38231     },
38232     
38233     
38234 // private
38235     factory : function(cfg)
38236     {
38237         
38238         var validRegions = Roo.bootstrap.layout.Border.regions;
38239
38240         var target = cfg.region;
38241         cfg.mgr = this;
38242         
38243         var r = Roo.bootstrap.layout;
38244         Roo.log(target);
38245         switch(target){
38246             case "north":
38247                 return new r.North(cfg);
38248             case "south":
38249                 return new r.South(cfg);
38250             case "east":
38251                 return new r.East(cfg);
38252             case "west":
38253                 return new r.West(cfg);
38254             case "center":
38255                 return new r.Center(cfg);
38256         }
38257         throw 'Layout region "'+target+'" not supported.';
38258     }
38259     
38260     
38261 });
38262  /*
38263  * Based on:
38264  * Ext JS Library 1.1.1
38265  * Copyright(c) 2006-2007, Ext JS, LLC.
38266  *
38267  * Originally Released Under LGPL - original licence link has changed is not relivant.
38268  *
38269  * Fork - LGPL
38270  * <script type="text/javascript">
38271  */
38272  
38273 /**
38274  * @class Roo.bootstrap.layout.Basic
38275  * @extends Roo.util.Observable
38276  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38277  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38278  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38279  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38280  * @cfg {string}   region  the region that it inhabits..
38281  * @cfg {bool}   skipConfig skip config?
38282  * 
38283
38284  */
38285 Roo.bootstrap.layout.Basic = function(config){
38286     
38287     this.mgr = config.mgr;
38288     
38289     this.position = config.region;
38290     
38291     var skipConfig = config.skipConfig;
38292     
38293     this.events = {
38294         /**
38295          * @scope Roo.BasicLayoutRegion
38296          */
38297         
38298         /**
38299          * @event beforeremove
38300          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38301          * @param {Roo.LayoutRegion} this
38302          * @param {Roo.ContentPanel} panel The panel
38303          * @param {Object} e The cancel event object
38304          */
38305         "beforeremove" : true,
38306         /**
38307          * @event invalidated
38308          * Fires when the layout for this region is changed.
38309          * @param {Roo.LayoutRegion} this
38310          */
38311         "invalidated" : true,
38312         /**
38313          * @event visibilitychange
38314          * Fires when this region is shown or hidden 
38315          * @param {Roo.LayoutRegion} this
38316          * @param {Boolean} visibility true or false
38317          */
38318         "visibilitychange" : true,
38319         /**
38320          * @event paneladded
38321          * Fires when a panel is added. 
38322          * @param {Roo.LayoutRegion} this
38323          * @param {Roo.ContentPanel} panel The panel
38324          */
38325         "paneladded" : true,
38326         /**
38327          * @event panelremoved
38328          * Fires when a panel is removed. 
38329          * @param {Roo.LayoutRegion} this
38330          * @param {Roo.ContentPanel} panel The panel
38331          */
38332         "panelremoved" : true,
38333         /**
38334          * @event beforecollapse
38335          * Fires when this region before collapse.
38336          * @param {Roo.LayoutRegion} this
38337          */
38338         "beforecollapse" : true,
38339         /**
38340          * @event collapsed
38341          * Fires when this region is collapsed.
38342          * @param {Roo.LayoutRegion} this
38343          */
38344         "collapsed" : true,
38345         /**
38346          * @event expanded
38347          * Fires when this region is expanded.
38348          * @param {Roo.LayoutRegion} this
38349          */
38350         "expanded" : true,
38351         /**
38352          * @event slideshow
38353          * Fires when this region is slid into view.
38354          * @param {Roo.LayoutRegion} this
38355          */
38356         "slideshow" : true,
38357         /**
38358          * @event slidehide
38359          * Fires when this region slides out of view. 
38360          * @param {Roo.LayoutRegion} this
38361          */
38362         "slidehide" : true,
38363         /**
38364          * @event panelactivated
38365          * Fires when a panel is activated. 
38366          * @param {Roo.LayoutRegion} this
38367          * @param {Roo.ContentPanel} panel The activated panel
38368          */
38369         "panelactivated" : true,
38370         /**
38371          * @event resized
38372          * Fires when the user resizes this region. 
38373          * @param {Roo.LayoutRegion} this
38374          * @param {Number} newSize The new size (width for east/west, height for north/south)
38375          */
38376         "resized" : true
38377     };
38378     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38379     this.panels = new Roo.util.MixedCollection();
38380     this.panels.getKey = this.getPanelId.createDelegate(this);
38381     this.box = null;
38382     this.activePanel = null;
38383     // ensure listeners are added...
38384     
38385     if (config.listeners || config.events) {
38386         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38387             listeners : config.listeners || {},
38388             events : config.events || {}
38389         });
38390     }
38391     
38392     if(skipConfig !== true){
38393         this.applyConfig(config);
38394     }
38395 };
38396
38397 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38398 {
38399     getPanelId : function(p){
38400         return p.getId();
38401     },
38402     
38403     applyConfig : function(config){
38404         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38405         this.config = config;
38406         
38407     },
38408     
38409     /**
38410      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38411      * the width, for horizontal (north, south) the height.
38412      * @param {Number} newSize The new width or height
38413      */
38414     resizeTo : function(newSize){
38415         var el = this.el ? this.el :
38416                  (this.activePanel ? this.activePanel.getEl() : null);
38417         if(el){
38418             switch(this.position){
38419                 case "east":
38420                 case "west":
38421                     el.setWidth(newSize);
38422                     this.fireEvent("resized", this, newSize);
38423                 break;
38424                 case "north":
38425                 case "south":
38426                     el.setHeight(newSize);
38427                     this.fireEvent("resized", this, newSize);
38428                 break;                
38429             }
38430         }
38431     },
38432     
38433     getBox : function(){
38434         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38435     },
38436     
38437     getMargins : function(){
38438         return this.margins;
38439     },
38440     
38441     updateBox : function(box){
38442         this.box = box;
38443         var el = this.activePanel.getEl();
38444         el.dom.style.left = box.x + "px";
38445         el.dom.style.top = box.y + "px";
38446         this.activePanel.setSize(box.width, box.height);
38447     },
38448     
38449     /**
38450      * Returns the container element for this region.
38451      * @return {Roo.Element}
38452      */
38453     getEl : function(){
38454         return this.activePanel;
38455     },
38456     
38457     /**
38458      * Returns true if this region is currently visible.
38459      * @return {Boolean}
38460      */
38461     isVisible : function(){
38462         return this.activePanel ? true : false;
38463     },
38464     
38465     setActivePanel : function(panel){
38466         panel = this.getPanel(panel);
38467         if(this.activePanel && this.activePanel != panel){
38468             this.activePanel.setActiveState(false);
38469             this.activePanel.getEl().setLeftTop(-10000,-10000);
38470         }
38471         this.activePanel = panel;
38472         panel.setActiveState(true);
38473         if(this.box){
38474             panel.setSize(this.box.width, this.box.height);
38475         }
38476         this.fireEvent("panelactivated", this, panel);
38477         this.fireEvent("invalidated");
38478     },
38479     
38480     /**
38481      * Show the specified panel.
38482      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38483      * @return {Roo.ContentPanel} The shown panel or null
38484      */
38485     showPanel : function(panel){
38486         panel = this.getPanel(panel);
38487         if(panel){
38488             this.setActivePanel(panel);
38489         }
38490         return panel;
38491     },
38492     
38493     /**
38494      * Get the active panel for this region.
38495      * @return {Roo.ContentPanel} The active panel or null
38496      */
38497     getActivePanel : function(){
38498         return this.activePanel;
38499     },
38500     
38501     /**
38502      * Add the passed ContentPanel(s)
38503      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38504      * @return {Roo.ContentPanel} The panel added (if only one was added)
38505      */
38506     add : function(panel){
38507         if(arguments.length > 1){
38508             for(var i = 0, len = arguments.length; i < len; i++) {
38509                 this.add(arguments[i]);
38510             }
38511             return null;
38512         }
38513         if(this.hasPanel(panel)){
38514             this.showPanel(panel);
38515             return panel;
38516         }
38517         var el = panel.getEl();
38518         if(el.dom.parentNode != this.mgr.el.dom){
38519             this.mgr.el.dom.appendChild(el.dom);
38520         }
38521         if(panel.setRegion){
38522             panel.setRegion(this);
38523         }
38524         this.panels.add(panel);
38525         el.setStyle("position", "absolute");
38526         if(!panel.background){
38527             this.setActivePanel(panel);
38528             if(this.config.initialSize && this.panels.getCount()==1){
38529                 this.resizeTo(this.config.initialSize);
38530             }
38531         }
38532         this.fireEvent("paneladded", this, panel);
38533         return panel;
38534     },
38535     
38536     /**
38537      * Returns true if the panel is in this region.
38538      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38539      * @return {Boolean}
38540      */
38541     hasPanel : function(panel){
38542         if(typeof panel == "object"){ // must be panel obj
38543             panel = panel.getId();
38544         }
38545         return this.getPanel(panel) ? true : false;
38546     },
38547     
38548     /**
38549      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38550      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38551      * @param {Boolean} preservePanel Overrides the config preservePanel option
38552      * @return {Roo.ContentPanel} The panel that was removed
38553      */
38554     remove : function(panel, preservePanel){
38555         panel = this.getPanel(panel);
38556         if(!panel){
38557             return null;
38558         }
38559         var e = {};
38560         this.fireEvent("beforeremove", this, panel, e);
38561         if(e.cancel === true){
38562             return null;
38563         }
38564         var panelId = panel.getId();
38565         this.panels.removeKey(panelId);
38566         return panel;
38567     },
38568     
38569     /**
38570      * Returns the panel specified or null if it's not in this region.
38571      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38572      * @return {Roo.ContentPanel}
38573      */
38574     getPanel : function(id){
38575         if(typeof id == "object"){ // must be panel obj
38576             return id;
38577         }
38578         return this.panels.get(id);
38579     },
38580     
38581     /**
38582      * Returns this regions position (north/south/east/west/center).
38583      * @return {String} 
38584      */
38585     getPosition: function(){
38586         return this.position;    
38587     }
38588 });/*
38589  * Based on:
38590  * Ext JS Library 1.1.1
38591  * Copyright(c) 2006-2007, Ext JS, LLC.
38592  *
38593  * Originally Released Under LGPL - original licence link has changed is not relivant.
38594  *
38595  * Fork - LGPL
38596  * <script type="text/javascript">
38597  */
38598  
38599 /**
38600  * @class Roo.bootstrap.layout.Region
38601  * @extends Roo.bootstrap.layout.Basic
38602  * This class represents a region in a layout manager.
38603  
38604  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38605  * @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})
38606  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38607  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38608  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38609  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38610  * @cfg {String}    title           The title for the region (overrides panel titles)
38611  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38612  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38613  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38614  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38615  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38616  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38617  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38618  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38619  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38620  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38621
38622  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38623  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38624  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38625  * @cfg {Number}    width           For East/West panels
38626  * @cfg {Number}    height          For North/South panels
38627  * @cfg {Boolean}   split           To show the splitter
38628  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38629  * 
38630  * @cfg {string}   cls             Extra CSS classes to add to region
38631  * 
38632  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38633  * @cfg {string}   region  the region that it inhabits..
38634  *
38635
38636  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38637  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38638
38639  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38640  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38641  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38642  */
38643 Roo.bootstrap.layout.Region = function(config)
38644 {
38645     this.applyConfig(config);
38646
38647     var mgr = config.mgr;
38648     var pos = config.region;
38649     config.skipConfig = true;
38650     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38651     
38652     if (mgr.el) {
38653         this.onRender(mgr.el);   
38654     }
38655      
38656     this.visible = true;
38657     this.collapsed = false;
38658     this.unrendered_panels = [];
38659 };
38660
38661 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38662
38663     position: '', // set by wrapper (eg. north/south etc..)
38664     unrendered_panels : null,  // unrendered panels.
38665     
38666     tabPosition : false,
38667     
38668     mgr: false, // points to 'Border'
38669     
38670     
38671     createBody : function(){
38672         /** This region's body element 
38673         * @type Roo.Element */
38674         this.bodyEl = this.el.createChild({
38675                 tag: "div",
38676                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38677         });
38678     },
38679
38680     onRender: function(ctr, pos)
38681     {
38682         var dh = Roo.DomHelper;
38683         /** This region's container element 
38684         * @type Roo.Element */
38685         this.el = dh.append(ctr.dom, {
38686                 tag: "div",
38687                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38688             }, true);
38689         /** This region's title element 
38690         * @type Roo.Element */
38691     
38692         this.titleEl = dh.append(this.el.dom,  {
38693                 tag: "div",
38694                 unselectable: "on",
38695                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38696                 children:[
38697                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38698                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38699                 ]
38700             }, true);
38701         
38702         this.titleEl.enableDisplayMode();
38703         /** This region's title text element 
38704         * @type HTMLElement */
38705         this.titleTextEl = this.titleEl.dom.firstChild;
38706         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38707         /*
38708         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38709         this.closeBtn.enableDisplayMode();
38710         this.closeBtn.on("click", this.closeClicked, this);
38711         this.closeBtn.hide();
38712     */
38713         this.createBody(this.config);
38714         if(this.config.hideWhenEmpty){
38715             this.hide();
38716             this.on("paneladded", this.validateVisibility, this);
38717             this.on("panelremoved", this.validateVisibility, this);
38718         }
38719         if(this.autoScroll){
38720             this.bodyEl.setStyle("overflow", "auto");
38721         }else{
38722             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38723         }
38724         //if(c.titlebar !== false){
38725             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38726                 this.titleEl.hide();
38727             }else{
38728                 this.titleEl.show();
38729                 if(this.config.title){
38730                     this.titleTextEl.innerHTML = this.config.title;
38731                 }
38732             }
38733         //}
38734         if(this.config.collapsed){
38735             this.collapse(true);
38736         }
38737         if(this.config.hidden){
38738             this.hide();
38739         }
38740         
38741         if (this.unrendered_panels && this.unrendered_panels.length) {
38742             for (var i =0;i< this.unrendered_panels.length; i++) {
38743                 this.add(this.unrendered_panels[i]);
38744             }
38745             this.unrendered_panels = null;
38746             
38747         }
38748         
38749     },
38750     
38751     applyConfig : function(c)
38752     {
38753         /*
38754          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38755             var dh = Roo.DomHelper;
38756             if(c.titlebar !== false){
38757                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38758                 this.collapseBtn.on("click", this.collapse, this);
38759                 this.collapseBtn.enableDisplayMode();
38760                 /*
38761                 if(c.showPin === true || this.showPin){
38762                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38763                     this.stickBtn.enableDisplayMode();
38764                     this.stickBtn.on("click", this.expand, this);
38765                     this.stickBtn.hide();
38766                 }
38767                 
38768             }
38769             */
38770             /** This region's collapsed element
38771             * @type Roo.Element */
38772             /*
38773              *
38774             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38775                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38776             ]}, true);
38777             
38778             if(c.floatable !== false){
38779                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38780                this.collapsedEl.on("click", this.collapseClick, this);
38781             }
38782
38783             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38784                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38785                    id: "message", unselectable: "on", style:{"float":"left"}});
38786                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38787              }
38788             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38789             this.expandBtn.on("click", this.expand, this);
38790             
38791         }
38792         
38793         if(this.collapseBtn){
38794             this.collapseBtn.setVisible(c.collapsible == true);
38795         }
38796         
38797         this.cmargins = c.cmargins || this.cmargins ||
38798                          (this.position == "west" || this.position == "east" ?
38799                              {top: 0, left: 2, right:2, bottom: 0} :
38800                              {top: 2, left: 0, right:0, bottom: 2});
38801         */
38802         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38803         
38804         
38805         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38806         
38807         this.autoScroll = c.autoScroll || false;
38808         
38809         
38810        
38811         
38812         this.duration = c.duration || .30;
38813         this.slideDuration = c.slideDuration || .45;
38814         this.config = c;
38815        
38816     },
38817     /**
38818      * Returns true if this region is currently visible.
38819      * @return {Boolean}
38820      */
38821     isVisible : function(){
38822         return this.visible;
38823     },
38824
38825     /**
38826      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38827      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38828      */
38829     //setCollapsedTitle : function(title){
38830     //    title = title || "&#160;";
38831      //   if(this.collapsedTitleTextEl){
38832       //      this.collapsedTitleTextEl.innerHTML = title;
38833        // }
38834     //},
38835
38836     getBox : function(){
38837         var b;
38838       //  if(!this.collapsed){
38839             b = this.el.getBox(false, true);
38840        // }else{
38841           //  b = this.collapsedEl.getBox(false, true);
38842         //}
38843         return b;
38844     },
38845
38846     getMargins : function(){
38847         return this.margins;
38848         //return this.collapsed ? this.cmargins : this.margins;
38849     },
38850 /*
38851     highlight : function(){
38852         this.el.addClass("x-layout-panel-dragover");
38853     },
38854
38855     unhighlight : function(){
38856         this.el.removeClass("x-layout-panel-dragover");
38857     },
38858 */
38859     updateBox : function(box)
38860     {
38861         if (!this.bodyEl) {
38862             return; // not rendered yet..
38863         }
38864         
38865         this.box = box;
38866         if(!this.collapsed){
38867             this.el.dom.style.left = box.x + "px";
38868             this.el.dom.style.top = box.y + "px";
38869             this.updateBody(box.width, box.height);
38870         }else{
38871             this.collapsedEl.dom.style.left = box.x + "px";
38872             this.collapsedEl.dom.style.top = box.y + "px";
38873             this.collapsedEl.setSize(box.width, box.height);
38874         }
38875         if(this.tabs){
38876             this.tabs.autoSizeTabs();
38877         }
38878     },
38879
38880     updateBody : function(w, h)
38881     {
38882         if(w !== null){
38883             this.el.setWidth(w);
38884             w -= this.el.getBorderWidth("rl");
38885             if(this.config.adjustments){
38886                 w += this.config.adjustments[0];
38887             }
38888         }
38889         if(h !== null && h > 0){
38890             this.el.setHeight(h);
38891             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38892             h -= this.el.getBorderWidth("tb");
38893             if(this.config.adjustments){
38894                 h += this.config.adjustments[1];
38895             }
38896             this.bodyEl.setHeight(h);
38897             if(this.tabs){
38898                 h = this.tabs.syncHeight(h);
38899             }
38900         }
38901         if(this.panelSize){
38902             w = w !== null ? w : this.panelSize.width;
38903             h = h !== null ? h : this.panelSize.height;
38904         }
38905         if(this.activePanel){
38906             var el = this.activePanel.getEl();
38907             w = w !== null ? w : el.getWidth();
38908             h = h !== null ? h : el.getHeight();
38909             this.panelSize = {width: w, height: h};
38910             this.activePanel.setSize(w, h);
38911         }
38912         if(Roo.isIE && this.tabs){
38913             this.tabs.el.repaint();
38914         }
38915     },
38916
38917     /**
38918      * Returns the container element for this region.
38919      * @return {Roo.Element}
38920      */
38921     getEl : function(){
38922         return this.el;
38923     },
38924
38925     /**
38926      * Hides this region.
38927      */
38928     hide : function(){
38929         //if(!this.collapsed){
38930             this.el.dom.style.left = "-2000px";
38931             this.el.hide();
38932         //}else{
38933          //   this.collapsedEl.dom.style.left = "-2000px";
38934          //   this.collapsedEl.hide();
38935        // }
38936         this.visible = false;
38937         this.fireEvent("visibilitychange", this, false);
38938     },
38939
38940     /**
38941      * Shows this region if it was previously hidden.
38942      */
38943     show : function(){
38944         //if(!this.collapsed){
38945             this.el.show();
38946         //}else{
38947         //    this.collapsedEl.show();
38948        // }
38949         this.visible = true;
38950         this.fireEvent("visibilitychange", this, true);
38951     },
38952 /*
38953     closeClicked : function(){
38954         if(this.activePanel){
38955             this.remove(this.activePanel);
38956         }
38957     },
38958
38959     collapseClick : function(e){
38960         if(this.isSlid){
38961            e.stopPropagation();
38962            this.slideIn();
38963         }else{
38964            e.stopPropagation();
38965            this.slideOut();
38966         }
38967     },
38968 */
38969     /**
38970      * Collapses this region.
38971      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38972      */
38973     /*
38974     collapse : function(skipAnim, skipCheck = false){
38975         if(this.collapsed) {
38976             return;
38977         }
38978         
38979         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38980             
38981             this.collapsed = true;
38982             if(this.split){
38983                 this.split.el.hide();
38984             }
38985             if(this.config.animate && skipAnim !== true){
38986                 this.fireEvent("invalidated", this);
38987                 this.animateCollapse();
38988             }else{
38989                 this.el.setLocation(-20000,-20000);
38990                 this.el.hide();
38991                 this.collapsedEl.show();
38992                 this.fireEvent("collapsed", this);
38993                 this.fireEvent("invalidated", this);
38994             }
38995         }
38996         
38997     },
38998 */
38999     animateCollapse : function(){
39000         // overridden
39001     },
39002
39003     /**
39004      * Expands this region if it was previously collapsed.
39005      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39006      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39007      */
39008     /*
39009     expand : function(e, skipAnim){
39010         if(e) {
39011             e.stopPropagation();
39012         }
39013         if(!this.collapsed || this.el.hasActiveFx()) {
39014             return;
39015         }
39016         if(this.isSlid){
39017             this.afterSlideIn();
39018             skipAnim = true;
39019         }
39020         this.collapsed = false;
39021         if(this.config.animate && skipAnim !== true){
39022             this.animateExpand();
39023         }else{
39024             this.el.show();
39025             if(this.split){
39026                 this.split.el.show();
39027             }
39028             this.collapsedEl.setLocation(-2000,-2000);
39029             this.collapsedEl.hide();
39030             this.fireEvent("invalidated", this);
39031             this.fireEvent("expanded", this);
39032         }
39033     },
39034 */
39035     animateExpand : function(){
39036         // overridden
39037     },
39038
39039     initTabs : function()
39040     {
39041         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39042         
39043         var ts = new Roo.bootstrap.panel.Tabs({
39044             el: this.bodyEl.dom,
39045             region : this,
39046             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39047             disableTooltips: this.config.disableTabTips,
39048             toolbar : this.config.toolbar
39049         });
39050         
39051         if(this.config.hideTabs){
39052             ts.stripWrap.setDisplayed(false);
39053         }
39054         this.tabs = ts;
39055         ts.resizeTabs = this.config.resizeTabs === true;
39056         ts.minTabWidth = this.config.minTabWidth || 40;
39057         ts.maxTabWidth = this.config.maxTabWidth || 250;
39058         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39059         ts.monitorResize = false;
39060         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39061         ts.bodyEl.addClass('roo-layout-tabs-body');
39062         this.panels.each(this.initPanelAsTab, this);
39063     },
39064
39065     initPanelAsTab : function(panel){
39066         var ti = this.tabs.addTab(
39067             panel.getEl().id,
39068             panel.getTitle(),
39069             null,
39070             this.config.closeOnTab && panel.isClosable(),
39071             panel.tpl
39072         );
39073         if(panel.tabTip !== undefined){
39074             ti.setTooltip(panel.tabTip);
39075         }
39076         ti.on("activate", function(){
39077               this.setActivePanel(panel);
39078         }, this);
39079         
39080         if(this.config.closeOnTab){
39081             ti.on("beforeclose", function(t, e){
39082                 e.cancel = true;
39083                 this.remove(panel);
39084             }, this);
39085         }
39086         
39087         panel.tabItem = ti;
39088         
39089         return ti;
39090     },
39091
39092     updatePanelTitle : function(panel, title)
39093     {
39094         if(this.activePanel == panel){
39095             this.updateTitle(title);
39096         }
39097         if(this.tabs){
39098             var ti = this.tabs.getTab(panel.getEl().id);
39099             ti.setText(title);
39100             if(panel.tabTip !== undefined){
39101                 ti.setTooltip(panel.tabTip);
39102             }
39103         }
39104     },
39105
39106     updateTitle : function(title){
39107         if(this.titleTextEl && !this.config.title){
39108             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39109         }
39110     },
39111
39112     setActivePanel : function(panel)
39113     {
39114         panel = this.getPanel(panel);
39115         if(this.activePanel && this.activePanel != panel){
39116             if(this.activePanel.setActiveState(false) === false){
39117                 return;
39118             }
39119         }
39120         this.activePanel = panel;
39121         panel.setActiveState(true);
39122         if(this.panelSize){
39123             panel.setSize(this.panelSize.width, this.panelSize.height);
39124         }
39125         if(this.closeBtn){
39126             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39127         }
39128         this.updateTitle(panel.getTitle());
39129         if(this.tabs){
39130             this.fireEvent("invalidated", this);
39131         }
39132         this.fireEvent("panelactivated", this, panel);
39133     },
39134
39135     /**
39136      * Shows the specified panel.
39137      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39138      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39139      */
39140     showPanel : function(panel)
39141     {
39142         panel = this.getPanel(panel);
39143         if(panel){
39144             if(this.tabs){
39145                 var tab = this.tabs.getTab(panel.getEl().id);
39146                 if(tab.isHidden()){
39147                     this.tabs.unhideTab(tab.id);
39148                 }
39149                 tab.activate();
39150             }else{
39151                 this.setActivePanel(panel);
39152             }
39153         }
39154         return panel;
39155     },
39156
39157     /**
39158      * Get the active panel for this region.
39159      * @return {Roo.ContentPanel} The active panel or null
39160      */
39161     getActivePanel : function(){
39162         return this.activePanel;
39163     },
39164
39165     validateVisibility : function(){
39166         if(this.panels.getCount() < 1){
39167             this.updateTitle("&#160;");
39168             this.closeBtn.hide();
39169             this.hide();
39170         }else{
39171             if(!this.isVisible()){
39172                 this.show();
39173             }
39174         }
39175     },
39176
39177     /**
39178      * Adds the passed ContentPanel(s) to this region.
39179      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39180      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39181      */
39182     add : function(panel)
39183     {
39184         if(arguments.length > 1){
39185             for(var i = 0, len = arguments.length; i < len; i++) {
39186                 this.add(arguments[i]);
39187             }
39188             return null;
39189         }
39190         
39191         // if we have not been rendered yet, then we can not really do much of this..
39192         if (!this.bodyEl) {
39193             this.unrendered_panels.push(panel);
39194             return panel;
39195         }
39196         
39197         
39198         
39199         
39200         if(this.hasPanel(panel)){
39201             this.showPanel(panel);
39202             return panel;
39203         }
39204         panel.setRegion(this);
39205         this.panels.add(panel);
39206        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39207             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39208             // and hide them... ???
39209             this.bodyEl.dom.appendChild(panel.getEl().dom);
39210             if(panel.background !== true){
39211                 this.setActivePanel(panel);
39212             }
39213             this.fireEvent("paneladded", this, panel);
39214             return panel;
39215         }
39216         */
39217         if(!this.tabs){
39218             this.initTabs();
39219         }else{
39220             this.initPanelAsTab(panel);
39221         }
39222         
39223         
39224         if(panel.background !== true){
39225             this.tabs.activate(panel.getEl().id);
39226         }
39227         this.fireEvent("paneladded", this, panel);
39228         return panel;
39229     },
39230
39231     /**
39232      * Hides the tab for the specified panel.
39233      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39234      */
39235     hidePanel : function(panel){
39236         if(this.tabs && (panel = this.getPanel(panel))){
39237             this.tabs.hideTab(panel.getEl().id);
39238         }
39239     },
39240
39241     /**
39242      * Unhides the tab for a previously hidden panel.
39243      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39244      */
39245     unhidePanel : function(panel){
39246         if(this.tabs && (panel = this.getPanel(panel))){
39247             this.tabs.unhideTab(panel.getEl().id);
39248         }
39249     },
39250
39251     clearPanels : function(){
39252         while(this.panels.getCount() > 0){
39253              this.remove(this.panels.first());
39254         }
39255     },
39256
39257     /**
39258      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39259      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39260      * @param {Boolean} preservePanel Overrides the config preservePanel option
39261      * @return {Roo.ContentPanel} The panel that was removed
39262      */
39263     remove : function(panel, preservePanel)
39264     {
39265         panel = this.getPanel(panel);
39266         if(!panel){
39267             return null;
39268         }
39269         var e = {};
39270         this.fireEvent("beforeremove", this, panel, e);
39271         if(e.cancel === true){
39272             return null;
39273         }
39274         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39275         var panelId = panel.getId();
39276         this.panels.removeKey(panelId);
39277         if(preservePanel){
39278             document.body.appendChild(panel.getEl().dom);
39279         }
39280         if(this.tabs){
39281             this.tabs.removeTab(panel.getEl().id);
39282         }else if (!preservePanel){
39283             this.bodyEl.dom.removeChild(panel.getEl().dom);
39284         }
39285         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39286             var p = this.panels.first();
39287             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39288             tempEl.appendChild(p.getEl().dom);
39289             this.bodyEl.update("");
39290             this.bodyEl.dom.appendChild(p.getEl().dom);
39291             tempEl = null;
39292             this.updateTitle(p.getTitle());
39293             this.tabs = null;
39294             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39295             this.setActivePanel(p);
39296         }
39297         panel.setRegion(null);
39298         if(this.activePanel == panel){
39299             this.activePanel = null;
39300         }
39301         if(this.config.autoDestroy !== false && preservePanel !== true){
39302             try{panel.destroy();}catch(e){}
39303         }
39304         this.fireEvent("panelremoved", this, panel);
39305         return panel;
39306     },
39307
39308     /**
39309      * Returns the TabPanel component used by this region
39310      * @return {Roo.TabPanel}
39311      */
39312     getTabs : function(){
39313         return this.tabs;
39314     },
39315
39316     createTool : function(parentEl, className){
39317         var btn = Roo.DomHelper.append(parentEl, {
39318             tag: "div",
39319             cls: "x-layout-tools-button",
39320             children: [ {
39321                 tag: "div",
39322                 cls: "roo-layout-tools-button-inner " + className,
39323                 html: "&#160;"
39324             }]
39325         }, true);
39326         btn.addClassOnOver("roo-layout-tools-button-over");
39327         return btn;
39328     }
39329 });/*
39330  * Based on:
39331  * Ext JS Library 1.1.1
39332  * Copyright(c) 2006-2007, Ext JS, LLC.
39333  *
39334  * Originally Released Under LGPL - original licence link has changed is not relivant.
39335  *
39336  * Fork - LGPL
39337  * <script type="text/javascript">
39338  */
39339  
39340
39341
39342 /**
39343  * @class Roo.SplitLayoutRegion
39344  * @extends Roo.LayoutRegion
39345  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39346  */
39347 Roo.bootstrap.layout.Split = function(config){
39348     this.cursor = config.cursor;
39349     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39350 };
39351
39352 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39353 {
39354     splitTip : "Drag to resize.",
39355     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39356     useSplitTips : false,
39357
39358     applyConfig : function(config){
39359         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39360     },
39361     
39362     onRender : function(ctr,pos) {
39363         
39364         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39365         if(!this.config.split){
39366             return;
39367         }
39368         if(!this.split){
39369             
39370             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39371                             tag: "div",
39372                             id: this.el.id + "-split",
39373                             cls: "roo-layout-split roo-layout-split-"+this.position,
39374                             html: "&#160;"
39375             });
39376             /** The SplitBar for this region 
39377             * @type Roo.SplitBar */
39378             // does not exist yet...
39379             Roo.log([this.position, this.orientation]);
39380             
39381             this.split = new Roo.bootstrap.SplitBar({
39382                 dragElement : splitEl,
39383                 resizingElement: this.el,
39384                 orientation : this.orientation
39385             });
39386             
39387             this.split.on("moved", this.onSplitMove, this);
39388             this.split.useShim = this.config.useShim === true;
39389             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39390             if(this.useSplitTips){
39391                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39392             }
39393             //if(config.collapsible){
39394             //    this.split.el.on("dblclick", this.collapse,  this);
39395             //}
39396         }
39397         if(typeof this.config.minSize != "undefined"){
39398             this.split.minSize = this.config.minSize;
39399         }
39400         if(typeof this.config.maxSize != "undefined"){
39401             this.split.maxSize = this.config.maxSize;
39402         }
39403         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39404             this.hideSplitter();
39405         }
39406         
39407     },
39408
39409     getHMaxSize : function(){
39410          var cmax = this.config.maxSize || 10000;
39411          var center = this.mgr.getRegion("center");
39412          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39413     },
39414
39415     getVMaxSize : function(){
39416          var cmax = this.config.maxSize || 10000;
39417          var center = this.mgr.getRegion("center");
39418          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39419     },
39420
39421     onSplitMove : function(split, newSize){
39422         this.fireEvent("resized", this, newSize);
39423     },
39424     
39425     /** 
39426      * Returns the {@link Roo.SplitBar} for this region.
39427      * @return {Roo.SplitBar}
39428      */
39429     getSplitBar : function(){
39430         return this.split;
39431     },
39432     
39433     hide : function(){
39434         this.hideSplitter();
39435         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39436     },
39437
39438     hideSplitter : function(){
39439         if(this.split){
39440             this.split.el.setLocation(-2000,-2000);
39441             this.split.el.hide();
39442         }
39443     },
39444
39445     show : function(){
39446         if(this.split){
39447             this.split.el.show();
39448         }
39449         Roo.bootstrap.layout.Split.superclass.show.call(this);
39450     },
39451     
39452     beforeSlide: function(){
39453         if(Roo.isGecko){// firefox overflow auto bug workaround
39454             this.bodyEl.clip();
39455             if(this.tabs) {
39456                 this.tabs.bodyEl.clip();
39457             }
39458             if(this.activePanel){
39459                 this.activePanel.getEl().clip();
39460                 
39461                 if(this.activePanel.beforeSlide){
39462                     this.activePanel.beforeSlide();
39463                 }
39464             }
39465         }
39466     },
39467     
39468     afterSlide : function(){
39469         if(Roo.isGecko){// firefox overflow auto bug workaround
39470             this.bodyEl.unclip();
39471             if(this.tabs) {
39472                 this.tabs.bodyEl.unclip();
39473             }
39474             if(this.activePanel){
39475                 this.activePanel.getEl().unclip();
39476                 if(this.activePanel.afterSlide){
39477                     this.activePanel.afterSlide();
39478                 }
39479             }
39480         }
39481     },
39482
39483     initAutoHide : function(){
39484         if(this.autoHide !== false){
39485             if(!this.autoHideHd){
39486                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39487                 this.autoHideHd = {
39488                     "mouseout": function(e){
39489                         if(!e.within(this.el, true)){
39490                             st.delay(500);
39491                         }
39492                     },
39493                     "mouseover" : function(e){
39494                         st.cancel();
39495                     },
39496                     scope : this
39497                 };
39498             }
39499             this.el.on(this.autoHideHd);
39500         }
39501     },
39502
39503     clearAutoHide : function(){
39504         if(this.autoHide !== false){
39505             this.el.un("mouseout", this.autoHideHd.mouseout);
39506             this.el.un("mouseover", this.autoHideHd.mouseover);
39507         }
39508     },
39509
39510     clearMonitor : function(){
39511         Roo.get(document).un("click", this.slideInIf, this);
39512     },
39513
39514     // these names are backwards but not changed for compat
39515     slideOut : function(){
39516         if(this.isSlid || this.el.hasActiveFx()){
39517             return;
39518         }
39519         this.isSlid = true;
39520         if(this.collapseBtn){
39521             this.collapseBtn.hide();
39522         }
39523         this.closeBtnState = this.closeBtn.getStyle('display');
39524         this.closeBtn.hide();
39525         if(this.stickBtn){
39526             this.stickBtn.show();
39527         }
39528         this.el.show();
39529         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39530         this.beforeSlide();
39531         this.el.setStyle("z-index", 10001);
39532         this.el.slideIn(this.getSlideAnchor(), {
39533             callback: function(){
39534                 this.afterSlide();
39535                 this.initAutoHide();
39536                 Roo.get(document).on("click", this.slideInIf, this);
39537                 this.fireEvent("slideshow", this);
39538             },
39539             scope: this,
39540             block: true
39541         });
39542     },
39543
39544     afterSlideIn : function(){
39545         this.clearAutoHide();
39546         this.isSlid = false;
39547         this.clearMonitor();
39548         this.el.setStyle("z-index", "");
39549         if(this.collapseBtn){
39550             this.collapseBtn.show();
39551         }
39552         this.closeBtn.setStyle('display', this.closeBtnState);
39553         if(this.stickBtn){
39554             this.stickBtn.hide();
39555         }
39556         this.fireEvent("slidehide", this);
39557     },
39558
39559     slideIn : function(cb){
39560         if(!this.isSlid || this.el.hasActiveFx()){
39561             Roo.callback(cb);
39562             return;
39563         }
39564         this.isSlid = false;
39565         this.beforeSlide();
39566         this.el.slideOut(this.getSlideAnchor(), {
39567             callback: function(){
39568                 this.el.setLeftTop(-10000, -10000);
39569                 this.afterSlide();
39570                 this.afterSlideIn();
39571                 Roo.callback(cb);
39572             },
39573             scope: this,
39574             block: true
39575         });
39576     },
39577     
39578     slideInIf : function(e){
39579         if(!e.within(this.el)){
39580             this.slideIn();
39581         }
39582     },
39583
39584     animateCollapse : function(){
39585         this.beforeSlide();
39586         this.el.setStyle("z-index", 20000);
39587         var anchor = this.getSlideAnchor();
39588         this.el.slideOut(anchor, {
39589             callback : function(){
39590                 this.el.setStyle("z-index", "");
39591                 this.collapsedEl.slideIn(anchor, {duration:.3});
39592                 this.afterSlide();
39593                 this.el.setLocation(-10000,-10000);
39594                 this.el.hide();
39595                 this.fireEvent("collapsed", this);
39596             },
39597             scope: this,
39598             block: true
39599         });
39600     },
39601
39602     animateExpand : function(){
39603         this.beforeSlide();
39604         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39605         this.el.setStyle("z-index", 20000);
39606         this.collapsedEl.hide({
39607             duration:.1
39608         });
39609         this.el.slideIn(this.getSlideAnchor(), {
39610             callback : function(){
39611                 this.el.setStyle("z-index", "");
39612                 this.afterSlide();
39613                 if(this.split){
39614                     this.split.el.show();
39615                 }
39616                 this.fireEvent("invalidated", this);
39617                 this.fireEvent("expanded", this);
39618             },
39619             scope: this,
39620             block: true
39621         });
39622     },
39623
39624     anchors : {
39625         "west" : "left",
39626         "east" : "right",
39627         "north" : "top",
39628         "south" : "bottom"
39629     },
39630
39631     sanchors : {
39632         "west" : "l",
39633         "east" : "r",
39634         "north" : "t",
39635         "south" : "b"
39636     },
39637
39638     canchors : {
39639         "west" : "tl-tr",
39640         "east" : "tr-tl",
39641         "north" : "tl-bl",
39642         "south" : "bl-tl"
39643     },
39644
39645     getAnchor : function(){
39646         return this.anchors[this.position];
39647     },
39648
39649     getCollapseAnchor : function(){
39650         return this.canchors[this.position];
39651     },
39652
39653     getSlideAnchor : function(){
39654         return this.sanchors[this.position];
39655     },
39656
39657     getAlignAdj : function(){
39658         var cm = this.cmargins;
39659         switch(this.position){
39660             case "west":
39661                 return [0, 0];
39662             break;
39663             case "east":
39664                 return [0, 0];
39665             break;
39666             case "north":
39667                 return [0, 0];
39668             break;
39669             case "south":
39670                 return [0, 0];
39671             break;
39672         }
39673     },
39674
39675     getExpandAdj : function(){
39676         var c = this.collapsedEl, cm = this.cmargins;
39677         switch(this.position){
39678             case "west":
39679                 return [-(cm.right+c.getWidth()+cm.left), 0];
39680             break;
39681             case "east":
39682                 return [cm.right+c.getWidth()+cm.left, 0];
39683             break;
39684             case "north":
39685                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39686             break;
39687             case "south":
39688                 return [0, cm.top+cm.bottom+c.getHeight()];
39689             break;
39690         }
39691     }
39692 });/*
39693  * Based on:
39694  * Ext JS Library 1.1.1
39695  * Copyright(c) 2006-2007, Ext JS, LLC.
39696  *
39697  * Originally Released Under LGPL - original licence link has changed is not relivant.
39698  *
39699  * Fork - LGPL
39700  * <script type="text/javascript">
39701  */
39702 /*
39703  * These classes are private internal classes
39704  */
39705 Roo.bootstrap.layout.Center = function(config){
39706     config.region = "center";
39707     Roo.bootstrap.layout.Region.call(this, config);
39708     this.visible = true;
39709     this.minWidth = config.minWidth || 20;
39710     this.minHeight = config.minHeight || 20;
39711 };
39712
39713 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39714     hide : function(){
39715         // center panel can't be hidden
39716     },
39717     
39718     show : function(){
39719         // center panel can't be hidden
39720     },
39721     
39722     getMinWidth: function(){
39723         return this.minWidth;
39724     },
39725     
39726     getMinHeight: function(){
39727         return this.minHeight;
39728     }
39729 });
39730
39731
39732
39733
39734  
39735
39736
39737
39738
39739
39740
39741 Roo.bootstrap.layout.North = function(config)
39742 {
39743     config.region = 'north';
39744     config.cursor = 'n-resize';
39745     
39746     Roo.bootstrap.layout.Split.call(this, config);
39747     
39748     
39749     if(this.split){
39750         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39751         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39752         this.split.el.addClass("roo-layout-split-v");
39753     }
39754     //var size = config.initialSize || config.height;
39755     //if(this.el && typeof size != "undefined"){
39756     //    this.el.setHeight(size);
39757     //}
39758 };
39759 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39760 {
39761     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39762      
39763      
39764     onRender : function(ctr, pos)
39765     {
39766         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39767         var size = this.config.initialSize || this.config.height;
39768         if(this.el && typeof size != "undefined"){
39769             this.el.setHeight(size);
39770         }
39771     
39772     },
39773     
39774     getBox : function(){
39775         if(this.collapsed){
39776             return this.collapsedEl.getBox();
39777         }
39778         var box = this.el.getBox();
39779         if(this.split){
39780             box.height += this.split.el.getHeight();
39781         }
39782         return box;
39783     },
39784     
39785     updateBox : function(box){
39786         if(this.split && !this.collapsed){
39787             box.height -= this.split.el.getHeight();
39788             this.split.el.setLeft(box.x);
39789             this.split.el.setTop(box.y+box.height);
39790             this.split.el.setWidth(box.width);
39791         }
39792         if(this.collapsed){
39793             this.updateBody(box.width, null);
39794         }
39795         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39796     }
39797 });
39798
39799
39800
39801
39802
39803 Roo.bootstrap.layout.South = function(config){
39804     config.region = 'south';
39805     config.cursor = 's-resize';
39806     Roo.bootstrap.layout.Split.call(this, config);
39807     if(this.split){
39808         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39809         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39810         this.split.el.addClass("roo-layout-split-v");
39811     }
39812     
39813 };
39814
39815 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39816     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39817     
39818     onRender : function(ctr, pos)
39819     {
39820         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39821         var size = this.config.initialSize || this.config.height;
39822         if(this.el && typeof size != "undefined"){
39823             this.el.setHeight(size);
39824         }
39825     
39826     },
39827     
39828     getBox : function(){
39829         if(this.collapsed){
39830             return this.collapsedEl.getBox();
39831         }
39832         var box = this.el.getBox();
39833         if(this.split){
39834             var sh = this.split.el.getHeight();
39835             box.height += sh;
39836             box.y -= sh;
39837         }
39838         return box;
39839     },
39840     
39841     updateBox : function(box){
39842         if(this.split && !this.collapsed){
39843             var sh = this.split.el.getHeight();
39844             box.height -= sh;
39845             box.y += sh;
39846             this.split.el.setLeft(box.x);
39847             this.split.el.setTop(box.y-sh);
39848             this.split.el.setWidth(box.width);
39849         }
39850         if(this.collapsed){
39851             this.updateBody(box.width, null);
39852         }
39853         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39854     }
39855 });
39856
39857 Roo.bootstrap.layout.East = function(config){
39858     config.region = "east";
39859     config.cursor = "e-resize";
39860     Roo.bootstrap.layout.Split.call(this, config);
39861     if(this.split){
39862         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39863         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39864         this.split.el.addClass("roo-layout-split-h");
39865     }
39866     
39867 };
39868 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39869     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39870     
39871     onRender : function(ctr, pos)
39872     {
39873         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39874         var size = this.config.initialSize || this.config.width;
39875         if(this.el && typeof size != "undefined"){
39876             this.el.setWidth(size);
39877         }
39878     
39879     },
39880     
39881     getBox : function(){
39882         if(this.collapsed){
39883             return this.collapsedEl.getBox();
39884         }
39885         var box = this.el.getBox();
39886         if(this.split){
39887             var sw = this.split.el.getWidth();
39888             box.width += sw;
39889             box.x -= sw;
39890         }
39891         return box;
39892     },
39893
39894     updateBox : function(box){
39895         if(this.split && !this.collapsed){
39896             var sw = this.split.el.getWidth();
39897             box.width -= sw;
39898             this.split.el.setLeft(box.x);
39899             this.split.el.setTop(box.y);
39900             this.split.el.setHeight(box.height);
39901             box.x += sw;
39902         }
39903         if(this.collapsed){
39904             this.updateBody(null, box.height);
39905         }
39906         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39907     }
39908 });
39909
39910 Roo.bootstrap.layout.West = function(config){
39911     config.region = "west";
39912     config.cursor = "w-resize";
39913     
39914     Roo.bootstrap.layout.Split.call(this, config);
39915     if(this.split){
39916         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39917         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39918         this.split.el.addClass("roo-layout-split-h");
39919     }
39920     
39921 };
39922 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39923     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39924     
39925     onRender: function(ctr, pos)
39926     {
39927         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39928         var size = this.config.initialSize || this.config.width;
39929         if(typeof size != "undefined"){
39930             this.el.setWidth(size);
39931         }
39932     },
39933     
39934     getBox : function(){
39935         if(this.collapsed){
39936             return this.collapsedEl.getBox();
39937         }
39938         var box = this.el.getBox();
39939         if (box.width == 0) {
39940             box.width = this.config.width; // kludge?
39941         }
39942         if(this.split){
39943             box.width += this.split.el.getWidth();
39944         }
39945         return box;
39946     },
39947     
39948     updateBox : function(box){
39949         if(this.split && !this.collapsed){
39950             var sw = this.split.el.getWidth();
39951             box.width -= sw;
39952             this.split.el.setLeft(box.x+box.width);
39953             this.split.el.setTop(box.y);
39954             this.split.el.setHeight(box.height);
39955         }
39956         if(this.collapsed){
39957             this.updateBody(null, box.height);
39958         }
39959         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39960     }
39961 });Roo.namespace("Roo.bootstrap.panel");/*
39962  * Based on:
39963  * Ext JS Library 1.1.1
39964  * Copyright(c) 2006-2007, Ext JS, LLC.
39965  *
39966  * Originally Released Under LGPL - original licence link has changed is not relivant.
39967  *
39968  * Fork - LGPL
39969  * <script type="text/javascript">
39970  */
39971 /**
39972  * @class Roo.ContentPanel
39973  * @extends Roo.util.Observable
39974  * A basic ContentPanel element.
39975  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39976  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39977  * @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
39978  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39979  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39980  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39981  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39982  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39983  * @cfg {String} title          The title for this panel
39984  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39985  * @cfg {String} url            Calls {@link #setUrl} with this value
39986  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39987  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39988  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39989  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39990  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39991  * @cfg {Boolean} badges render the badges
39992  * @cfg {String} cls  extra classes to use  
39993  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39994
39995  * @constructor
39996  * Create a new ContentPanel.
39997  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39998  * @param {String/Object} config A string to set only the title or a config object
39999  * @param {String} content (optional) Set the HTML content for this panel
40000  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40001  */
40002 Roo.bootstrap.panel.Content = function( config){
40003     
40004     this.tpl = config.tpl || false;
40005     
40006     var el = config.el;
40007     var content = config.content;
40008
40009     if(config.autoCreate){ // xtype is available if this is called from factory
40010         el = Roo.id();
40011     }
40012     this.el = Roo.get(el);
40013     if(!this.el && config && config.autoCreate){
40014         if(typeof config.autoCreate == "object"){
40015             if(!config.autoCreate.id){
40016                 config.autoCreate.id = config.id||el;
40017             }
40018             this.el = Roo.DomHelper.append(document.body,
40019                         config.autoCreate, true);
40020         }else{
40021             var elcfg =  {
40022                 tag: "div",
40023                 cls: (config.cls || '') +
40024                     (config.background ? ' bg-' + config.background : '') +
40025                     " roo-layout-inactive-content",
40026                 id: config.id||el
40027             };
40028             if (config.iframe) {
40029                 elcfg.cn = [
40030                     {
40031                         tag : 'iframe',
40032                         style : 'border: 0px',
40033                         src : 'about:blank'
40034                     }
40035                 ];
40036             }
40037               
40038             if (config.html) {
40039                 elcfg.html = config.html;
40040                 
40041             }
40042                         
40043             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40044             if (config.iframe) {
40045                 this.iframeEl = this.el.select('iframe',true).first();
40046             }
40047             
40048         }
40049     } 
40050     this.closable = false;
40051     this.loaded = false;
40052     this.active = false;
40053    
40054       
40055     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40056         
40057         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40058         
40059         this.wrapEl = this.el; //this.el.wrap();
40060         var ti = [];
40061         if (config.toolbar.items) {
40062             ti = config.toolbar.items ;
40063             delete config.toolbar.items ;
40064         }
40065         
40066         var nitems = [];
40067         this.toolbar.render(this.wrapEl, 'before');
40068         for(var i =0;i < ti.length;i++) {
40069           //  Roo.log(['add child', items[i]]);
40070             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40071         }
40072         this.toolbar.items = nitems;
40073         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40074         delete config.toolbar;
40075         
40076     }
40077     /*
40078     // xtype created footer. - not sure if will work as we normally have to render first..
40079     if (this.footer && !this.footer.el && this.footer.xtype) {
40080         if (!this.wrapEl) {
40081             this.wrapEl = this.el.wrap();
40082         }
40083     
40084         this.footer.container = this.wrapEl.createChild();
40085          
40086         this.footer = Roo.factory(this.footer, Roo);
40087         
40088     }
40089     */
40090     
40091      if(typeof config == "string"){
40092         this.title = config;
40093     }else{
40094         Roo.apply(this, config);
40095     }
40096     
40097     if(this.resizeEl){
40098         this.resizeEl = Roo.get(this.resizeEl, true);
40099     }else{
40100         this.resizeEl = this.el;
40101     }
40102     // handle view.xtype
40103     
40104  
40105     
40106     
40107     this.addEvents({
40108         /**
40109          * @event activate
40110          * Fires when this panel is activated. 
40111          * @param {Roo.ContentPanel} this
40112          */
40113         "activate" : true,
40114         /**
40115          * @event deactivate
40116          * Fires when this panel is activated. 
40117          * @param {Roo.ContentPanel} this
40118          */
40119         "deactivate" : true,
40120
40121         /**
40122          * @event resize
40123          * Fires when this panel is resized if fitToFrame is true.
40124          * @param {Roo.ContentPanel} this
40125          * @param {Number} width The width after any component adjustments
40126          * @param {Number} height The height after any component adjustments
40127          */
40128         "resize" : true,
40129         
40130          /**
40131          * @event render
40132          * Fires when this tab is created
40133          * @param {Roo.ContentPanel} this
40134          */
40135         "render" : true
40136         
40137         
40138         
40139     });
40140     
40141
40142     
40143     
40144     if(this.autoScroll && !this.iframe){
40145         this.resizeEl.setStyle("overflow", "auto");
40146     } else {
40147         // fix randome scrolling
40148         //this.el.on('scroll', function() {
40149         //    Roo.log('fix random scolling');
40150         //    this.scrollTo('top',0); 
40151         //});
40152     }
40153     content = content || this.content;
40154     if(content){
40155         this.setContent(content);
40156     }
40157     if(config && config.url){
40158         this.setUrl(this.url, this.params, this.loadOnce);
40159     }
40160     
40161     
40162     
40163     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40164     
40165     if (this.view && typeof(this.view.xtype) != 'undefined') {
40166         this.view.el = this.el.appendChild(document.createElement("div"));
40167         this.view = Roo.factory(this.view); 
40168         this.view.render  &&  this.view.render(false, '');  
40169     }
40170     
40171     
40172     this.fireEvent('render', this);
40173 };
40174
40175 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40176     
40177     cls : '',
40178     background : '',
40179     
40180     tabTip : '',
40181     
40182     iframe : false,
40183     iframeEl : false,
40184     
40185     setRegion : function(region){
40186         this.region = region;
40187         this.setActiveClass(region && !this.background);
40188     },
40189     
40190     
40191     setActiveClass: function(state)
40192     {
40193         if(state){
40194            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40195            this.el.setStyle('position','relative');
40196         }else{
40197            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40198            this.el.setStyle('position', 'absolute');
40199         } 
40200     },
40201     
40202     /**
40203      * Returns the toolbar for this Panel if one was configured. 
40204      * @return {Roo.Toolbar} 
40205      */
40206     getToolbar : function(){
40207         return this.toolbar;
40208     },
40209     
40210     setActiveState : function(active)
40211     {
40212         this.active = active;
40213         this.setActiveClass(active);
40214         if(!active){
40215             if(this.fireEvent("deactivate", this) === false){
40216                 return false;
40217             }
40218             return true;
40219         }
40220         this.fireEvent("activate", this);
40221         return true;
40222     },
40223     /**
40224      * Updates this panel's element (not for iframe)
40225      * @param {String} content The new content
40226      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40227     */
40228     setContent : function(content, loadScripts){
40229         if (this.iframe) {
40230             return;
40231         }
40232         
40233         this.el.update(content, loadScripts);
40234     },
40235
40236     ignoreResize : function(w, h){
40237         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40238             return true;
40239         }else{
40240             this.lastSize = {width: w, height: h};
40241             return false;
40242         }
40243     },
40244     /**
40245      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40246      * @return {Roo.UpdateManager} The UpdateManager
40247      */
40248     getUpdateManager : function(){
40249         if (this.iframe) {
40250             return false;
40251         }
40252         return this.el.getUpdateManager();
40253     },
40254      /**
40255      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40256      * Does not work with IFRAME contents
40257      * @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:
40258 <pre><code>
40259 panel.load({
40260     url: "your-url.php",
40261     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40262     callback: yourFunction,
40263     scope: yourObject, //(optional scope)
40264     discardUrl: false,
40265     nocache: false,
40266     text: "Loading...",
40267     timeout: 30,
40268     scripts: false
40269 });
40270 </code></pre>
40271      
40272      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40273      * 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.
40274      * @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}
40275      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40276      * @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.
40277      * @return {Roo.ContentPanel} this
40278      */
40279     load : function(){
40280         
40281         if (this.iframe) {
40282             return this;
40283         }
40284         
40285         var um = this.el.getUpdateManager();
40286         um.update.apply(um, arguments);
40287         return this;
40288     },
40289
40290
40291     /**
40292      * 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.
40293      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40294      * @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)
40295      * @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)
40296      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40297      */
40298     setUrl : function(url, params, loadOnce){
40299         if (this.iframe) {
40300             this.iframeEl.dom.src = url;
40301             return false;
40302         }
40303         
40304         if(this.refreshDelegate){
40305             this.removeListener("activate", this.refreshDelegate);
40306         }
40307         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40308         this.on("activate", this.refreshDelegate);
40309         return this.el.getUpdateManager();
40310     },
40311     
40312     _handleRefresh : function(url, params, loadOnce){
40313         if(!loadOnce || !this.loaded){
40314             var updater = this.el.getUpdateManager();
40315             updater.update(url, params, this._setLoaded.createDelegate(this));
40316         }
40317     },
40318     
40319     _setLoaded : function(){
40320         this.loaded = true;
40321     }, 
40322     
40323     /**
40324      * Returns this panel's id
40325      * @return {String} 
40326      */
40327     getId : function(){
40328         return this.el.id;
40329     },
40330     
40331     /** 
40332      * Returns this panel's element - used by regiosn to add.
40333      * @return {Roo.Element} 
40334      */
40335     getEl : function(){
40336         return this.wrapEl || this.el;
40337     },
40338     
40339    
40340     
40341     adjustForComponents : function(width, height)
40342     {
40343         //Roo.log('adjustForComponents ');
40344         if(this.resizeEl != this.el){
40345             width -= this.el.getFrameWidth('lr');
40346             height -= this.el.getFrameWidth('tb');
40347         }
40348         if(this.toolbar){
40349             var te = this.toolbar.getEl();
40350             te.setWidth(width);
40351             height -= te.getHeight();
40352         }
40353         if(this.footer){
40354             var te = this.footer.getEl();
40355             te.setWidth(width);
40356             height -= te.getHeight();
40357         }
40358         
40359         
40360         if(this.adjustments){
40361             width += this.adjustments[0];
40362             height += this.adjustments[1];
40363         }
40364         return {"width": width, "height": height};
40365     },
40366     
40367     setSize : function(width, height){
40368         if(this.fitToFrame && !this.ignoreResize(width, height)){
40369             if(this.fitContainer && this.resizeEl != this.el){
40370                 this.el.setSize(width, height);
40371             }
40372             var size = this.adjustForComponents(width, height);
40373             if (this.iframe) {
40374                 this.iframeEl.setSize(width,height);
40375             }
40376             
40377             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40378             this.fireEvent('resize', this, size.width, size.height);
40379             
40380             
40381         }
40382     },
40383     
40384     /**
40385      * Returns this panel's title
40386      * @return {String} 
40387      */
40388     getTitle : function(){
40389         
40390         if (typeof(this.title) != 'object') {
40391             return this.title;
40392         }
40393         
40394         var t = '';
40395         for (var k in this.title) {
40396             if (!this.title.hasOwnProperty(k)) {
40397                 continue;
40398             }
40399             
40400             if (k.indexOf('-') >= 0) {
40401                 var s = k.split('-');
40402                 for (var i = 0; i<s.length; i++) {
40403                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40404                 }
40405             } else {
40406                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40407             }
40408         }
40409         return t;
40410     },
40411     
40412     /**
40413      * Set this panel's title
40414      * @param {String} title
40415      */
40416     setTitle : function(title){
40417         this.title = title;
40418         if(this.region){
40419             this.region.updatePanelTitle(this, title);
40420         }
40421     },
40422     
40423     /**
40424      * Returns true is this panel was configured to be closable
40425      * @return {Boolean} 
40426      */
40427     isClosable : function(){
40428         return this.closable;
40429     },
40430     
40431     beforeSlide : function(){
40432         this.el.clip();
40433         this.resizeEl.clip();
40434     },
40435     
40436     afterSlide : function(){
40437         this.el.unclip();
40438         this.resizeEl.unclip();
40439     },
40440     
40441     /**
40442      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40443      *   Will fail silently if the {@link #setUrl} method has not been called.
40444      *   This does not activate the panel, just updates its content.
40445      */
40446     refresh : function(){
40447         if(this.refreshDelegate){
40448            this.loaded = false;
40449            this.refreshDelegate();
40450         }
40451     },
40452     
40453     /**
40454      * Destroys this panel
40455      */
40456     destroy : function(){
40457         this.el.removeAllListeners();
40458         var tempEl = document.createElement("span");
40459         tempEl.appendChild(this.el.dom);
40460         tempEl.innerHTML = "";
40461         this.el.remove();
40462         this.el = null;
40463     },
40464     
40465     /**
40466      * form - if the content panel contains a form - this is a reference to it.
40467      * @type {Roo.form.Form}
40468      */
40469     form : false,
40470     /**
40471      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40472      *    This contains a reference to it.
40473      * @type {Roo.View}
40474      */
40475     view : false,
40476     
40477       /**
40478      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40479      * <pre><code>
40480
40481 layout.addxtype({
40482        xtype : 'Form',
40483        items: [ .... ]
40484    }
40485 );
40486
40487 </code></pre>
40488      * @param {Object} cfg Xtype definition of item to add.
40489      */
40490     
40491     
40492     getChildContainer: function () {
40493         return this.getEl();
40494     }
40495     
40496     
40497     /*
40498         var  ret = new Roo.factory(cfg);
40499         return ret;
40500         
40501         
40502         // add form..
40503         if (cfg.xtype.match(/^Form$/)) {
40504             
40505             var el;
40506             //if (this.footer) {
40507             //    el = this.footer.container.insertSibling(false, 'before');
40508             //} else {
40509                 el = this.el.createChild();
40510             //}
40511
40512             this.form = new  Roo.form.Form(cfg);
40513             
40514             
40515             if ( this.form.allItems.length) {
40516                 this.form.render(el.dom);
40517             }
40518             return this.form;
40519         }
40520         // should only have one of theses..
40521         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40522             // views.. should not be just added - used named prop 'view''
40523             
40524             cfg.el = this.el.appendChild(document.createElement("div"));
40525             // factory?
40526             
40527             var ret = new Roo.factory(cfg);
40528              
40529              ret.render && ret.render(false, ''); // render blank..
40530             this.view = ret;
40531             return ret;
40532         }
40533         return false;
40534     }
40535     \*/
40536 });
40537  
40538 /**
40539  * @class Roo.bootstrap.panel.Grid
40540  * @extends Roo.bootstrap.panel.Content
40541  * @constructor
40542  * Create a new GridPanel.
40543  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40544  * @param {Object} config A the config object
40545   
40546  */
40547
40548
40549
40550 Roo.bootstrap.panel.Grid = function(config)
40551 {
40552     
40553       
40554     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40555         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40556
40557     config.el = this.wrapper;
40558     //this.el = this.wrapper;
40559     
40560       if (config.container) {
40561         // ctor'ed from a Border/panel.grid
40562         
40563         
40564         this.wrapper.setStyle("overflow", "hidden");
40565         this.wrapper.addClass('roo-grid-container');
40566
40567     }
40568     
40569     
40570     if(config.toolbar){
40571         var tool_el = this.wrapper.createChild();    
40572         this.toolbar = Roo.factory(config.toolbar);
40573         var ti = [];
40574         if (config.toolbar.items) {
40575             ti = config.toolbar.items ;
40576             delete config.toolbar.items ;
40577         }
40578         
40579         var nitems = [];
40580         this.toolbar.render(tool_el);
40581         for(var i =0;i < ti.length;i++) {
40582           //  Roo.log(['add child', items[i]]);
40583             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40584         }
40585         this.toolbar.items = nitems;
40586         
40587         delete config.toolbar;
40588     }
40589     
40590     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40591     config.grid.scrollBody = true;;
40592     config.grid.monitorWindowResize = false; // turn off autosizing
40593     config.grid.autoHeight = false;
40594     config.grid.autoWidth = false;
40595     
40596     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40597     
40598     if (config.background) {
40599         // render grid on panel activation (if panel background)
40600         this.on('activate', function(gp) {
40601             if (!gp.grid.rendered) {
40602                 gp.grid.render(this.wrapper);
40603                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40604             }
40605         });
40606             
40607     } else {
40608         this.grid.render(this.wrapper);
40609         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40610
40611     }
40612     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40613     // ??? needed ??? config.el = this.wrapper;
40614     
40615     
40616     
40617   
40618     // xtype created footer. - not sure if will work as we normally have to render first..
40619     if (this.footer && !this.footer.el && this.footer.xtype) {
40620         
40621         var ctr = this.grid.getView().getFooterPanel(true);
40622         this.footer.dataSource = this.grid.dataSource;
40623         this.footer = Roo.factory(this.footer, Roo);
40624         this.footer.render(ctr);
40625         
40626     }
40627     
40628     
40629     
40630     
40631      
40632 };
40633
40634 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40635     getId : function(){
40636         return this.grid.id;
40637     },
40638     
40639     /**
40640      * Returns the grid for this panel
40641      * @return {Roo.bootstrap.Table} 
40642      */
40643     getGrid : function(){
40644         return this.grid;    
40645     },
40646     
40647     setSize : function(width, height){
40648         if(!this.ignoreResize(width, height)){
40649             var grid = this.grid;
40650             var size = this.adjustForComponents(width, height);
40651             // tfoot is not a footer?
40652           
40653             
40654             var gridel = grid.getGridEl();
40655             gridel.setSize(size.width, size.height);
40656             
40657             var tbd = grid.getGridEl().select('tbody', true).first();
40658             var thd = grid.getGridEl().select('thead',true).first();
40659             var tbf= grid.getGridEl().select('tfoot', true).first();
40660
40661             if (tbf) {
40662                 size.height -= tbf.getHeight();
40663             }
40664             if (thd) {
40665                 size.height -= thd.getHeight();
40666             }
40667             
40668             tbd.setSize(size.width, size.height );
40669             // this is for the account management tab -seems to work there.
40670             var thd = grid.getGridEl().select('thead',true).first();
40671             //if (tbd) {
40672             //    tbd.setSize(size.width, size.height - thd.getHeight());
40673             //}
40674              
40675             grid.autoSize();
40676         }
40677     },
40678      
40679     
40680     
40681     beforeSlide : function(){
40682         this.grid.getView().scroller.clip();
40683     },
40684     
40685     afterSlide : function(){
40686         this.grid.getView().scroller.unclip();
40687     },
40688     
40689     destroy : function(){
40690         this.grid.destroy();
40691         delete this.grid;
40692         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40693     }
40694 });
40695
40696 /**
40697  * @class Roo.bootstrap.panel.Nest
40698  * @extends Roo.bootstrap.panel.Content
40699  * @constructor
40700  * Create a new Panel, that can contain a layout.Border.
40701  * 
40702  * 
40703  * @param {Roo.BorderLayout} layout The layout for this panel
40704  * @param {String/Object} config A string to set only the title or a config object
40705  */
40706 Roo.bootstrap.panel.Nest = function(config)
40707 {
40708     // construct with only one argument..
40709     /* FIXME - implement nicer consturctors
40710     if (layout.layout) {
40711         config = layout;
40712         layout = config.layout;
40713         delete config.layout;
40714     }
40715     if (layout.xtype && !layout.getEl) {
40716         // then layout needs constructing..
40717         layout = Roo.factory(layout, Roo);
40718     }
40719     */
40720     
40721     config.el =  config.layout.getEl();
40722     
40723     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40724     
40725     config.layout.monitorWindowResize = false; // turn off autosizing
40726     this.layout = config.layout;
40727     this.layout.getEl().addClass("roo-layout-nested-layout");
40728     this.layout.parent = this;
40729     
40730     
40731     
40732     
40733 };
40734
40735 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40736
40737     setSize : function(width, height){
40738         if(!this.ignoreResize(width, height)){
40739             var size = this.adjustForComponents(width, height);
40740             var el = this.layout.getEl();
40741             if (size.height < 1) {
40742                 el.setWidth(size.width);   
40743             } else {
40744                 el.setSize(size.width, size.height);
40745             }
40746             var touch = el.dom.offsetWidth;
40747             this.layout.layout();
40748             // ie requires a double layout on the first pass
40749             if(Roo.isIE && !this.initialized){
40750                 this.initialized = true;
40751                 this.layout.layout();
40752             }
40753         }
40754     },
40755     
40756     // activate all subpanels if not currently active..
40757     
40758     setActiveState : function(active){
40759         this.active = active;
40760         this.setActiveClass(active);
40761         
40762         if(!active){
40763             this.fireEvent("deactivate", this);
40764             return;
40765         }
40766         
40767         this.fireEvent("activate", this);
40768         // not sure if this should happen before or after..
40769         if (!this.layout) {
40770             return; // should not happen..
40771         }
40772         var reg = false;
40773         for (var r in this.layout.regions) {
40774             reg = this.layout.getRegion(r);
40775             if (reg.getActivePanel()) {
40776                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40777                 reg.setActivePanel(reg.getActivePanel());
40778                 continue;
40779             }
40780             if (!reg.panels.length) {
40781                 continue;
40782             }
40783             reg.showPanel(reg.getPanel(0));
40784         }
40785         
40786         
40787         
40788         
40789     },
40790     
40791     /**
40792      * Returns the nested BorderLayout for this panel
40793      * @return {Roo.BorderLayout} 
40794      */
40795     getLayout : function(){
40796         return this.layout;
40797     },
40798     
40799      /**
40800      * Adds a xtype elements to the layout of the nested panel
40801      * <pre><code>
40802
40803 panel.addxtype({
40804        xtype : 'ContentPanel',
40805        region: 'west',
40806        items: [ .... ]
40807    }
40808 );
40809
40810 panel.addxtype({
40811         xtype : 'NestedLayoutPanel',
40812         region: 'west',
40813         layout: {
40814            center: { },
40815            west: { }   
40816         },
40817         items : [ ... list of content panels or nested layout panels.. ]
40818    }
40819 );
40820 </code></pre>
40821      * @param {Object} cfg Xtype definition of item to add.
40822      */
40823     addxtype : function(cfg) {
40824         return this.layout.addxtype(cfg);
40825     
40826     }
40827 });/*
40828  * Based on:
40829  * Ext JS Library 1.1.1
40830  * Copyright(c) 2006-2007, Ext JS, LLC.
40831  *
40832  * Originally Released Under LGPL - original licence link has changed is not relivant.
40833  *
40834  * Fork - LGPL
40835  * <script type="text/javascript">
40836  */
40837 /**
40838  * @class Roo.TabPanel
40839  * @extends Roo.util.Observable
40840  * A lightweight tab container.
40841  * <br><br>
40842  * Usage:
40843  * <pre><code>
40844 // basic tabs 1, built from existing content
40845 var tabs = new Roo.TabPanel("tabs1");
40846 tabs.addTab("script", "View Script");
40847 tabs.addTab("markup", "View Markup");
40848 tabs.activate("script");
40849
40850 // more advanced tabs, built from javascript
40851 var jtabs = new Roo.TabPanel("jtabs");
40852 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40853
40854 // set up the UpdateManager
40855 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40856 var updater = tab2.getUpdateManager();
40857 updater.setDefaultUrl("ajax1.htm");
40858 tab2.on('activate', updater.refresh, updater, true);
40859
40860 // Use setUrl for Ajax loading
40861 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40862 tab3.setUrl("ajax2.htm", null, true);
40863
40864 // Disabled tab
40865 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40866 tab4.disable();
40867
40868 jtabs.activate("jtabs-1");
40869  * </code></pre>
40870  * @constructor
40871  * Create a new TabPanel.
40872  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40873  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40874  */
40875 Roo.bootstrap.panel.Tabs = function(config){
40876     /**
40877     * The container element for this TabPanel.
40878     * @type Roo.Element
40879     */
40880     this.el = Roo.get(config.el);
40881     delete config.el;
40882     if(config){
40883         if(typeof config == "boolean"){
40884             this.tabPosition = config ? "bottom" : "top";
40885         }else{
40886             Roo.apply(this, config);
40887         }
40888     }
40889     
40890     if(this.tabPosition == "bottom"){
40891         // if tabs are at the bottom = create the body first.
40892         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40893         this.el.addClass("roo-tabs-bottom");
40894     }
40895     // next create the tabs holders
40896     
40897     if (this.tabPosition == "west"){
40898         
40899         var reg = this.region; // fake it..
40900         while (reg) {
40901             if (!reg.mgr.parent) {
40902                 break;
40903             }
40904             reg = reg.mgr.parent.region;
40905         }
40906         Roo.log("got nest?");
40907         Roo.log(reg);
40908         if (reg.mgr.getRegion('west')) {
40909             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40910             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40911             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40912             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40913             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40914         
40915             
40916         }
40917         
40918         
40919     } else {
40920      
40921         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40922         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40923         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40924         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40925     }
40926     
40927     
40928     if(Roo.isIE){
40929         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40930     }
40931     
40932     // finally - if tabs are at the top, then create the body last..
40933     if(this.tabPosition != "bottom"){
40934         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40935          * @type Roo.Element
40936          */
40937         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40938         this.el.addClass("roo-tabs-top");
40939     }
40940     this.items = [];
40941
40942     this.bodyEl.setStyle("position", "relative");
40943
40944     this.active = null;
40945     this.activateDelegate = this.activate.createDelegate(this);
40946
40947     this.addEvents({
40948         /**
40949          * @event tabchange
40950          * Fires when the active tab changes
40951          * @param {Roo.TabPanel} this
40952          * @param {Roo.TabPanelItem} activePanel The new active tab
40953          */
40954         "tabchange": true,
40955         /**
40956          * @event beforetabchange
40957          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40958          * @param {Roo.TabPanel} this
40959          * @param {Object} e Set cancel to true on this object to cancel the tab change
40960          * @param {Roo.TabPanelItem} tab The tab being changed to
40961          */
40962         "beforetabchange" : true
40963     });
40964
40965     Roo.EventManager.onWindowResize(this.onResize, this);
40966     this.cpad = this.el.getPadding("lr");
40967     this.hiddenCount = 0;
40968
40969
40970     // toolbar on the tabbar support...
40971     if (this.toolbar) {
40972         alert("no toolbar support yet");
40973         this.toolbar  = false;
40974         /*
40975         var tcfg = this.toolbar;
40976         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40977         this.toolbar = new Roo.Toolbar(tcfg);
40978         if (Roo.isSafari) {
40979             var tbl = tcfg.container.child('table', true);
40980             tbl.setAttribute('width', '100%');
40981         }
40982         */
40983         
40984     }
40985    
40986
40987
40988     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40989 };
40990
40991 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40992     /*
40993      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40994      */
40995     tabPosition : "top",
40996     /*
40997      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40998      */
40999     currentTabWidth : 0,
41000     /*
41001      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41002      */
41003     minTabWidth : 40,
41004     /*
41005      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41006      */
41007     maxTabWidth : 250,
41008     /*
41009      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41010      */
41011     preferredTabWidth : 175,
41012     /*
41013      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41014      */
41015     resizeTabs : false,
41016     /*
41017      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41018      */
41019     monitorResize : true,
41020     /*
41021      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41022      */
41023     toolbar : false,  // set by caller..
41024     
41025     region : false, /// set by caller
41026     
41027     disableTooltips : true, // not used yet...
41028
41029     /**
41030      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41031      * @param {String} id The id of the div to use <b>or create</b>
41032      * @param {String} text The text for the tab
41033      * @param {String} content (optional) Content to put in the TabPanelItem body
41034      * @param {Boolean} closable (optional) True to create a close icon on the tab
41035      * @return {Roo.TabPanelItem} The created TabPanelItem
41036      */
41037     addTab : function(id, text, content, closable, tpl)
41038     {
41039         var item = new Roo.bootstrap.panel.TabItem({
41040             panel: this,
41041             id : id,
41042             text : text,
41043             closable : closable,
41044             tpl : tpl
41045         });
41046         this.addTabItem(item);
41047         if(content){
41048             item.setContent(content);
41049         }
41050         return item;
41051     },
41052
41053     /**
41054      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41055      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41056      * @return {Roo.TabPanelItem}
41057      */
41058     getTab : function(id){
41059         return this.items[id];
41060     },
41061
41062     /**
41063      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41064      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41065      */
41066     hideTab : function(id){
41067         var t = this.items[id];
41068         if(!t.isHidden()){
41069            t.setHidden(true);
41070            this.hiddenCount++;
41071            this.autoSizeTabs();
41072         }
41073     },
41074
41075     /**
41076      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41077      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41078      */
41079     unhideTab : function(id){
41080         var t = this.items[id];
41081         if(t.isHidden()){
41082            t.setHidden(false);
41083            this.hiddenCount--;
41084            this.autoSizeTabs();
41085         }
41086     },
41087
41088     /**
41089      * Adds an existing {@link Roo.TabPanelItem}.
41090      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41091      */
41092     addTabItem : function(item)
41093     {
41094         this.items[item.id] = item;
41095         this.items.push(item);
41096         this.autoSizeTabs();
41097       //  if(this.resizeTabs){
41098     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41099   //         this.autoSizeTabs();
41100 //        }else{
41101 //            item.autoSize();
41102        // }
41103     },
41104
41105     /**
41106      * Removes a {@link Roo.TabPanelItem}.
41107      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41108      */
41109     removeTab : function(id){
41110         var items = this.items;
41111         var tab = items[id];
41112         if(!tab) { return; }
41113         var index = items.indexOf(tab);
41114         if(this.active == tab && items.length > 1){
41115             var newTab = this.getNextAvailable(index);
41116             if(newTab) {
41117                 newTab.activate();
41118             }
41119         }
41120         this.stripEl.dom.removeChild(tab.pnode.dom);
41121         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41122             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41123         }
41124         items.splice(index, 1);
41125         delete this.items[tab.id];
41126         tab.fireEvent("close", tab);
41127         tab.purgeListeners();
41128         this.autoSizeTabs();
41129     },
41130
41131     getNextAvailable : function(start){
41132         var items = this.items;
41133         var index = start;
41134         // look for a next tab that will slide over to
41135         // replace the one being removed
41136         while(index < items.length){
41137             var item = items[++index];
41138             if(item && !item.isHidden()){
41139                 return item;
41140             }
41141         }
41142         // if one isn't found select the previous tab (on the left)
41143         index = start;
41144         while(index >= 0){
41145             var item = items[--index];
41146             if(item && !item.isHidden()){
41147                 return item;
41148             }
41149         }
41150         return null;
41151     },
41152
41153     /**
41154      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41155      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41156      */
41157     disableTab : function(id){
41158         var tab = this.items[id];
41159         if(tab && this.active != tab){
41160             tab.disable();
41161         }
41162     },
41163
41164     /**
41165      * Enables a {@link Roo.TabPanelItem} that is disabled.
41166      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41167      */
41168     enableTab : function(id){
41169         var tab = this.items[id];
41170         tab.enable();
41171     },
41172
41173     /**
41174      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41175      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41176      * @return {Roo.TabPanelItem} The TabPanelItem.
41177      */
41178     activate : function(id)
41179     {
41180         //Roo.log('activite:'  + id);
41181         
41182         var tab = this.items[id];
41183         if(!tab){
41184             return null;
41185         }
41186         if(tab == this.active || tab.disabled){
41187             return tab;
41188         }
41189         var e = {};
41190         this.fireEvent("beforetabchange", this, e, tab);
41191         if(e.cancel !== true && !tab.disabled){
41192             if(this.active){
41193                 this.active.hide();
41194             }
41195             this.active = this.items[id];
41196             this.active.show();
41197             this.fireEvent("tabchange", this, this.active);
41198         }
41199         return tab;
41200     },
41201
41202     /**
41203      * Gets the active {@link Roo.TabPanelItem}.
41204      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41205      */
41206     getActiveTab : function(){
41207         return this.active;
41208     },
41209
41210     /**
41211      * Updates the tab body element to fit the height of the container element
41212      * for overflow scrolling
41213      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41214      */
41215     syncHeight : function(targetHeight){
41216         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41217         var bm = this.bodyEl.getMargins();
41218         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41219         this.bodyEl.setHeight(newHeight);
41220         return newHeight;
41221     },
41222
41223     onResize : function(){
41224         if(this.monitorResize){
41225             this.autoSizeTabs();
41226         }
41227     },
41228
41229     /**
41230      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41231      */
41232     beginUpdate : function(){
41233         this.updating = true;
41234     },
41235
41236     /**
41237      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41238      */
41239     endUpdate : function(){
41240         this.updating = false;
41241         this.autoSizeTabs();
41242     },
41243
41244     /**
41245      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41246      */
41247     autoSizeTabs : function()
41248     {
41249         var count = this.items.length;
41250         var vcount = count - this.hiddenCount;
41251         
41252         if (vcount < 2) {
41253             this.stripEl.hide();
41254         } else {
41255             this.stripEl.show();
41256         }
41257         
41258         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41259             return;
41260         }
41261         
41262         
41263         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41264         var availWidth = Math.floor(w / vcount);
41265         var b = this.stripBody;
41266         if(b.getWidth() > w){
41267             var tabs = this.items;
41268             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41269             if(availWidth < this.minTabWidth){
41270                 /*if(!this.sleft){    // incomplete scrolling code
41271                     this.createScrollButtons();
41272                 }
41273                 this.showScroll();
41274                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41275             }
41276         }else{
41277             if(this.currentTabWidth < this.preferredTabWidth){
41278                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41279             }
41280         }
41281     },
41282
41283     /**
41284      * Returns the number of tabs in this TabPanel.
41285      * @return {Number}
41286      */
41287      getCount : function(){
41288          return this.items.length;
41289      },
41290
41291     /**
41292      * Resizes all the tabs to the passed width
41293      * @param {Number} The new width
41294      */
41295     setTabWidth : function(width){
41296         this.currentTabWidth = width;
41297         for(var i = 0, len = this.items.length; i < len; i++) {
41298                 if(!this.items[i].isHidden()) {
41299                 this.items[i].setWidth(width);
41300             }
41301         }
41302     },
41303
41304     /**
41305      * Destroys this TabPanel
41306      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41307      */
41308     destroy : function(removeEl){
41309         Roo.EventManager.removeResizeListener(this.onResize, this);
41310         for(var i = 0, len = this.items.length; i < len; i++){
41311             this.items[i].purgeListeners();
41312         }
41313         if(removeEl === true){
41314             this.el.update("");
41315             this.el.remove();
41316         }
41317     },
41318     
41319     createStrip : function(container)
41320     {
41321         var strip = document.createElement("nav");
41322         strip.className = Roo.bootstrap.version == 4 ?
41323             "navbar-light bg-light" : 
41324             "navbar navbar-default"; //"x-tabs-wrap";
41325         container.appendChild(strip);
41326         return strip;
41327     },
41328     
41329     createStripList : function(strip)
41330     {
41331         // div wrapper for retard IE
41332         // returns the "tr" element.
41333         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41334         //'<div class="x-tabs-strip-wrap">'+
41335           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41336           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41337         return strip.firstChild; //.firstChild.firstChild.firstChild;
41338     },
41339     createBody : function(container)
41340     {
41341         var body = document.createElement("div");
41342         Roo.id(body, "tab-body");
41343         //Roo.fly(body).addClass("x-tabs-body");
41344         Roo.fly(body).addClass("tab-content");
41345         container.appendChild(body);
41346         return body;
41347     },
41348     createItemBody :function(bodyEl, id){
41349         var body = Roo.getDom(id);
41350         if(!body){
41351             body = document.createElement("div");
41352             body.id = id;
41353         }
41354         //Roo.fly(body).addClass("x-tabs-item-body");
41355         Roo.fly(body).addClass("tab-pane");
41356          bodyEl.insertBefore(body, bodyEl.firstChild);
41357         return body;
41358     },
41359     /** @private */
41360     createStripElements :  function(stripEl, text, closable, tpl)
41361     {
41362         var td = document.createElement("li"); // was td..
41363         td.className = 'nav-item';
41364         
41365         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41366         
41367         
41368         stripEl.appendChild(td);
41369         /*if(closable){
41370             td.className = "x-tabs-closable";
41371             if(!this.closeTpl){
41372                 this.closeTpl = new Roo.Template(
41373                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41374                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41375                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41376                 );
41377             }
41378             var el = this.closeTpl.overwrite(td, {"text": text});
41379             var close = el.getElementsByTagName("div")[0];
41380             var inner = el.getElementsByTagName("em")[0];
41381             return {"el": el, "close": close, "inner": inner};
41382         } else {
41383         */
41384         // not sure what this is..
41385 //            if(!this.tabTpl){
41386                 //this.tabTpl = new Roo.Template(
41387                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41388                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41389                 //);
41390 //                this.tabTpl = new Roo.Template(
41391 //                   '<a href="#">' +
41392 //                   '<span unselectable="on"' +
41393 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41394 //                            ' >{text}</span></a>'
41395 //                );
41396 //                
41397 //            }
41398
41399
41400             var template = tpl || this.tabTpl || false;
41401             
41402             if(!template){
41403                 template =  new Roo.Template(
41404                         Roo.bootstrap.version == 4 ? 
41405                             (
41406                                 '<a class="nav-link" href="#" unselectable="on"' +
41407                                      (this.disableTooltips ? '' : ' title="{text}"') +
41408                                      ' >{text}</a>'
41409                             ) : (
41410                                 '<a class="nav-link" href="#">' +
41411                                 '<span unselectable="on"' +
41412                                          (this.disableTooltips ? '' : ' title="{text}"') +
41413                                     ' >{text}</span></a>'
41414                             )
41415                 );
41416             }
41417             
41418             switch (typeof(template)) {
41419                 case 'object' :
41420                     break;
41421                 case 'string' :
41422                     template = new Roo.Template(template);
41423                     break;
41424                 default :
41425                     break;
41426             }
41427             
41428             var el = template.overwrite(td, {"text": text});
41429             
41430             var inner = el.getElementsByTagName("span")[0];
41431             
41432             return {"el": el, "inner": inner};
41433             
41434     }
41435         
41436     
41437 });
41438
41439 /**
41440  * @class Roo.TabPanelItem
41441  * @extends Roo.util.Observable
41442  * Represents an individual item (tab plus body) in a TabPanel.
41443  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41444  * @param {String} id The id of this TabPanelItem
41445  * @param {String} text The text for the tab of this TabPanelItem
41446  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41447  */
41448 Roo.bootstrap.panel.TabItem = function(config){
41449     /**
41450      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41451      * @type Roo.TabPanel
41452      */
41453     this.tabPanel = config.panel;
41454     /**
41455      * The id for this TabPanelItem
41456      * @type String
41457      */
41458     this.id = config.id;
41459     /** @private */
41460     this.disabled = false;
41461     /** @private */
41462     this.text = config.text;
41463     /** @private */
41464     this.loaded = false;
41465     this.closable = config.closable;
41466
41467     /**
41468      * The body element for this TabPanelItem.
41469      * @type Roo.Element
41470      */
41471     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41472     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41473     this.bodyEl.setStyle("display", "block");
41474     this.bodyEl.setStyle("zoom", "1");
41475     //this.hideAction();
41476
41477     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41478     /** @private */
41479     this.el = Roo.get(els.el);
41480     this.inner = Roo.get(els.inner, true);
41481      this.textEl = Roo.bootstrap.version == 4 ?
41482         this.el : Roo.get(this.el.dom.firstChild, true);
41483
41484     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41485     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41486
41487     
41488 //    this.el.on("mousedown", this.onTabMouseDown, this);
41489     this.el.on("click", this.onTabClick, this);
41490     /** @private */
41491     if(config.closable){
41492         var c = Roo.get(els.close, true);
41493         c.dom.title = this.closeText;
41494         c.addClassOnOver("close-over");
41495         c.on("click", this.closeClick, this);
41496      }
41497
41498     this.addEvents({
41499          /**
41500          * @event activate
41501          * Fires when this tab becomes the active tab.
41502          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41503          * @param {Roo.TabPanelItem} this
41504          */
41505         "activate": true,
41506         /**
41507          * @event beforeclose
41508          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41509          * @param {Roo.TabPanelItem} this
41510          * @param {Object} e Set cancel to true on this object to cancel the close.
41511          */
41512         "beforeclose": true,
41513         /**
41514          * @event close
41515          * Fires when this tab is closed.
41516          * @param {Roo.TabPanelItem} this
41517          */
41518          "close": true,
41519         /**
41520          * @event deactivate
41521          * Fires when this tab is no longer the active tab.
41522          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41523          * @param {Roo.TabPanelItem} this
41524          */
41525          "deactivate" : true
41526     });
41527     this.hidden = false;
41528
41529     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41530 };
41531
41532 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41533            {
41534     purgeListeners : function(){
41535        Roo.util.Observable.prototype.purgeListeners.call(this);
41536        this.el.removeAllListeners();
41537     },
41538     /**
41539      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41540      */
41541     show : function(){
41542         this.status_node.addClass("active");
41543         this.showAction();
41544         if(Roo.isOpera){
41545             this.tabPanel.stripWrap.repaint();
41546         }
41547         this.fireEvent("activate", this.tabPanel, this);
41548     },
41549
41550     /**
41551      * Returns true if this tab is the active tab.
41552      * @return {Boolean}
41553      */
41554     isActive : function(){
41555         return this.tabPanel.getActiveTab() == this;
41556     },
41557
41558     /**
41559      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41560      */
41561     hide : function(){
41562         this.status_node.removeClass("active");
41563         this.hideAction();
41564         this.fireEvent("deactivate", this.tabPanel, this);
41565     },
41566
41567     hideAction : function(){
41568         this.bodyEl.hide();
41569         this.bodyEl.setStyle("position", "absolute");
41570         this.bodyEl.setLeft("-20000px");
41571         this.bodyEl.setTop("-20000px");
41572     },
41573
41574     showAction : function(){
41575         this.bodyEl.setStyle("position", "relative");
41576         this.bodyEl.setTop("");
41577         this.bodyEl.setLeft("");
41578         this.bodyEl.show();
41579     },
41580
41581     /**
41582      * Set the tooltip for the tab.
41583      * @param {String} tooltip The tab's tooltip
41584      */
41585     setTooltip : function(text){
41586         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41587             this.textEl.dom.qtip = text;
41588             this.textEl.dom.removeAttribute('title');
41589         }else{
41590             this.textEl.dom.title = text;
41591         }
41592     },
41593
41594     onTabClick : function(e){
41595         e.preventDefault();
41596         this.tabPanel.activate(this.id);
41597     },
41598
41599     onTabMouseDown : function(e){
41600         e.preventDefault();
41601         this.tabPanel.activate(this.id);
41602     },
41603 /*
41604     getWidth : function(){
41605         return this.inner.getWidth();
41606     },
41607
41608     setWidth : function(width){
41609         var iwidth = width - this.linode.getPadding("lr");
41610         this.inner.setWidth(iwidth);
41611         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41612         this.linode.setWidth(width);
41613     },
41614 */
41615     /**
41616      * Show or hide the tab
41617      * @param {Boolean} hidden True to hide or false to show.
41618      */
41619     setHidden : function(hidden){
41620         this.hidden = hidden;
41621         this.linode.setStyle("display", hidden ? "none" : "");
41622     },
41623
41624     /**
41625      * Returns true if this tab is "hidden"
41626      * @return {Boolean}
41627      */
41628     isHidden : function(){
41629         return this.hidden;
41630     },
41631
41632     /**
41633      * Returns the text for this tab
41634      * @return {String}
41635      */
41636     getText : function(){
41637         return this.text;
41638     },
41639     /*
41640     autoSize : function(){
41641         //this.el.beginMeasure();
41642         this.textEl.setWidth(1);
41643         /*
41644          *  #2804 [new] Tabs in Roojs
41645          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41646          */
41647         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41648         //this.el.endMeasure();
41649     //},
41650
41651     /**
41652      * Sets the text for the tab (Note: this also sets the tooltip text)
41653      * @param {String} text The tab's text and tooltip
41654      */
41655     setText : function(text){
41656         this.text = text;
41657         this.textEl.update(text);
41658         this.setTooltip(text);
41659         //if(!this.tabPanel.resizeTabs){
41660         //    this.autoSize();
41661         //}
41662     },
41663     /**
41664      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41665      */
41666     activate : function(){
41667         this.tabPanel.activate(this.id);
41668     },
41669
41670     /**
41671      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41672      */
41673     disable : function(){
41674         if(this.tabPanel.active != this){
41675             this.disabled = true;
41676             this.status_node.addClass("disabled");
41677         }
41678     },
41679
41680     /**
41681      * Enables this TabPanelItem if it was previously disabled.
41682      */
41683     enable : function(){
41684         this.disabled = false;
41685         this.status_node.removeClass("disabled");
41686     },
41687
41688     /**
41689      * Sets the content for this TabPanelItem.
41690      * @param {String} content The content
41691      * @param {Boolean} loadScripts true to look for and load scripts
41692      */
41693     setContent : function(content, loadScripts){
41694         this.bodyEl.update(content, loadScripts);
41695     },
41696
41697     /**
41698      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41699      * @return {Roo.UpdateManager} The UpdateManager
41700      */
41701     getUpdateManager : function(){
41702         return this.bodyEl.getUpdateManager();
41703     },
41704
41705     /**
41706      * Set a URL to be used to load the content for this TabPanelItem.
41707      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41708      * @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)
41709      * @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)
41710      * @return {Roo.UpdateManager} The UpdateManager
41711      */
41712     setUrl : function(url, params, loadOnce){
41713         if(this.refreshDelegate){
41714             this.un('activate', this.refreshDelegate);
41715         }
41716         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41717         this.on("activate", this.refreshDelegate);
41718         return this.bodyEl.getUpdateManager();
41719     },
41720
41721     /** @private */
41722     _handleRefresh : function(url, params, loadOnce){
41723         if(!loadOnce || !this.loaded){
41724             var updater = this.bodyEl.getUpdateManager();
41725             updater.update(url, params, this._setLoaded.createDelegate(this));
41726         }
41727     },
41728
41729     /**
41730      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41731      *   Will fail silently if the setUrl method has not been called.
41732      *   This does not activate the panel, just updates its content.
41733      */
41734     refresh : function(){
41735         if(this.refreshDelegate){
41736            this.loaded = false;
41737            this.refreshDelegate();
41738         }
41739     },
41740
41741     /** @private */
41742     _setLoaded : function(){
41743         this.loaded = true;
41744     },
41745
41746     /** @private */
41747     closeClick : function(e){
41748         var o = {};
41749         e.stopEvent();
41750         this.fireEvent("beforeclose", this, o);
41751         if(o.cancel !== true){
41752             this.tabPanel.removeTab(this.id);
41753         }
41754     },
41755     /**
41756      * The text displayed in the tooltip for the close icon.
41757      * @type String
41758      */
41759     closeText : "Close this tab"
41760 });
41761 /**
41762 *    This script refer to:
41763 *    Title: International Telephone Input
41764 *    Author: Jack O'Connor
41765 *    Code version:  v12.1.12
41766 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41767 **/
41768
41769 Roo.bootstrap.PhoneInputData = function() {
41770     var d = [
41771       [
41772         "Afghanistan (‫افغانستان‬‎)",
41773         "af",
41774         "93"
41775       ],
41776       [
41777         "Albania (Shqipëri)",
41778         "al",
41779         "355"
41780       ],
41781       [
41782         "Algeria (‫الجزائر‬‎)",
41783         "dz",
41784         "213"
41785       ],
41786       [
41787         "American Samoa",
41788         "as",
41789         "1684"
41790       ],
41791       [
41792         "Andorra",
41793         "ad",
41794         "376"
41795       ],
41796       [
41797         "Angola",
41798         "ao",
41799         "244"
41800       ],
41801       [
41802         "Anguilla",
41803         "ai",
41804         "1264"
41805       ],
41806       [
41807         "Antigua and Barbuda",
41808         "ag",
41809         "1268"
41810       ],
41811       [
41812         "Argentina",
41813         "ar",
41814         "54"
41815       ],
41816       [
41817         "Armenia (Հայաստան)",
41818         "am",
41819         "374"
41820       ],
41821       [
41822         "Aruba",
41823         "aw",
41824         "297"
41825       ],
41826       [
41827         "Australia",
41828         "au",
41829         "61",
41830         0
41831       ],
41832       [
41833         "Austria (Österreich)",
41834         "at",
41835         "43"
41836       ],
41837       [
41838         "Azerbaijan (Azərbaycan)",
41839         "az",
41840         "994"
41841       ],
41842       [
41843         "Bahamas",
41844         "bs",
41845         "1242"
41846       ],
41847       [
41848         "Bahrain (‫البحرين‬‎)",
41849         "bh",
41850         "973"
41851       ],
41852       [
41853         "Bangladesh (বাংলাদেশ)",
41854         "bd",
41855         "880"
41856       ],
41857       [
41858         "Barbados",
41859         "bb",
41860         "1246"
41861       ],
41862       [
41863         "Belarus (Беларусь)",
41864         "by",
41865         "375"
41866       ],
41867       [
41868         "Belgium (België)",
41869         "be",
41870         "32"
41871       ],
41872       [
41873         "Belize",
41874         "bz",
41875         "501"
41876       ],
41877       [
41878         "Benin (Bénin)",
41879         "bj",
41880         "229"
41881       ],
41882       [
41883         "Bermuda",
41884         "bm",
41885         "1441"
41886       ],
41887       [
41888         "Bhutan (འབྲུག)",
41889         "bt",
41890         "975"
41891       ],
41892       [
41893         "Bolivia",
41894         "bo",
41895         "591"
41896       ],
41897       [
41898         "Bosnia and Herzegovina (Босна и Херцеговина)",
41899         "ba",
41900         "387"
41901       ],
41902       [
41903         "Botswana",
41904         "bw",
41905         "267"
41906       ],
41907       [
41908         "Brazil (Brasil)",
41909         "br",
41910         "55"
41911       ],
41912       [
41913         "British Indian Ocean Territory",
41914         "io",
41915         "246"
41916       ],
41917       [
41918         "British Virgin Islands",
41919         "vg",
41920         "1284"
41921       ],
41922       [
41923         "Brunei",
41924         "bn",
41925         "673"
41926       ],
41927       [
41928         "Bulgaria (България)",
41929         "bg",
41930         "359"
41931       ],
41932       [
41933         "Burkina Faso",
41934         "bf",
41935         "226"
41936       ],
41937       [
41938         "Burundi (Uburundi)",
41939         "bi",
41940         "257"
41941       ],
41942       [
41943         "Cambodia (កម្ពុជា)",
41944         "kh",
41945         "855"
41946       ],
41947       [
41948         "Cameroon (Cameroun)",
41949         "cm",
41950         "237"
41951       ],
41952       [
41953         "Canada",
41954         "ca",
41955         "1",
41956         1,
41957         ["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"]
41958       ],
41959       [
41960         "Cape Verde (Kabu Verdi)",
41961         "cv",
41962         "238"
41963       ],
41964       [
41965         "Caribbean Netherlands",
41966         "bq",
41967         "599",
41968         1
41969       ],
41970       [
41971         "Cayman Islands",
41972         "ky",
41973         "1345"
41974       ],
41975       [
41976         "Central African Republic (République centrafricaine)",
41977         "cf",
41978         "236"
41979       ],
41980       [
41981         "Chad (Tchad)",
41982         "td",
41983         "235"
41984       ],
41985       [
41986         "Chile",
41987         "cl",
41988         "56"
41989       ],
41990       [
41991         "China (中国)",
41992         "cn",
41993         "86"
41994       ],
41995       [
41996         "Christmas Island",
41997         "cx",
41998         "61",
41999         2
42000       ],
42001       [
42002         "Cocos (Keeling) Islands",
42003         "cc",
42004         "61",
42005         1
42006       ],
42007       [
42008         "Colombia",
42009         "co",
42010         "57"
42011       ],
42012       [
42013         "Comoros (‫جزر القمر‬‎)",
42014         "km",
42015         "269"
42016       ],
42017       [
42018         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42019         "cd",
42020         "243"
42021       ],
42022       [
42023         "Congo (Republic) (Congo-Brazzaville)",
42024         "cg",
42025         "242"
42026       ],
42027       [
42028         "Cook Islands",
42029         "ck",
42030         "682"
42031       ],
42032       [
42033         "Costa Rica",
42034         "cr",
42035         "506"
42036       ],
42037       [
42038         "Côte d’Ivoire",
42039         "ci",
42040         "225"
42041       ],
42042       [
42043         "Croatia (Hrvatska)",
42044         "hr",
42045         "385"
42046       ],
42047       [
42048         "Cuba",
42049         "cu",
42050         "53"
42051       ],
42052       [
42053         "Curaçao",
42054         "cw",
42055         "599",
42056         0
42057       ],
42058       [
42059         "Cyprus (Κύπρος)",
42060         "cy",
42061         "357"
42062       ],
42063       [
42064         "Czech Republic (Česká republika)",
42065         "cz",
42066         "420"
42067       ],
42068       [
42069         "Denmark (Danmark)",
42070         "dk",
42071         "45"
42072       ],
42073       [
42074         "Djibouti",
42075         "dj",
42076         "253"
42077       ],
42078       [
42079         "Dominica",
42080         "dm",
42081         "1767"
42082       ],
42083       [
42084         "Dominican Republic (República Dominicana)",
42085         "do",
42086         "1",
42087         2,
42088         ["809", "829", "849"]
42089       ],
42090       [
42091         "Ecuador",
42092         "ec",
42093         "593"
42094       ],
42095       [
42096         "Egypt (‫مصر‬‎)",
42097         "eg",
42098         "20"
42099       ],
42100       [
42101         "El Salvador",
42102         "sv",
42103         "503"
42104       ],
42105       [
42106         "Equatorial Guinea (Guinea Ecuatorial)",
42107         "gq",
42108         "240"
42109       ],
42110       [
42111         "Eritrea",
42112         "er",
42113         "291"
42114       ],
42115       [
42116         "Estonia (Eesti)",
42117         "ee",
42118         "372"
42119       ],
42120       [
42121         "Ethiopia",
42122         "et",
42123         "251"
42124       ],
42125       [
42126         "Falkland Islands (Islas Malvinas)",
42127         "fk",
42128         "500"
42129       ],
42130       [
42131         "Faroe Islands (Føroyar)",
42132         "fo",
42133         "298"
42134       ],
42135       [
42136         "Fiji",
42137         "fj",
42138         "679"
42139       ],
42140       [
42141         "Finland (Suomi)",
42142         "fi",
42143         "358",
42144         0
42145       ],
42146       [
42147         "France",
42148         "fr",
42149         "33"
42150       ],
42151       [
42152         "French Guiana (Guyane française)",
42153         "gf",
42154         "594"
42155       ],
42156       [
42157         "French Polynesia (Polynésie française)",
42158         "pf",
42159         "689"
42160       ],
42161       [
42162         "Gabon",
42163         "ga",
42164         "241"
42165       ],
42166       [
42167         "Gambia",
42168         "gm",
42169         "220"
42170       ],
42171       [
42172         "Georgia (საქართველო)",
42173         "ge",
42174         "995"
42175       ],
42176       [
42177         "Germany (Deutschland)",
42178         "de",
42179         "49"
42180       ],
42181       [
42182         "Ghana (Gaana)",
42183         "gh",
42184         "233"
42185       ],
42186       [
42187         "Gibraltar",
42188         "gi",
42189         "350"
42190       ],
42191       [
42192         "Greece (Ελλάδα)",
42193         "gr",
42194         "30"
42195       ],
42196       [
42197         "Greenland (Kalaallit Nunaat)",
42198         "gl",
42199         "299"
42200       ],
42201       [
42202         "Grenada",
42203         "gd",
42204         "1473"
42205       ],
42206       [
42207         "Guadeloupe",
42208         "gp",
42209         "590",
42210         0
42211       ],
42212       [
42213         "Guam",
42214         "gu",
42215         "1671"
42216       ],
42217       [
42218         "Guatemala",
42219         "gt",
42220         "502"
42221       ],
42222       [
42223         "Guernsey",
42224         "gg",
42225         "44",
42226         1
42227       ],
42228       [
42229         "Guinea (Guinée)",
42230         "gn",
42231         "224"
42232       ],
42233       [
42234         "Guinea-Bissau (Guiné Bissau)",
42235         "gw",
42236         "245"
42237       ],
42238       [
42239         "Guyana",
42240         "gy",
42241         "592"
42242       ],
42243       [
42244         "Haiti",
42245         "ht",
42246         "509"
42247       ],
42248       [
42249         "Honduras",
42250         "hn",
42251         "504"
42252       ],
42253       [
42254         "Hong Kong (香港)",
42255         "hk",
42256         "852"
42257       ],
42258       [
42259         "Hungary (Magyarország)",
42260         "hu",
42261         "36"
42262       ],
42263       [
42264         "Iceland (Ísland)",
42265         "is",
42266         "354"
42267       ],
42268       [
42269         "India (भारत)",
42270         "in",
42271         "91"
42272       ],
42273       [
42274         "Indonesia",
42275         "id",
42276         "62"
42277       ],
42278       [
42279         "Iran (‫ایران‬‎)",
42280         "ir",
42281         "98"
42282       ],
42283       [
42284         "Iraq (‫العراق‬‎)",
42285         "iq",
42286         "964"
42287       ],
42288       [
42289         "Ireland",
42290         "ie",
42291         "353"
42292       ],
42293       [
42294         "Isle of Man",
42295         "im",
42296         "44",
42297         2
42298       ],
42299       [
42300         "Israel (‫ישראל‬‎)",
42301         "il",
42302         "972"
42303       ],
42304       [
42305         "Italy (Italia)",
42306         "it",
42307         "39",
42308         0
42309       ],
42310       [
42311         "Jamaica",
42312         "jm",
42313         "1876"
42314       ],
42315       [
42316         "Japan (日本)",
42317         "jp",
42318         "81"
42319       ],
42320       [
42321         "Jersey",
42322         "je",
42323         "44",
42324         3
42325       ],
42326       [
42327         "Jordan (‫الأردن‬‎)",
42328         "jo",
42329         "962"
42330       ],
42331       [
42332         "Kazakhstan (Казахстан)",
42333         "kz",
42334         "7",
42335         1
42336       ],
42337       [
42338         "Kenya",
42339         "ke",
42340         "254"
42341       ],
42342       [
42343         "Kiribati",
42344         "ki",
42345         "686"
42346       ],
42347       [
42348         "Kosovo",
42349         "xk",
42350         "383"
42351       ],
42352       [
42353         "Kuwait (‫الكويت‬‎)",
42354         "kw",
42355         "965"
42356       ],
42357       [
42358         "Kyrgyzstan (Кыргызстан)",
42359         "kg",
42360         "996"
42361       ],
42362       [
42363         "Laos (ລາວ)",
42364         "la",
42365         "856"
42366       ],
42367       [
42368         "Latvia (Latvija)",
42369         "lv",
42370         "371"
42371       ],
42372       [
42373         "Lebanon (‫لبنان‬‎)",
42374         "lb",
42375         "961"
42376       ],
42377       [
42378         "Lesotho",
42379         "ls",
42380         "266"
42381       ],
42382       [
42383         "Liberia",
42384         "lr",
42385         "231"
42386       ],
42387       [
42388         "Libya (‫ليبيا‬‎)",
42389         "ly",
42390         "218"
42391       ],
42392       [
42393         "Liechtenstein",
42394         "li",
42395         "423"
42396       ],
42397       [
42398         "Lithuania (Lietuva)",
42399         "lt",
42400         "370"
42401       ],
42402       [
42403         "Luxembourg",
42404         "lu",
42405         "352"
42406       ],
42407       [
42408         "Macau (澳門)",
42409         "mo",
42410         "853"
42411       ],
42412       [
42413         "Macedonia (FYROM) (Македонија)",
42414         "mk",
42415         "389"
42416       ],
42417       [
42418         "Madagascar (Madagasikara)",
42419         "mg",
42420         "261"
42421       ],
42422       [
42423         "Malawi",
42424         "mw",
42425         "265"
42426       ],
42427       [
42428         "Malaysia",
42429         "my",
42430         "60"
42431       ],
42432       [
42433         "Maldives",
42434         "mv",
42435         "960"
42436       ],
42437       [
42438         "Mali",
42439         "ml",
42440         "223"
42441       ],
42442       [
42443         "Malta",
42444         "mt",
42445         "356"
42446       ],
42447       [
42448         "Marshall Islands",
42449         "mh",
42450         "692"
42451       ],
42452       [
42453         "Martinique",
42454         "mq",
42455         "596"
42456       ],
42457       [
42458         "Mauritania (‫موريتانيا‬‎)",
42459         "mr",
42460         "222"
42461       ],
42462       [
42463         "Mauritius (Moris)",
42464         "mu",
42465         "230"
42466       ],
42467       [
42468         "Mayotte",
42469         "yt",
42470         "262",
42471         1
42472       ],
42473       [
42474         "Mexico (México)",
42475         "mx",
42476         "52"
42477       ],
42478       [
42479         "Micronesia",
42480         "fm",
42481         "691"
42482       ],
42483       [
42484         "Moldova (Republica Moldova)",
42485         "md",
42486         "373"
42487       ],
42488       [
42489         "Monaco",
42490         "mc",
42491         "377"
42492       ],
42493       [
42494         "Mongolia (Монгол)",
42495         "mn",
42496         "976"
42497       ],
42498       [
42499         "Montenegro (Crna Gora)",
42500         "me",
42501         "382"
42502       ],
42503       [
42504         "Montserrat",
42505         "ms",
42506         "1664"
42507       ],
42508       [
42509         "Morocco (‫المغرب‬‎)",
42510         "ma",
42511         "212",
42512         0
42513       ],
42514       [
42515         "Mozambique (Moçambique)",
42516         "mz",
42517         "258"
42518       ],
42519       [
42520         "Myanmar (Burma) (မြန်မာ)",
42521         "mm",
42522         "95"
42523       ],
42524       [
42525         "Namibia (Namibië)",
42526         "na",
42527         "264"
42528       ],
42529       [
42530         "Nauru",
42531         "nr",
42532         "674"
42533       ],
42534       [
42535         "Nepal (नेपाल)",
42536         "np",
42537         "977"
42538       ],
42539       [
42540         "Netherlands (Nederland)",
42541         "nl",
42542         "31"
42543       ],
42544       [
42545         "New Caledonia (Nouvelle-Calédonie)",
42546         "nc",
42547         "687"
42548       ],
42549       [
42550         "New Zealand",
42551         "nz",
42552         "64"
42553       ],
42554       [
42555         "Nicaragua",
42556         "ni",
42557         "505"
42558       ],
42559       [
42560         "Niger (Nijar)",
42561         "ne",
42562         "227"
42563       ],
42564       [
42565         "Nigeria",
42566         "ng",
42567         "234"
42568       ],
42569       [
42570         "Niue",
42571         "nu",
42572         "683"
42573       ],
42574       [
42575         "Norfolk Island",
42576         "nf",
42577         "672"
42578       ],
42579       [
42580         "North Korea (조선 민주주의 인민 공화국)",
42581         "kp",
42582         "850"
42583       ],
42584       [
42585         "Northern Mariana Islands",
42586         "mp",
42587         "1670"
42588       ],
42589       [
42590         "Norway (Norge)",
42591         "no",
42592         "47",
42593         0
42594       ],
42595       [
42596         "Oman (‫عُمان‬‎)",
42597         "om",
42598         "968"
42599       ],
42600       [
42601         "Pakistan (‫پاکستان‬‎)",
42602         "pk",
42603         "92"
42604       ],
42605       [
42606         "Palau",
42607         "pw",
42608         "680"
42609       ],
42610       [
42611         "Palestine (‫فلسطين‬‎)",
42612         "ps",
42613         "970"
42614       ],
42615       [
42616         "Panama (Panamá)",
42617         "pa",
42618         "507"
42619       ],
42620       [
42621         "Papua New Guinea",
42622         "pg",
42623         "675"
42624       ],
42625       [
42626         "Paraguay",
42627         "py",
42628         "595"
42629       ],
42630       [
42631         "Peru (Perú)",
42632         "pe",
42633         "51"
42634       ],
42635       [
42636         "Philippines",
42637         "ph",
42638         "63"
42639       ],
42640       [
42641         "Poland (Polska)",
42642         "pl",
42643         "48"
42644       ],
42645       [
42646         "Portugal",
42647         "pt",
42648         "351"
42649       ],
42650       [
42651         "Puerto Rico",
42652         "pr",
42653         "1",
42654         3,
42655         ["787", "939"]
42656       ],
42657       [
42658         "Qatar (‫قطر‬‎)",
42659         "qa",
42660         "974"
42661       ],
42662       [
42663         "Réunion (La Réunion)",
42664         "re",
42665         "262",
42666         0
42667       ],
42668       [
42669         "Romania (România)",
42670         "ro",
42671         "40"
42672       ],
42673       [
42674         "Russia (Россия)",
42675         "ru",
42676         "7",
42677         0
42678       ],
42679       [
42680         "Rwanda",
42681         "rw",
42682         "250"
42683       ],
42684       [
42685         "Saint Barthélemy",
42686         "bl",
42687         "590",
42688         1
42689       ],
42690       [
42691         "Saint Helena",
42692         "sh",
42693         "290"
42694       ],
42695       [
42696         "Saint Kitts and Nevis",
42697         "kn",
42698         "1869"
42699       ],
42700       [
42701         "Saint Lucia",
42702         "lc",
42703         "1758"
42704       ],
42705       [
42706         "Saint Martin (Saint-Martin (partie française))",
42707         "mf",
42708         "590",
42709         2
42710       ],
42711       [
42712         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42713         "pm",
42714         "508"
42715       ],
42716       [
42717         "Saint Vincent and the Grenadines",
42718         "vc",
42719         "1784"
42720       ],
42721       [
42722         "Samoa",
42723         "ws",
42724         "685"
42725       ],
42726       [
42727         "San Marino",
42728         "sm",
42729         "378"
42730       ],
42731       [
42732         "São Tomé and Príncipe (São Tomé e Príncipe)",
42733         "st",
42734         "239"
42735       ],
42736       [
42737         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42738         "sa",
42739         "966"
42740       ],
42741       [
42742         "Senegal (Sénégal)",
42743         "sn",
42744         "221"
42745       ],
42746       [
42747         "Serbia (Србија)",
42748         "rs",
42749         "381"
42750       ],
42751       [
42752         "Seychelles",
42753         "sc",
42754         "248"
42755       ],
42756       [
42757         "Sierra Leone",
42758         "sl",
42759         "232"
42760       ],
42761       [
42762         "Singapore",
42763         "sg",
42764         "65"
42765       ],
42766       [
42767         "Sint Maarten",
42768         "sx",
42769         "1721"
42770       ],
42771       [
42772         "Slovakia (Slovensko)",
42773         "sk",
42774         "421"
42775       ],
42776       [
42777         "Slovenia (Slovenija)",
42778         "si",
42779         "386"
42780       ],
42781       [
42782         "Solomon Islands",
42783         "sb",
42784         "677"
42785       ],
42786       [
42787         "Somalia (Soomaaliya)",
42788         "so",
42789         "252"
42790       ],
42791       [
42792         "South Africa",
42793         "za",
42794         "27"
42795       ],
42796       [
42797         "South Korea (대한민국)",
42798         "kr",
42799         "82"
42800       ],
42801       [
42802         "South Sudan (‫جنوب السودان‬‎)",
42803         "ss",
42804         "211"
42805       ],
42806       [
42807         "Spain (España)",
42808         "es",
42809         "34"
42810       ],
42811       [
42812         "Sri Lanka (ශ්‍රී ලංකාව)",
42813         "lk",
42814         "94"
42815       ],
42816       [
42817         "Sudan (‫السودان‬‎)",
42818         "sd",
42819         "249"
42820       ],
42821       [
42822         "Suriname",
42823         "sr",
42824         "597"
42825       ],
42826       [
42827         "Svalbard and Jan Mayen",
42828         "sj",
42829         "47",
42830         1
42831       ],
42832       [
42833         "Swaziland",
42834         "sz",
42835         "268"
42836       ],
42837       [
42838         "Sweden (Sverige)",
42839         "se",
42840         "46"
42841       ],
42842       [
42843         "Switzerland (Schweiz)",
42844         "ch",
42845         "41"
42846       ],
42847       [
42848         "Syria (‫سوريا‬‎)",
42849         "sy",
42850         "963"
42851       ],
42852       [
42853         "Taiwan (台灣)",
42854         "tw",
42855         "886"
42856       ],
42857       [
42858         "Tajikistan",
42859         "tj",
42860         "992"
42861       ],
42862       [
42863         "Tanzania",
42864         "tz",
42865         "255"
42866       ],
42867       [
42868         "Thailand (ไทย)",
42869         "th",
42870         "66"
42871       ],
42872       [
42873         "Timor-Leste",
42874         "tl",
42875         "670"
42876       ],
42877       [
42878         "Togo",
42879         "tg",
42880         "228"
42881       ],
42882       [
42883         "Tokelau",
42884         "tk",
42885         "690"
42886       ],
42887       [
42888         "Tonga",
42889         "to",
42890         "676"
42891       ],
42892       [
42893         "Trinidad and Tobago",
42894         "tt",
42895         "1868"
42896       ],
42897       [
42898         "Tunisia (‫تونس‬‎)",
42899         "tn",
42900         "216"
42901       ],
42902       [
42903         "Turkey (Türkiye)",
42904         "tr",
42905         "90"
42906       ],
42907       [
42908         "Turkmenistan",
42909         "tm",
42910         "993"
42911       ],
42912       [
42913         "Turks and Caicos Islands",
42914         "tc",
42915         "1649"
42916       ],
42917       [
42918         "Tuvalu",
42919         "tv",
42920         "688"
42921       ],
42922       [
42923         "U.S. Virgin Islands",
42924         "vi",
42925         "1340"
42926       ],
42927       [
42928         "Uganda",
42929         "ug",
42930         "256"
42931       ],
42932       [
42933         "Ukraine (Україна)",
42934         "ua",
42935         "380"
42936       ],
42937       [
42938         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42939         "ae",
42940         "971"
42941       ],
42942       [
42943         "United Kingdom",
42944         "gb",
42945         "44",
42946         0
42947       ],
42948       [
42949         "United States",
42950         "us",
42951         "1",
42952         0
42953       ],
42954       [
42955         "Uruguay",
42956         "uy",
42957         "598"
42958       ],
42959       [
42960         "Uzbekistan (Oʻzbekiston)",
42961         "uz",
42962         "998"
42963       ],
42964       [
42965         "Vanuatu",
42966         "vu",
42967         "678"
42968       ],
42969       [
42970         "Vatican City (Città del Vaticano)",
42971         "va",
42972         "39",
42973         1
42974       ],
42975       [
42976         "Venezuela",
42977         "ve",
42978         "58"
42979       ],
42980       [
42981         "Vietnam (Việt Nam)",
42982         "vn",
42983         "84"
42984       ],
42985       [
42986         "Wallis and Futuna (Wallis-et-Futuna)",
42987         "wf",
42988         "681"
42989       ],
42990       [
42991         "Western Sahara (‫الصحراء الغربية‬‎)",
42992         "eh",
42993         "212",
42994         1
42995       ],
42996       [
42997         "Yemen (‫اليمن‬‎)",
42998         "ye",
42999         "967"
43000       ],
43001       [
43002         "Zambia",
43003         "zm",
43004         "260"
43005       ],
43006       [
43007         "Zimbabwe",
43008         "zw",
43009         "263"
43010       ],
43011       [
43012         "Åland Islands",
43013         "ax",
43014         "358",
43015         1
43016       ]
43017   ];
43018   
43019   return d;
43020 }/**
43021 *    This script refer to:
43022 *    Title: International Telephone Input
43023 *    Author: Jack O'Connor
43024 *    Code version:  v12.1.12
43025 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43026 **/
43027
43028 /**
43029  * @class Roo.bootstrap.PhoneInput
43030  * @extends Roo.bootstrap.TriggerField
43031  * An input with International dial-code selection
43032  
43033  * @cfg {String} defaultDialCode default '+852'
43034  * @cfg {Array} preferedCountries default []
43035   
43036  * @constructor
43037  * Create a new PhoneInput.
43038  * @param {Object} config Configuration options
43039  */
43040
43041 Roo.bootstrap.PhoneInput = function(config) {
43042     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43043 };
43044
43045 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43046         
43047         listWidth: undefined,
43048         
43049         selectedClass: 'active',
43050         
43051         invalidClass : "has-warning",
43052         
43053         validClass: 'has-success',
43054         
43055         allowed: '0123456789',
43056         
43057         max_length: 15,
43058         
43059         /**
43060          * @cfg {String} defaultDialCode The default dial code when initializing the input
43061          */
43062         defaultDialCode: '+852',
43063         
43064         /**
43065          * @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
43066          */
43067         preferedCountries: false,
43068         
43069         getAutoCreate : function()
43070         {
43071             var data = Roo.bootstrap.PhoneInputData();
43072             var align = this.labelAlign || this.parentLabelAlign();
43073             var id = Roo.id();
43074             
43075             this.allCountries = [];
43076             this.dialCodeMapping = [];
43077             
43078             for (var i = 0; i < data.length; i++) {
43079               var c = data[i];
43080               this.allCountries[i] = {
43081                 name: c[0],
43082                 iso2: c[1],
43083                 dialCode: c[2],
43084                 priority: c[3] || 0,
43085                 areaCodes: c[4] || null
43086               };
43087               this.dialCodeMapping[c[2]] = {
43088                   name: c[0],
43089                   iso2: c[1],
43090                   priority: c[3] || 0,
43091                   areaCodes: c[4] || null
43092               };
43093             }
43094             
43095             var cfg = {
43096                 cls: 'form-group',
43097                 cn: []
43098             };
43099             
43100             var input =  {
43101                 tag: 'input',
43102                 id : id,
43103                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43104                 maxlength: this.max_length,
43105                 cls : 'form-control tel-input',
43106                 autocomplete: 'new-password'
43107             };
43108             
43109             var hiddenInput = {
43110                 tag: 'input',
43111                 type: 'hidden',
43112                 cls: 'hidden-tel-input'
43113             };
43114             
43115             if (this.name) {
43116                 hiddenInput.name = this.name;
43117             }
43118             
43119             if (this.disabled) {
43120                 input.disabled = true;
43121             }
43122             
43123             var flag_container = {
43124                 tag: 'div',
43125                 cls: 'flag-box',
43126                 cn: [
43127                     {
43128                         tag: 'div',
43129                         cls: 'flag'
43130                     },
43131                     {
43132                         tag: 'div',
43133                         cls: 'caret'
43134                     }
43135                 ]
43136             };
43137             
43138             var box = {
43139                 tag: 'div',
43140                 cls: this.hasFeedback ? 'has-feedback' : '',
43141                 cn: [
43142                     hiddenInput,
43143                     input,
43144                     {
43145                         tag: 'input',
43146                         cls: 'dial-code-holder',
43147                         disabled: true
43148                     }
43149                 ]
43150             };
43151             
43152             var container = {
43153                 cls: 'roo-select2-container input-group',
43154                 cn: [
43155                     flag_container,
43156                     box
43157                 ]
43158             };
43159             
43160             if (this.fieldLabel.length) {
43161                 var indicator = {
43162                     tag: 'i',
43163                     tooltip: 'This field is required'
43164                 };
43165                 
43166                 var label = {
43167                     tag: 'label',
43168                     'for':  id,
43169                     cls: 'control-label',
43170                     cn: []
43171                 };
43172                 
43173                 var label_text = {
43174                     tag: 'span',
43175                     html: this.fieldLabel
43176                 };
43177                 
43178                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43179                 label.cn = [
43180                     indicator,
43181                     label_text
43182                 ];
43183                 
43184                 if(this.indicatorpos == 'right') {
43185                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43186                     label.cn = [
43187                         label_text,
43188                         indicator
43189                     ];
43190                 }
43191                 
43192                 if(align == 'left') {
43193                     container = {
43194                         tag: 'div',
43195                         cn: [
43196                             container
43197                         ]
43198                     };
43199                     
43200                     if(this.labelWidth > 12){
43201                         label.style = "width: " + this.labelWidth + 'px';
43202                     }
43203                     if(this.labelWidth < 13 && this.labelmd == 0){
43204                         this.labelmd = this.labelWidth;
43205                     }
43206                     if(this.labellg > 0){
43207                         label.cls += ' col-lg-' + this.labellg;
43208                         input.cls += ' col-lg-' + (12 - this.labellg);
43209                     }
43210                     if(this.labelmd > 0){
43211                         label.cls += ' col-md-' + this.labelmd;
43212                         container.cls += ' col-md-' + (12 - this.labelmd);
43213                     }
43214                     if(this.labelsm > 0){
43215                         label.cls += ' col-sm-' + this.labelsm;
43216                         container.cls += ' col-sm-' + (12 - this.labelsm);
43217                     }
43218                     if(this.labelxs > 0){
43219                         label.cls += ' col-xs-' + this.labelxs;
43220                         container.cls += ' col-xs-' + (12 - this.labelxs);
43221                     }
43222                 }
43223             }
43224             
43225             cfg.cn = [
43226                 label,
43227                 container
43228             ];
43229             
43230             var settings = this;
43231             
43232             ['xs','sm','md','lg'].map(function(size){
43233                 if (settings[size]) {
43234                     cfg.cls += ' col-' + size + '-' + settings[size];
43235                 }
43236             });
43237             
43238             this.store = new Roo.data.Store({
43239                 proxy : new Roo.data.MemoryProxy({}),
43240                 reader : new Roo.data.JsonReader({
43241                     fields : [
43242                         {
43243                             'name' : 'name',
43244                             'type' : 'string'
43245                         },
43246                         {
43247                             'name' : 'iso2',
43248                             'type' : 'string'
43249                         },
43250                         {
43251                             'name' : 'dialCode',
43252                             'type' : 'string'
43253                         },
43254                         {
43255                             'name' : 'priority',
43256                             'type' : 'string'
43257                         },
43258                         {
43259                             'name' : 'areaCodes',
43260                             'type' : 'string'
43261                         }
43262                     ]
43263                 })
43264             });
43265             
43266             if(!this.preferedCountries) {
43267                 this.preferedCountries = [
43268                     'hk',
43269                     'gb',
43270                     'us'
43271                 ];
43272             }
43273             
43274             var p = this.preferedCountries.reverse();
43275             
43276             if(p) {
43277                 for (var i = 0; i < p.length; i++) {
43278                     for (var j = 0; j < this.allCountries.length; j++) {
43279                         if(this.allCountries[j].iso2 == p[i]) {
43280                             var t = this.allCountries[j];
43281                             this.allCountries.splice(j,1);
43282                             this.allCountries.unshift(t);
43283                         }
43284                     } 
43285                 }
43286             }
43287             
43288             this.store.proxy.data = {
43289                 success: true,
43290                 data: this.allCountries
43291             };
43292             
43293             return cfg;
43294         },
43295         
43296         initEvents : function()
43297         {
43298             this.createList();
43299             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43300             
43301             this.indicator = this.indicatorEl();
43302             this.flag = this.flagEl();
43303             this.dialCodeHolder = this.dialCodeHolderEl();
43304             
43305             this.trigger = this.el.select('div.flag-box',true).first();
43306             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43307             
43308             var _this = this;
43309             
43310             (function(){
43311                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43312                 _this.list.setWidth(lw);
43313             }).defer(100);
43314             
43315             this.list.on('mouseover', this.onViewOver, this);
43316             this.list.on('mousemove', this.onViewMove, this);
43317             this.inputEl().on("keyup", this.onKeyUp, this);
43318             this.inputEl().on("keypress", this.onKeyPress, this);
43319             
43320             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43321
43322             this.view = new Roo.View(this.list, this.tpl, {
43323                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43324             });
43325             
43326             this.view.on('click', this.onViewClick, this);
43327             this.setValue(this.defaultDialCode);
43328         },
43329         
43330         onTriggerClick : function(e)
43331         {
43332             Roo.log('trigger click');
43333             if(this.disabled){
43334                 return;
43335             }
43336             
43337             if(this.isExpanded()){
43338                 this.collapse();
43339                 this.hasFocus = false;
43340             }else {
43341                 this.store.load({});
43342                 this.hasFocus = true;
43343                 this.expand();
43344             }
43345         },
43346         
43347         isExpanded : function()
43348         {
43349             return this.list.isVisible();
43350         },
43351         
43352         collapse : function()
43353         {
43354             if(!this.isExpanded()){
43355                 return;
43356             }
43357             this.list.hide();
43358             Roo.get(document).un('mousedown', this.collapseIf, this);
43359             Roo.get(document).un('mousewheel', this.collapseIf, this);
43360             this.fireEvent('collapse', this);
43361             this.validate();
43362         },
43363         
43364         expand : function()
43365         {
43366             Roo.log('expand');
43367
43368             if(this.isExpanded() || !this.hasFocus){
43369                 return;
43370             }
43371             
43372             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43373             this.list.setWidth(lw);
43374             
43375             this.list.show();
43376             this.restrictHeight();
43377             
43378             Roo.get(document).on('mousedown', this.collapseIf, this);
43379             Roo.get(document).on('mousewheel', this.collapseIf, this);
43380             
43381             this.fireEvent('expand', this);
43382         },
43383         
43384         restrictHeight : function()
43385         {
43386             this.list.alignTo(this.inputEl(), this.listAlign);
43387             this.list.alignTo(this.inputEl(), this.listAlign);
43388         },
43389         
43390         onViewOver : function(e, t)
43391         {
43392             if(this.inKeyMode){
43393                 return;
43394             }
43395             var item = this.view.findItemFromChild(t);
43396             
43397             if(item){
43398                 var index = this.view.indexOf(item);
43399                 this.select(index, false);
43400             }
43401         },
43402
43403         // private
43404         onViewClick : function(view, doFocus, el, e)
43405         {
43406             var index = this.view.getSelectedIndexes()[0];
43407             
43408             var r = this.store.getAt(index);
43409             
43410             if(r){
43411                 this.onSelect(r, index);
43412             }
43413             if(doFocus !== false && !this.blockFocus){
43414                 this.inputEl().focus();
43415             }
43416         },
43417         
43418         onViewMove : function(e, t)
43419         {
43420             this.inKeyMode = false;
43421         },
43422         
43423         select : function(index, scrollIntoView)
43424         {
43425             this.selectedIndex = index;
43426             this.view.select(index);
43427             if(scrollIntoView !== false){
43428                 var el = this.view.getNode(index);
43429                 if(el){
43430                     this.list.scrollChildIntoView(el, false);
43431                 }
43432             }
43433         },
43434         
43435         createList : function()
43436         {
43437             this.list = Roo.get(document.body).createChild({
43438                 tag: 'ul',
43439                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43440                 style: 'display:none'
43441             });
43442             
43443             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43444         },
43445         
43446         collapseIf : function(e)
43447         {
43448             var in_combo  = e.within(this.el);
43449             var in_list =  e.within(this.list);
43450             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43451             
43452             if (in_combo || in_list || is_list) {
43453                 return;
43454             }
43455             this.collapse();
43456         },
43457         
43458         onSelect : function(record, index)
43459         {
43460             if(this.fireEvent('beforeselect', this, record, index) !== false){
43461                 
43462                 this.setFlagClass(record.data.iso2);
43463                 this.setDialCode(record.data.dialCode);
43464                 this.hasFocus = false;
43465                 this.collapse();
43466                 this.fireEvent('select', this, record, index);
43467             }
43468         },
43469         
43470         flagEl : function()
43471         {
43472             var flag = this.el.select('div.flag',true).first();
43473             if(!flag){
43474                 return false;
43475             }
43476             return flag;
43477         },
43478         
43479         dialCodeHolderEl : function()
43480         {
43481             var d = this.el.select('input.dial-code-holder',true).first();
43482             if(!d){
43483                 return false;
43484             }
43485             return d;
43486         },
43487         
43488         setDialCode : function(v)
43489         {
43490             this.dialCodeHolder.dom.value = '+'+v;
43491         },
43492         
43493         setFlagClass : function(n)
43494         {
43495             this.flag.dom.className = 'flag '+n;
43496         },
43497         
43498         getValue : function()
43499         {
43500             var v = this.inputEl().getValue();
43501             if(this.dialCodeHolder) {
43502                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43503             }
43504             return v;
43505         },
43506         
43507         setValue : function(v)
43508         {
43509             var d = this.getDialCode(v);
43510             
43511             //invalid dial code
43512             if(v.length == 0 || !d || d.length == 0) {
43513                 if(this.rendered){
43514                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43515                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43516                 }
43517                 return;
43518             }
43519             
43520             //valid dial code
43521             this.setFlagClass(this.dialCodeMapping[d].iso2);
43522             this.setDialCode(d);
43523             this.inputEl().dom.value = v.replace('+'+d,'');
43524             this.hiddenEl().dom.value = this.getValue();
43525             
43526             this.validate();
43527         },
43528         
43529         getDialCode : function(v)
43530         {
43531             v = v ||  '';
43532             
43533             if (v.length == 0) {
43534                 return this.dialCodeHolder.dom.value;
43535             }
43536             
43537             var dialCode = "";
43538             if (v.charAt(0) != "+") {
43539                 return false;
43540             }
43541             var numericChars = "";
43542             for (var i = 1; i < v.length; i++) {
43543               var c = v.charAt(i);
43544               if (!isNaN(c)) {
43545                 numericChars += c;
43546                 if (this.dialCodeMapping[numericChars]) {
43547                   dialCode = v.substr(1, i);
43548                 }
43549                 if (numericChars.length == 4) {
43550                   break;
43551                 }
43552               }
43553             }
43554             return dialCode;
43555         },
43556         
43557         reset : function()
43558         {
43559             this.setValue(this.defaultDialCode);
43560             this.validate();
43561         },
43562         
43563         hiddenEl : function()
43564         {
43565             return this.el.select('input.hidden-tel-input',true).first();
43566         },
43567         
43568         // after setting val
43569         onKeyUp : function(e){
43570             this.setValue(this.getValue());
43571         },
43572         
43573         onKeyPress : function(e){
43574             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43575                 e.stopEvent();
43576             }
43577         }
43578         
43579 });
43580 /**
43581  * @class Roo.bootstrap.MoneyField
43582  * @extends Roo.bootstrap.ComboBox
43583  * Bootstrap MoneyField class
43584  * 
43585  * @constructor
43586  * Create a new MoneyField.
43587  * @param {Object} config Configuration options
43588  */
43589
43590 Roo.bootstrap.MoneyField = function(config) {
43591     
43592     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43593     
43594 };
43595
43596 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43597     
43598     /**
43599      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43600      */
43601     allowDecimals : true,
43602     /**
43603      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43604      */
43605     decimalSeparator : ".",
43606     /**
43607      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43608      */
43609     decimalPrecision : 0,
43610     /**
43611      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43612      */
43613     allowNegative : true,
43614     /**
43615      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43616      */
43617     allowZero: true,
43618     /**
43619      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43620      */
43621     minValue : Number.NEGATIVE_INFINITY,
43622     /**
43623      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43624      */
43625     maxValue : Number.MAX_VALUE,
43626     /**
43627      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43628      */
43629     minText : "The minimum value for this field is {0}",
43630     /**
43631      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43632      */
43633     maxText : "The maximum value for this field is {0}",
43634     /**
43635      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43636      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43637      */
43638     nanText : "{0} is not a valid number",
43639     /**
43640      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43641      */
43642     castInt : true,
43643     /**
43644      * @cfg {String} defaults currency of the MoneyField
43645      * value should be in lkey
43646      */
43647     defaultCurrency : false,
43648     /**
43649      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43650      */
43651     thousandsDelimiter : false,
43652     /**
43653      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43654      */
43655     max_length: false,
43656     
43657     inputlg : 9,
43658     inputmd : 9,
43659     inputsm : 9,
43660     inputxs : 6,
43661     
43662     store : false,
43663     
43664     getAutoCreate : function()
43665     {
43666         var align = this.labelAlign || this.parentLabelAlign();
43667         
43668         var id = Roo.id();
43669
43670         var cfg = {
43671             cls: 'form-group',
43672             cn: []
43673         };
43674
43675         var input =  {
43676             tag: 'input',
43677             id : id,
43678             cls : 'form-control roo-money-amount-input',
43679             autocomplete: 'new-password'
43680         };
43681         
43682         var hiddenInput = {
43683             tag: 'input',
43684             type: 'hidden',
43685             id: Roo.id(),
43686             cls: 'hidden-number-input'
43687         };
43688         
43689         if(this.max_length) {
43690             input.maxlength = this.max_length; 
43691         }
43692         
43693         if (this.name) {
43694             hiddenInput.name = this.name;
43695         }
43696
43697         if (this.disabled) {
43698             input.disabled = true;
43699         }
43700
43701         var clg = 12 - this.inputlg;
43702         var cmd = 12 - this.inputmd;
43703         var csm = 12 - this.inputsm;
43704         var cxs = 12 - this.inputxs;
43705         
43706         var container = {
43707             tag : 'div',
43708             cls : 'row roo-money-field',
43709             cn : [
43710                 {
43711                     tag : 'div',
43712                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43713                     cn : [
43714                         {
43715                             tag : 'div',
43716                             cls: 'roo-select2-container input-group',
43717                             cn: [
43718                                 {
43719                                     tag : 'input',
43720                                     cls : 'form-control roo-money-currency-input',
43721                                     autocomplete: 'new-password',
43722                                     readOnly : 1,
43723                                     name : this.currencyName
43724                                 },
43725                                 {
43726                                     tag :'span',
43727                                     cls : 'input-group-addon',
43728                                     cn : [
43729                                         {
43730                                             tag: 'span',
43731                                             cls: 'caret'
43732                                         }
43733                                     ]
43734                                 }
43735                             ]
43736                         }
43737                     ]
43738                 },
43739                 {
43740                     tag : 'div',
43741                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43742                     cn : [
43743                         {
43744                             tag: 'div',
43745                             cls: this.hasFeedback ? 'has-feedback' : '',
43746                             cn: [
43747                                 input
43748                             ]
43749                         }
43750                     ]
43751                 }
43752             ]
43753             
43754         };
43755         
43756         if (this.fieldLabel.length) {
43757             var indicator = {
43758                 tag: 'i',
43759                 tooltip: 'This field is required'
43760             };
43761
43762             var label = {
43763                 tag: 'label',
43764                 'for':  id,
43765                 cls: 'control-label',
43766                 cn: []
43767             };
43768
43769             var label_text = {
43770                 tag: 'span',
43771                 html: this.fieldLabel
43772             };
43773
43774             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43775             label.cn = [
43776                 indicator,
43777                 label_text
43778             ];
43779
43780             if(this.indicatorpos == 'right') {
43781                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43782                 label.cn = [
43783                     label_text,
43784                     indicator
43785                 ];
43786             }
43787
43788             if(align == 'left') {
43789                 container = {
43790                     tag: 'div',
43791                     cn: [
43792                         container
43793                     ]
43794                 };
43795
43796                 if(this.labelWidth > 12){
43797                     label.style = "width: " + this.labelWidth + 'px';
43798                 }
43799                 if(this.labelWidth < 13 && this.labelmd == 0){
43800                     this.labelmd = this.labelWidth;
43801                 }
43802                 if(this.labellg > 0){
43803                     label.cls += ' col-lg-' + this.labellg;
43804                     input.cls += ' col-lg-' + (12 - this.labellg);
43805                 }
43806                 if(this.labelmd > 0){
43807                     label.cls += ' col-md-' + this.labelmd;
43808                     container.cls += ' col-md-' + (12 - this.labelmd);
43809                 }
43810                 if(this.labelsm > 0){
43811                     label.cls += ' col-sm-' + this.labelsm;
43812                     container.cls += ' col-sm-' + (12 - this.labelsm);
43813                 }
43814                 if(this.labelxs > 0){
43815                     label.cls += ' col-xs-' + this.labelxs;
43816                     container.cls += ' col-xs-' + (12 - this.labelxs);
43817                 }
43818             }
43819         }
43820
43821         cfg.cn = [
43822             label,
43823             container,
43824             hiddenInput
43825         ];
43826         
43827         var settings = this;
43828
43829         ['xs','sm','md','lg'].map(function(size){
43830             if (settings[size]) {
43831                 cfg.cls += ' col-' + size + '-' + settings[size];
43832             }
43833         });
43834         
43835         return cfg;
43836     },
43837     
43838     initEvents : function()
43839     {
43840         this.indicator = this.indicatorEl();
43841         
43842         this.initCurrencyEvent();
43843         
43844         this.initNumberEvent();
43845     },
43846     
43847     initCurrencyEvent : function()
43848     {
43849         if (!this.store) {
43850             throw "can not find store for combo";
43851         }
43852         
43853         this.store = Roo.factory(this.store, Roo.data);
43854         this.store.parent = this;
43855         
43856         this.createList();
43857         
43858         this.triggerEl = this.el.select('.input-group-addon', true).first();
43859         
43860         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43861         
43862         var _this = this;
43863         
43864         (function(){
43865             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43866             _this.list.setWidth(lw);
43867         }).defer(100);
43868         
43869         this.list.on('mouseover', this.onViewOver, this);
43870         this.list.on('mousemove', this.onViewMove, this);
43871         this.list.on('scroll', this.onViewScroll, this);
43872         
43873         if(!this.tpl){
43874             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43875         }
43876         
43877         this.view = new Roo.View(this.list, this.tpl, {
43878             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43879         });
43880         
43881         this.view.on('click', this.onViewClick, this);
43882         
43883         this.store.on('beforeload', this.onBeforeLoad, this);
43884         this.store.on('load', this.onLoad, this);
43885         this.store.on('loadexception', this.onLoadException, this);
43886         
43887         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43888             "up" : function(e){
43889                 this.inKeyMode = true;
43890                 this.selectPrev();
43891             },
43892
43893             "down" : function(e){
43894                 if(!this.isExpanded()){
43895                     this.onTriggerClick();
43896                 }else{
43897                     this.inKeyMode = true;
43898                     this.selectNext();
43899                 }
43900             },
43901
43902             "enter" : function(e){
43903                 this.collapse();
43904                 
43905                 if(this.fireEvent("specialkey", this, e)){
43906                     this.onViewClick(false);
43907                 }
43908                 
43909                 return true;
43910             },
43911
43912             "esc" : function(e){
43913                 this.collapse();
43914             },
43915
43916             "tab" : function(e){
43917                 this.collapse();
43918                 
43919                 if(this.fireEvent("specialkey", this, e)){
43920                     this.onViewClick(false);
43921                 }
43922                 
43923                 return true;
43924             },
43925
43926             scope : this,
43927
43928             doRelay : function(foo, bar, hname){
43929                 if(hname == 'down' || this.scope.isExpanded()){
43930                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43931                 }
43932                 return true;
43933             },
43934
43935             forceKeyDown: true
43936         });
43937         
43938         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43939         
43940     },
43941     
43942     initNumberEvent : function(e)
43943     {
43944         this.inputEl().on("keydown" , this.fireKey,  this);
43945         this.inputEl().on("focus", this.onFocus,  this);
43946         this.inputEl().on("blur", this.onBlur,  this);
43947         
43948         this.inputEl().relayEvent('keyup', this);
43949         
43950         if(this.indicator){
43951             this.indicator.addClass('invisible');
43952         }
43953  
43954         this.originalValue = this.getValue();
43955         
43956         if(this.validationEvent == 'keyup'){
43957             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43958             this.inputEl().on('keyup', this.filterValidation, this);
43959         }
43960         else if(this.validationEvent !== false){
43961             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43962         }
43963         
43964         if(this.selectOnFocus){
43965             this.on("focus", this.preFocus, this);
43966             
43967         }
43968         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43969             this.inputEl().on("keypress", this.filterKeys, this);
43970         } else {
43971             this.inputEl().relayEvent('keypress', this);
43972         }
43973         
43974         var allowed = "0123456789";
43975         
43976         if(this.allowDecimals){
43977             allowed += this.decimalSeparator;
43978         }
43979         
43980         if(this.allowNegative){
43981             allowed += "-";
43982         }
43983         
43984         if(this.thousandsDelimiter) {
43985             allowed += ",";
43986         }
43987         
43988         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43989         
43990         var keyPress = function(e){
43991             
43992             var k = e.getKey();
43993             
43994             var c = e.getCharCode();
43995             
43996             if(
43997                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43998                     allowed.indexOf(String.fromCharCode(c)) === -1
43999             ){
44000                 e.stopEvent();
44001                 return;
44002             }
44003             
44004             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44005                 return;
44006             }
44007             
44008             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44009                 e.stopEvent();
44010             }
44011         };
44012         
44013         this.inputEl().on("keypress", keyPress, this);
44014         
44015     },
44016     
44017     onTriggerClick : function(e)
44018     {   
44019         if(this.disabled){
44020             return;
44021         }
44022         
44023         this.page = 0;
44024         this.loadNext = false;
44025         
44026         if(this.isExpanded()){
44027             this.collapse();
44028             return;
44029         }
44030         
44031         this.hasFocus = true;
44032         
44033         if(this.triggerAction == 'all') {
44034             this.doQuery(this.allQuery, true);
44035             return;
44036         }
44037         
44038         this.doQuery(this.getRawValue());
44039     },
44040     
44041     getCurrency : function()
44042     {   
44043         var v = this.currencyEl().getValue();
44044         
44045         return v;
44046     },
44047     
44048     restrictHeight : function()
44049     {
44050         this.list.alignTo(this.currencyEl(), this.listAlign);
44051         this.list.alignTo(this.currencyEl(), this.listAlign);
44052     },
44053     
44054     onViewClick : function(view, doFocus, el, e)
44055     {
44056         var index = this.view.getSelectedIndexes()[0];
44057         
44058         var r = this.store.getAt(index);
44059         
44060         if(r){
44061             this.onSelect(r, index);
44062         }
44063     },
44064     
44065     onSelect : function(record, index){
44066         
44067         if(this.fireEvent('beforeselect', this, record, index) !== false){
44068         
44069             this.setFromCurrencyData(index > -1 ? record.data : false);
44070             
44071             this.collapse();
44072             
44073             this.fireEvent('select', this, record, index);
44074         }
44075     },
44076     
44077     setFromCurrencyData : function(o)
44078     {
44079         var currency = '';
44080         
44081         this.lastCurrency = o;
44082         
44083         if (this.currencyField) {
44084             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44085         } else {
44086             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44087         }
44088         
44089         this.lastSelectionText = currency;
44090         
44091         //setting default currency
44092         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44093             this.setCurrency(this.defaultCurrency);
44094             return;
44095         }
44096         
44097         this.setCurrency(currency);
44098     },
44099     
44100     setFromData : function(o)
44101     {
44102         var c = {};
44103         
44104         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44105         
44106         this.setFromCurrencyData(c);
44107         
44108         var value = '';
44109         
44110         if (this.name) {
44111             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44112         } else {
44113             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44114         }
44115         
44116         this.setValue(value);
44117         
44118     },
44119     
44120     setCurrency : function(v)
44121     {   
44122         this.currencyValue = v;
44123         
44124         if(this.rendered){
44125             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44126             this.validate();
44127         }
44128     },
44129     
44130     setValue : function(v)
44131     {
44132         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44133         
44134         this.value = v;
44135         
44136         if(this.rendered){
44137             
44138             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44139             
44140             this.inputEl().dom.value = (v == '') ? '' :
44141                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44142             
44143             if(!this.allowZero && v === '0') {
44144                 this.hiddenEl().dom.value = '';
44145                 this.inputEl().dom.value = '';
44146             }
44147             
44148             this.validate();
44149         }
44150     },
44151     
44152     getRawValue : function()
44153     {
44154         var v = this.inputEl().getValue();
44155         
44156         return v;
44157     },
44158     
44159     getValue : function()
44160     {
44161         return this.fixPrecision(this.parseValue(this.getRawValue()));
44162     },
44163     
44164     parseValue : function(value)
44165     {
44166         if(this.thousandsDelimiter) {
44167             value += "";
44168             r = new RegExp(",", "g");
44169             value = value.replace(r, "");
44170         }
44171         
44172         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44173         return isNaN(value) ? '' : value;
44174         
44175     },
44176     
44177     fixPrecision : function(value)
44178     {
44179         if(this.thousandsDelimiter) {
44180             value += "";
44181             r = new RegExp(",", "g");
44182             value = value.replace(r, "");
44183         }
44184         
44185         var nan = isNaN(value);
44186         
44187         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44188             return nan ? '' : value;
44189         }
44190         return parseFloat(value).toFixed(this.decimalPrecision);
44191     },
44192     
44193     decimalPrecisionFcn : function(v)
44194     {
44195         return Math.floor(v);
44196     },
44197     
44198     validateValue : function(value)
44199     {
44200         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44201             return false;
44202         }
44203         
44204         var num = this.parseValue(value);
44205         
44206         if(isNaN(num)){
44207             this.markInvalid(String.format(this.nanText, value));
44208             return false;
44209         }
44210         
44211         if(num < this.minValue){
44212             this.markInvalid(String.format(this.minText, this.minValue));
44213             return false;
44214         }
44215         
44216         if(num > this.maxValue){
44217             this.markInvalid(String.format(this.maxText, this.maxValue));
44218             return false;
44219         }
44220         
44221         return true;
44222     },
44223     
44224     validate : function()
44225     {
44226         if(this.disabled || this.allowBlank){
44227             this.markValid();
44228             return true;
44229         }
44230         
44231         var currency = this.getCurrency();
44232         
44233         if(this.validateValue(this.getRawValue()) && currency.length){
44234             this.markValid();
44235             return true;
44236         }
44237         
44238         this.markInvalid();
44239         return false;
44240     },
44241     
44242     getName: function()
44243     {
44244         return this.name;
44245     },
44246     
44247     beforeBlur : function()
44248     {
44249         if(!this.castInt){
44250             return;
44251         }
44252         
44253         var v = this.parseValue(this.getRawValue());
44254         
44255         if(v || v == 0){
44256             this.setValue(v);
44257         }
44258     },
44259     
44260     onBlur : function()
44261     {
44262         this.beforeBlur();
44263         
44264         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44265             //this.el.removeClass(this.focusClass);
44266         }
44267         
44268         this.hasFocus = false;
44269         
44270         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44271             this.validate();
44272         }
44273         
44274         var v = this.getValue();
44275         
44276         if(String(v) !== String(this.startValue)){
44277             this.fireEvent('change', this, v, this.startValue);
44278         }
44279         
44280         this.fireEvent("blur", this);
44281     },
44282     
44283     inputEl : function()
44284     {
44285         return this.el.select('.roo-money-amount-input', true).first();
44286     },
44287     
44288     currencyEl : function()
44289     {
44290         return this.el.select('.roo-money-currency-input', true).first();
44291     },
44292     
44293     hiddenEl : function()
44294     {
44295         return this.el.select('input.hidden-number-input',true).first();
44296     }
44297     
44298 });/**
44299  * @class Roo.bootstrap.BezierSignature
44300  * @extends Roo.bootstrap.Component
44301  * Bootstrap BezierSignature class
44302  * This script refer to:
44303  *    Title: Signature Pad
44304  *    Author: szimek
44305  *    Availability: https://github.com/szimek/signature_pad
44306  *
44307  * @constructor
44308  * Create a new BezierSignature
44309  * @param {Object} config The config object
44310  */
44311
44312 Roo.bootstrap.BezierSignature = function(config){
44313     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44314     this.addEvents({
44315         "resize" : true
44316     });
44317 };
44318
44319 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44320 {
44321      
44322     curve_data: [],
44323     
44324     is_empty: true,
44325     
44326     mouse_btn_down: true,
44327     
44328     /**
44329      * @cfg {int} canvas height
44330      */
44331     canvas_height: '200px',
44332     
44333     /**
44334      * @cfg {float|function} Radius of a single dot.
44335      */ 
44336     dot_size: false,
44337     
44338     /**
44339      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44340      */
44341     min_width: 0.5,
44342     
44343     /**
44344      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44345      */
44346     max_width: 2.5,
44347     
44348     /**
44349      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44350      */
44351     throttle: 16,
44352     
44353     /**
44354      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44355      */
44356     min_distance: 5,
44357     
44358     /**
44359      * @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.
44360      */
44361     bg_color: 'rgba(0, 0, 0, 0)',
44362     
44363     /**
44364      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44365      */
44366     dot_color: 'black',
44367     
44368     /**
44369      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44370      */ 
44371     velocity_filter_weight: 0.7,
44372     
44373     /**
44374      * @cfg {function} Callback when stroke begin. 
44375      */
44376     onBegin: false,
44377     
44378     /**
44379      * @cfg {function} Callback when stroke end.
44380      */
44381     onEnd: false,
44382     
44383     getAutoCreate : function()
44384     {
44385         var cls = 'roo-signature column';
44386         
44387         if(this.cls){
44388             cls += ' ' + this.cls;
44389         }
44390         
44391         var col_sizes = [
44392             'lg',
44393             'md',
44394             'sm',
44395             'xs'
44396         ];
44397         
44398         for(var i = 0; i < col_sizes.length; i++) {
44399             if(this[col_sizes[i]]) {
44400                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44401             }
44402         }
44403         
44404         var cfg = {
44405             tag: 'div',
44406             cls: cls,
44407             cn: [
44408                 {
44409                     tag: 'div',
44410                     cls: 'roo-signature-body',
44411                     cn: [
44412                         {
44413                             tag: 'canvas',
44414                             cls: 'roo-signature-body-canvas',
44415                             height: this.canvas_height,
44416                             width: this.canvas_width
44417                         }
44418                     ]
44419                 },
44420                 {
44421                     tag: 'input',
44422                     type: 'file',
44423                     style: 'display: none'
44424                 }
44425             ]
44426         };
44427         
44428         return cfg;
44429     },
44430     
44431     initEvents: function() 
44432     {
44433         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44434         
44435         var canvas = this.canvasEl();
44436         
44437         // mouse && touch event swapping...
44438         canvas.dom.style.touchAction = 'none';
44439         canvas.dom.style.msTouchAction = 'none';
44440         
44441         this.mouse_btn_down = false;
44442         canvas.on('mousedown', this._handleMouseDown, this);
44443         canvas.on('mousemove', this._handleMouseMove, this);
44444         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44445         
44446         if (window.PointerEvent) {
44447             canvas.on('pointerdown', this._handleMouseDown, this);
44448             canvas.on('pointermove', this._handleMouseMove, this);
44449             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44450         }
44451         
44452         if ('ontouchstart' in window) {
44453             canvas.on('touchstart', this._handleTouchStart, this);
44454             canvas.on('touchmove', this._handleTouchMove, this);
44455             canvas.on('touchend', this._handleTouchEnd, this);
44456         }
44457         
44458         Roo.EventManager.onWindowResize(this.resize, this, true);
44459         
44460         // file input event
44461         this.fileEl().on('change', this.uploadImage, this);
44462         
44463         this.clear();
44464         
44465         this.resize();
44466     },
44467     
44468     resize: function(){
44469         
44470         var canvas = this.canvasEl().dom;
44471         var ctx = this.canvasElCtx();
44472         var img_data = false;
44473         
44474         if(canvas.width > 0) {
44475             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44476         }
44477         // setting canvas width will clean img data
44478         canvas.width = 0;
44479         
44480         var style = window.getComputedStyle ? 
44481             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44482             
44483         var padding_left = parseInt(style.paddingLeft) || 0;
44484         var padding_right = parseInt(style.paddingRight) || 0;
44485         
44486         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44487         
44488         if(img_data) {
44489             ctx.putImageData(img_data, 0, 0);
44490         }
44491     },
44492     
44493     _handleMouseDown: function(e)
44494     {
44495         if (e.browserEvent.which === 1) {
44496             this.mouse_btn_down = true;
44497             this.strokeBegin(e);
44498         }
44499     },
44500     
44501     _handleMouseMove: function (e)
44502     {
44503         if (this.mouse_btn_down) {
44504             this.strokeMoveUpdate(e);
44505         }
44506     },
44507     
44508     _handleMouseUp: function (e)
44509     {
44510         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44511             this.mouse_btn_down = false;
44512             this.strokeEnd(e);
44513         }
44514     },
44515     
44516     _handleTouchStart: function (e) {
44517         
44518         e.preventDefault();
44519         if (e.browserEvent.targetTouches.length === 1) {
44520             // var touch = e.browserEvent.changedTouches[0];
44521             // this.strokeBegin(touch);
44522             
44523              this.strokeBegin(e); // assume e catching the correct xy...
44524         }
44525     },
44526     
44527     _handleTouchMove: function (e) {
44528         e.preventDefault();
44529         // var touch = event.targetTouches[0];
44530         // _this._strokeMoveUpdate(touch);
44531         this.strokeMoveUpdate(e);
44532     },
44533     
44534     _handleTouchEnd: function (e) {
44535         var wasCanvasTouched = e.target === this.canvasEl().dom;
44536         if (wasCanvasTouched) {
44537             e.preventDefault();
44538             // var touch = event.changedTouches[0];
44539             // _this._strokeEnd(touch);
44540             this.strokeEnd(e);
44541         }
44542     },
44543     
44544     reset: function () {
44545         this._lastPoints = [];
44546         this._lastVelocity = 0;
44547         this._lastWidth = (this.min_width + this.max_width) / 2;
44548         this.canvasElCtx().fillStyle = this.dot_color;
44549     },
44550     
44551     strokeMoveUpdate: function(e)
44552     {
44553         this.strokeUpdate(e);
44554         
44555         if (this.throttle) {
44556             this.throttleStroke(this.strokeUpdate, this.throttle);
44557         }
44558         else {
44559             this.strokeUpdate(e);
44560         }
44561     },
44562     
44563     strokeBegin: function(e)
44564     {
44565         var newPointGroup = {
44566             color: this.dot_color,
44567             points: []
44568         };
44569         
44570         if (typeof this.onBegin === 'function') {
44571             this.onBegin(e);
44572         }
44573         
44574         this.curve_data.push(newPointGroup);
44575         this.reset();
44576         this.strokeUpdate(e);
44577     },
44578     
44579     strokeUpdate: function(e)
44580     {
44581         var rect = this.canvasEl().dom.getBoundingClientRect();
44582         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44583         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44584         var lastPoints = lastPointGroup.points;
44585         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44586         var isLastPointTooClose = lastPoint
44587             ? point.distanceTo(lastPoint) <= this.min_distance
44588             : false;
44589         var color = lastPointGroup.color;
44590         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44591             var curve = this.addPoint(point);
44592             if (!lastPoint) {
44593                 this.drawDot({color: color, point: point});
44594             }
44595             else if (curve) {
44596                 this.drawCurve({color: color, curve: curve});
44597             }
44598             lastPoints.push({
44599                 time: point.time,
44600                 x: point.x,
44601                 y: point.y
44602             });
44603         }
44604     },
44605     
44606     strokeEnd: function(e)
44607     {
44608         this.strokeUpdate(e);
44609         if (typeof this.onEnd === 'function') {
44610             this.onEnd(e);
44611         }
44612     },
44613     
44614     addPoint:  function (point) {
44615         var _lastPoints = this._lastPoints;
44616         _lastPoints.push(point);
44617         if (_lastPoints.length > 2) {
44618             if (_lastPoints.length === 3) {
44619                 _lastPoints.unshift(_lastPoints[0]);
44620             }
44621             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44622             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44623             _lastPoints.shift();
44624             return curve;
44625         }
44626         return null;
44627     },
44628     
44629     calculateCurveWidths: function (startPoint, endPoint) {
44630         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44631             (1 - this.velocity_filter_weight) * this._lastVelocity;
44632
44633         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44634         var widths = {
44635             end: newWidth,
44636             start: this._lastWidth
44637         };
44638         
44639         this._lastVelocity = velocity;
44640         this._lastWidth = newWidth;
44641         return widths;
44642     },
44643     
44644     drawDot: function (_a) {
44645         var color = _a.color, point = _a.point;
44646         var ctx = this.canvasElCtx();
44647         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44648         ctx.beginPath();
44649         this.drawCurveSegment(point.x, point.y, width);
44650         ctx.closePath();
44651         ctx.fillStyle = color;
44652         ctx.fill();
44653     },
44654     
44655     drawCurve: function (_a) {
44656         var color = _a.color, curve = _a.curve;
44657         var ctx = this.canvasElCtx();
44658         var widthDelta = curve.endWidth - curve.startWidth;
44659         var drawSteps = Math.floor(curve.length()) * 2;
44660         ctx.beginPath();
44661         ctx.fillStyle = color;
44662         for (var i = 0; i < drawSteps; i += 1) {
44663         var t = i / drawSteps;
44664         var tt = t * t;
44665         var ttt = tt * t;
44666         var u = 1 - t;
44667         var uu = u * u;
44668         var uuu = uu * u;
44669         var x = uuu * curve.startPoint.x;
44670         x += 3 * uu * t * curve.control1.x;
44671         x += 3 * u * tt * curve.control2.x;
44672         x += ttt * curve.endPoint.x;
44673         var y = uuu * curve.startPoint.y;
44674         y += 3 * uu * t * curve.control1.y;
44675         y += 3 * u * tt * curve.control2.y;
44676         y += ttt * curve.endPoint.y;
44677         var width = curve.startWidth + ttt * widthDelta;
44678         this.drawCurveSegment(x, y, width);
44679         }
44680         ctx.closePath();
44681         ctx.fill();
44682     },
44683     
44684     drawCurveSegment: function (x, y, width) {
44685         var ctx = this.canvasElCtx();
44686         ctx.moveTo(x, y);
44687         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44688         this.is_empty = false;
44689     },
44690     
44691     clear: function()
44692     {
44693         var ctx = this.canvasElCtx();
44694         var canvas = this.canvasEl().dom;
44695         ctx.fillStyle = this.bg_color;
44696         ctx.clearRect(0, 0, canvas.width, canvas.height);
44697         ctx.fillRect(0, 0, canvas.width, canvas.height);
44698         this.curve_data = [];
44699         this.reset();
44700         this.is_empty = true;
44701     },
44702     
44703     fileEl: function()
44704     {
44705         return  this.el.select('input',true).first();
44706     },
44707     
44708     canvasEl: function()
44709     {
44710         return this.el.select('canvas',true).first();
44711     },
44712     
44713     canvasElCtx: function()
44714     {
44715         return this.el.select('canvas',true).first().dom.getContext('2d');
44716     },
44717     
44718     getImage: function(type)
44719     {
44720         if(this.is_empty) {
44721             return false;
44722         }
44723         
44724         // encryption ?
44725         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44726     },
44727     
44728     drawFromImage: function(img_src)
44729     {
44730         var img = new Image();
44731         
44732         img.onload = function(){
44733             this.canvasElCtx().drawImage(img, 0, 0);
44734         }.bind(this);
44735         
44736         img.src = img_src;
44737         
44738         this.is_empty = false;
44739     },
44740     
44741     selectImage: function()
44742     {
44743         this.fileEl().dom.click();
44744     },
44745     
44746     uploadImage: function(e)
44747     {
44748         var reader = new FileReader();
44749         
44750         reader.onload = function(e){
44751             var img = new Image();
44752             img.onload = function(){
44753                 this.reset();
44754                 this.canvasElCtx().drawImage(img, 0, 0);
44755             }.bind(this);
44756             img.src = e.target.result;
44757         }.bind(this);
44758         
44759         reader.readAsDataURL(e.target.files[0]);
44760     },
44761     
44762     // Bezier Point Constructor
44763     Point: (function () {
44764         function Point(x, y, time) {
44765             this.x = x;
44766             this.y = y;
44767             this.time = time || Date.now();
44768         }
44769         Point.prototype.distanceTo = function (start) {
44770             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44771         };
44772         Point.prototype.equals = function (other) {
44773             return this.x === other.x && this.y === other.y && this.time === other.time;
44774         };
44775         Point.prototype.velocityFrom = function (start) {
44776             return this.time !== start.time
44777             ? this.distanceTo(start) / (this.time - start.time)
44778             : 0;
44779         };
44780         return Point;
44781     }()),
44782     
44783     
44784     // Bezier Constructor
44785     Bezier: (function () {
44786         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44787             this.startPoint = startPoint;
44788             this.control2 = control2;
44789             this.control1 = control1;
44790             this.endPoint = endPoint;
44791             this.startWidth = startWidth;
44792             this.endWidth = endWidth;
44793         }
44794         Bezier.fromPoints = function (points, widths, scope) {
44795             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44796             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44797             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44798         };
44799         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44800             var dx1 = s1.x - s2.x;
44801             var dy1 = s1.y - s2.y;
44802             var dx2 = s2.x - s3.x;
44803             var dy2 = s2.y - s3.y;
44804             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44805             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44806             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44807             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44808             var dxm = m1.x - m2.x;
44809             var dym = m1.y - m2.y;
44810             var k = l2 / (l1 + l2);
44811             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44812             var tx = s2.x - cm.x;
44813             var ty = s2.y - cm.y;
44814             return {
44815                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44816                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44817             };
44818         };
44819         Bezier.prototype.length = function () {
44820             var steps = 10;
44821             var length = 0;
44822             var px;
44823             var py;
44824             for (var i = 0; i <= steps; i += 1) {
44825                 var t = i / steps;
44826                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44827                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44828                 if (i > 0) {
44829                     var xdiff = cx - px;
44830                     var ydiff = cy - py;
44831                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44832                 }
44833                 px = cx;
44834                 py = cy;
44835             }
44836             return length;
44837         };
44838         Bezier.prototype.point = function (t, start, c1, c2, end) {
44839             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44840             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44841             + (3.0 * c2 * (1.0 - t) * t * t)
44842             + (end * t * t * t);
44843         };
44844         return Bezier;
44845     }()),
44846     
44847     throttleStroke: function(fn, wait) {
44848       if (wait === void 0) { wait = 250; }
44849       var previous = 0;
44850       var timeout = null;
44851       var result;
44852       var storedContext;
44853       var storedArgs;
44854       var later = function () {
44855           previous = Date.now();
44856           timeout = null;
44857           result = fn.apply(storedContext, storedArgs);
44858           if (!timeout) {
44859               storedContext = null;
44860               storedArgs = [];
44861           }
44862       };
44863       return function wrapper() {
44864           var args = [];
44865           for (var _i = 0; _i < arguments.length; _i++) {
44866               args[_i] = arguments[_i];
44867           }
44868           var now = Date.now();
44869           var remaining = wait - (now - previous);
44870           storedContext = this;
44871           storedArgs = args;
44872           if (remaining <= 0 || remaining > wait) {
44873               if (timeout) {
44874                   clearTimeout(timeout);
44875                   timeout = null;
44876               }
44877               previous = now;
44878               result = fn.apply(storedContext, storedArgs);
44879               if (!timeout) {
44880                   storedContext = null;
44881                   storedArgs = [];
44882               }
44883           }
44884           else if (!timeout) {
44885               timeout = window.setTimeout(later, remaining);
44886           }
44887           return result;
44888       };
44889   }
44890   
44891 });
44892
44893  
44894
44895