Changed docs/json/roodata.jsondocs/src/Roo_bootstrap_panel_Content.js.htmldocs/symbol...
[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          * @event load
3065          * The when any image loads
3066          * @param {Roo.EventObject} e
3067          */
3068         "load" : true
3069     });
3070 };
3071
3072 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3073     
3074     imgResponsive: true,
3075     border: '',
3076     src: 'about:blank',
3077     href: false,
3078     target: false,
3079     xsUrl: '',
3080     smUrl: '',
3081     mdUrl: '',
3082     lgUrl: '',
3083
3084     getAutoCreate : function()
3085     {   
3086         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3087             return this.createSingleImg();
3088         }
3089         
3090         var cfg = {
3091             tag: 'div',
3092             cls: 'roo-image-responsive-group',
3093             cn: []
3094         };
3095         var _this = this;
3096         
3097         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3098             
3099             if(!_this[size + 'Url']){
3100                 return;
3101             }
3102             
3103             var img = {
3104                 tag: 'img',
3105                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3106                 html: _this.html || cfg.html,
3107                 src: _this[size + 'Url']
3108             };
3109             
3110             img.cls += ' roo-image-responsive-' + size;
3111             
3112             var s = ['xs', 'sm', 'md', 'lg'];
3113             
3114             s.splice(s.indexOf(size), 1);
3115             
3116             Roo.each(s, function(ss){
3117                 img.cls += ' hidden-' + ss;
3118             });
3119             
3120             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3121                 cfg.cls += ' img-' + _this.border;
3122             }
3123             
3124             if(_this.alt){
3125                 cfg.alt = _this.alt;
3126             }
3127             
3128             if(_this.href){
3129                 var a = {
3130                     tag: 'a',
3131                     href: _this.href,
3132                     cn: [
3133                         img
3134                     ]
3135                 };
3136
3137                 if(this.target){
3138                     a.target = _this.target;
3139                 }
3140             }
3141             
3142             cfg.cn.push((_this.href) ? a : img);
3143             
3144         });
3145         
3146         return cfg;
3147     },
3148     
3149     createSingleImg : function()
3150     {
3151         var cfg = {
3152             tag: 'img',
3153             cls: (this.imgResponsive) ? 'img-responsive' : '',
3154             html : null,
3155             src : 'about:blank'  // just incase src get's set to undefined?!?
3156         };
3157         
3158         cfg.html = this.html || cfg.html;
3159         
3160         cfg.src = this.src || cfg.src;
3161         
3162         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3163             cfg.cls += ' img-' + this.border;
3164         }
3165         
3166         if(this.alt){
3167             cfg.alt = this.alt;
3168         }
3169         
3170         if(this.href){
3171             var a = {
3172                 tag: 'a',
3173                 href: this.href,
3174                 cn: [
3175                     cfg
3176                 ]
3177             };
3178             
3179             if(this.target){
3180                 a.target = this.target;
3181             }
3182             
3183         }
3184         
3185         return (this.href) ? a : cfg;
3186     },
3187     
3188     initEvents: function() 
3189     {
3190         if(!this.href){
3191             this.el.on('click', this.onClick, this);
3192         }
3193         this.el.select('img', true).on('load', this.onImageLoad, this);
3194     },
3195     
3196     onClick : function(e)
3197     {
3198         Roo.log('img onclick');
3199         this.fireEvent('click', this, e);
3200     },
3201     onImageLoad: function(e)
3202     {
3203         Roo.log('img load');
3204         this.fireEvent('load', this, e);
3205     },
3206     
3207     /**
3208      * Sets the url of the image - used to update it
3209      * @param {String} url the url of the image
3210      */
3211     
3212     setSrc : function(url)
3213     {
3214         this.src =  url;
3215         
3216         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3217             this.el.dom.src =  url;
3218             return;
3219         }
3220         
3221         this.el.select('img', true).first().dom.src =  url;
3222     }
3223     
3224     
3225    
3226 });
3227
3228  /*
3229  * - LGPL
3230  *
3231  * image
3232  * 
3233  */
3234
3235
3236 /**
3237  * @class Roo.bootstrap.Link
3238  * @extends Roo.bootstrap.Component
3239  * Bootstrap Link Class
3240  * @cfg {String} alt image alternative text
3241  * @cfg {String} href a tag href
3242  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3243  * @cfg {String} html the content of the link.
3244  * @cfg {String} anchor name for the anchor link
3245  * @cfg {String} fa - favicon
3246
3247  * @cfg {Boolean} preventDefault (true | false) default false
3248
3249  * 
3250  * @constructor
3251  * Create a new Input
3252  * @param {Object} config The config object
3253  */
3254
3255 Roo.bootstrap.Link = function(config){
3256     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3257     
3258     this.addEvents({
3259         // img events
3260         /**
3261          * @event click
3262          * The img click event for the img.
3263          * @param {Roo.EventObject} e
3264          */
3265         "click" : true
3266     });
3267 };
3268
3269 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3270     
3271     href: false,
3272     target: false,
3273     preventDefault: false,
3274     anchor : false,
3275     alt : false,
3276     fa: false,
3277
3278
3279     getAutoCreate : function()
3280     {
3281         var html = this.html || '';
3282         
3283         if (this.fa !== false) {
3284             html = '<i class="fa fa-' + this.fa + '"></i>';
3285         }
3286         var cfg = {
3287             tag: 'a'
3288         };
3289         // anchor's do not require html/href...
3290         if (this.anchor === false) {
3291             cfg.html = html;
3292             cfg.href = this.href || '#';
3293         } else {
3294             cfg.name = this.anchor;
3295             if (this.html !== false || this.fa !== false) {
3296                 cfg.html = html;
3297             }
3298             if (this.href !== false) {
3299                 cfg.href = this.href;
3300             }
3301         }
3302         
3303         if(this.alt !== false){
3304             cfg.alt = this.alt;
3305         }
3306         
3307         
3308         if(this.target !== false) {
3309             cfg.target = this.target;
3310         }
3311         
3312         return cfg;
3313     },
3314     
3315     initEvents: function() {
3316         
3317         if(!this.href || this.preventDefault){
3318             this.el.on('click', this.onClick, this);
3319         }
3320     },
3321     
3322     onClick : function(e)
3323     {
3324         if(this.preventDefault){
3325             e.preventDefault();
3326         }
3327         //Roo.log('img onclick');
3328         this.fireEvent('click', this, e);
3329     }
3330    
3331 });
3332
3333  /*
3334  * - LGPL
3335  *
3336  * header
3337  * 
3338  */
3339
3340 /**
3341  * @class Roo.bootstrap.Header
3342  * @extends Roo.bootstrap.Component
3343  * Bootstrap Header class
3344  * @cfg {String} html content of header
3345  * @cfg {Number} level (1|2|3|4|5|6) default 1
3346  * 
3347  * @constructor
3348  * Create a new Header
3349  * @param {Object} config The config object
3350  */
3351
3352
3353 Roo.bootstrap.Header  = function(config){
3354     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3355 };
3356
3357 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3358     
3359     //href : false,
3360     html : false,
3361     level : 1,
3362     
3363     
3364     
3365     getAutoCreate : function(){
3366         
3367         
3368         
3369         var cfg = {
3370             tag: 'h' + (1 *this.level),
3371             html: this.html || ''
3372         } ;
3373         
3374         return cfg;
3375     }
3376    
3377 });
3378
3379  
3380
3381  /*
3382  * Based on:
3383  * Ext JS Library 1.1.1
3384  * Copyright(c) 2006-2007, Ext JS, LLC.
3385  *
3386  * Originally Released Under LGPL - original licence link has changed is not relivant.
3387  *
3388  * Fork - LGPL
3389  * <script type="text/javascript">
3390  */
3391  
3392 /**
3393  * @class Roo.bootstrap.MenuMgr
3394  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3395  * @singleton
3396  */
3397 Roo.bootstrap.MenuMgr = function(){
3398    var menus, active, groups = {}, attached = false, lastShow = new Date();
3399
3400    // private - called when first menu is created
3401    function init(){
3402        menus = {};
3403        active = new Roo.util.MixedCollection();
3404        Roo.get(document).addKeyListener(27, function(){
3405            if(active.length > 0){
3406                hideAll();
3407            }
3408        });
3409    }
3410
3411    // private
3412    function hideAll(){
3413        if(active && active.length > 0){
3414            var c = active.clone();
3415            c.each(function(m){
3416                m.hide();
3417            });
3418        }
3419    }
3420
3421    // private
3422    function onHide(m){
3423        active.remove(m);
3424        if(active.length < 1){
3425            Roo.get(document).un("mouseup", onMouseDown);
3426             
3427            attached = false;
3428        }
3429    }
3430
3431    // private
3432    function onShow(m){
3433        var last = active.last();
3434        lastShow = new Date();
3435        active.add(m);
3436        if(!attached){
3437           Roo.get(document).on("mouseup", onMouseDown);
3438            
3439            attached = true;
3440        }
3441        if(m.parentMenu){
3442           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3443           m.parentMenu.activeChild = m;
3444        }else if(last && last.isVisible()){
3445           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3446        }
3447    }
3448
3449    // private
3450    function onBeforeHide(m){
3451        if(m.activeChild){
3452            m.activeChild.hide();
3453        }
3454        if(m.autoHideTimer){
3455            clearTimeout(m.autoHideTimer);
3456            delete m.autoHideTimer;
3457        }
3458    }
3459
3460    // private
3461    function onBeforeShow(m){
3462        var pm = m.parentMenu;
3463        if(!pm && !m.allowOtherMenus){
3464            hideAll();
3465        }else if(pm && pm.activeChild && active != m){
3466            pm.activeChild.hide();
3467        }
3468    }
3469
3470    // private this should really trigger on mouseup..
3471    function onMouseDown(e){
3472         Roo.log("on Mouse Up");
3473         
3474         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3475             Roo.log("MenuManager hideAll");
3476             hideAll();
3477             e.stopEvent();
3478         }
3479         
3480         
3481    }
3482
3483    // private
3484    function onBeforeCheck(mi, state){
3485        if(state){
3486            var g = groups[mi.group];
3487            for(var i = 0, l = g.length; i < l; i++){
3488                if(g[i] != mi){
3489                    g[i].setChecked(false);
3490                }
3491            }
3492        }
3493    }
3494
3495    return {
3496
3497        /**
3498         * Hides all menus that are currently visible
3499         */
3500        hideAll : function(){
3501             hideAll();  
3502        },
3503
3504        // private
3505        register : function(menu){
3506            if(!menus){
3507                init();
3508            }
3509            menus[menu.id] = menu;
3510            menu.on("beforehide", onBeforeHide);
3511            menu.on("hide", onHide);
3512            menu.on("beforeshow", onBeforeShow);
3513            menu.on("show", onShow);
3514            var g = menu.group;
3515            if(g && menu.events["checkchange"]){
3516                if(!groups[g]){
3517                    groups[g] = [];
3518                }
3519                groups[g].push(menu);
3520                menu.on("checkchange", onCheck);
3521            }
3522        },
3523
3524         /**
3525          * Returns a {@link Roo.menu.Menu} object
3526          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3527          * be used to generate and return a new Menu instance.
3528          */
3529        get : function(menu){
3530            if(typeof menu == "string"){ // menu id
3531                return menus[menu];
3532            }else if(menu.events){  // menu instance
3533                return menu;
3534            }
3535            /*else if(typeof menu.length == 'number'){ // array of menu items?
3536                return new Roo.bootstrap.Menu({items:menu});
3537            }else{ // otherwise, must be a config
3538                return new Roo.bootstrap.Menu(menu);
3539            }
3540            */
3541            return false;
3542        },
3543
3544        // private
3545        unregister : function(menu){
3546            delete menus[menu.id];
3547            menu.un("beforehide", onBeforeHide);
3548            menu.un("hide", onHide);
3549            menu.un("beforeshow", onBeforeShow);
3550            menu.un("show", onShow);
3551            var g = menu.group;
3552            if(g && menu.events["checkchange"]){
3553                groups[g].remove(menu);
3554                menu.un("checkchange", onCheck);
3555            }
3556        },
3557
3558        // private
3559        registerCheckable : function(menuItem){
3560            var g = menuItem.group;
3561            if(g){
3562                if(!groups[g]){
3563                    groups[g] = [];
3564                }
3565                groups[g].push(menuItem);
3566                menuItem.on("beforecheckchange", onBeforeCheck);
3567            }
3568        },
3569
3570        // private
3571        unregisterCheckable : function(menuItem){
3572            var g = menuItem.group;
3573            if(g){
3574                groups[g].remove(menuItem);
3575                menuItem.un("beforecheckchange", onBeforeCheck);
3576            }
3577        }
3578    };
3579 }();/*
3580  * - LGPL
3581  *
3582  * menu
3583  * 
3584  */
3585
3586 /**
3587  * @class Roo.bootstrap.Menu
3588  * @extends Roo.bootstrap.Component
3589  * Bootstrap Menu class - container for MenuItems
3590  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3591  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3592  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3593  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3594   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3595   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3596  
3597  * @constructor
3598  * Create a new Menu
3599  * @param {Object} config The config object
3600  */
3601
3602
3603 Roo.bootstrap.Menu = function(config){
3604     
3605     if (config.type == 'treeview') {
3606         // normally menu's are drawn attached to the document to handle layering etc..
3607         // however treeview (used by the docs menu is drawn into the parent element)
3608         this.container_method = 'getChildContainer'; 
3609     }
3610     
3611     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3612     if (this.registerMenu && this.type != 'treeview')  {
3613         Roo.bootstrap.MenuMgr.register(this);
3614     }
3615     
3616     
3617     this.addEvents({
3618         /**
3619          * @event beforeshow
3620          * Fires before this menu is displayed (return false to block)
3621          * @param {Roo.menu.Menu} this
3622          */
3623         beforeshow : true,
3624         /**
3625          * @event beforehide
3626          * Fires before this menu is hidden (return false to block)
3627          * @param {Roo.menu.Menu} this
3628          */
3629         beforehide : true,
3630         /**
3631          * @event show
3632          * Fires after this menu is displayed
3633          * @param {Roo.menu.Menu} this
3634          */
3635         show : true,
3636         /**
3637          * @event hide
3638          * Fires after this menu is hidden
3639          * @param {Roo.menu.Menu} this
3640          */
3641         hide : true,
3642         /**
3643          * @event click
3644          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3645          * @param {Roo.menu.Menu} this
3646          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3647          * @param {Roo.EventObject} e
3648          */
3649         click : true,
3650         /**
3651          * @event mouseover
3652          * Fires when the mouse is hovering over this menu
3653          * @param {Roo.menu.Menu} this
3654          * @param {Roo.EventObject} e
3655          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3656          */
3657         mouseover : true,
3658         /**
3659          * @event mouseout
3660          * Fires when the mouse exits this menu
3661          * @param {Roo.menu.Menu} this
3662          * @param {Roo.EventObject} e
3663          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3664          */
3665         mouseout : true,
3666         /**
3667          * @event itemclick
3668          * Fires when a menu item contained in this menu is clicked
3669          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3670          * @param {Roo.EventObject} e
3671          */
3672         itemclick: true
3673     });
3674     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3675 };
3676
3677 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3678     
3679    /// html : false,
3680    
3681     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3682     type: false,
3683     /**
3684      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3685      */
3686     registerMenu : true,
3687     
3688     menuItems :false, // stores the menu items..
3689     
3690     hidden:true,
3691         
3692     parentMenu : false,
3693     
3694     stopEvent : true,
3695     
3696     isLink : false,
3697     
3698     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3699     
3700     hideTrigger : false,
3701     
3702     align : 'tl-bl?',
3703     
3704     
3705     getChildContainer : function() {
3706         return this.el;  
3707     },
3708     
3709     getAutoCreate : function(){
3710          
3711         //if (['right'].indexOf(this.align)!==-1) {
3712         //    cfg.cn[1].cls += ' pull-right'
3713         //}
3714          
3715         var cfg = {
3716             tag : 'ul',
3717             cls : 'dropdown-menu shadow' ,
3718             style : 'z-index:1000'
3719             
3720         };
3721         
3722         if (this.type === 'submenu') {
3723             cfg.cls = 'submenu active';
3724         }
3725         if (this.type === 'treeview') {
3726             cfg.cls = 'treeview-menu';
3727         }
3728         
3729         return cfg;
3730     },
3731     initEvents : function() {
3732         
3733        // Roo.log("ADD event");
3734        // Roo.log(this.triggerEl.dom);
3735         if (this.triggerEl) {
3736             
3737             this.triggerEl.on('click', this.onTriggerClick, this);
3738             
3739             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3740             
3741             if (!this.hideTrigger) {
3742                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3743                     // dropdown toggle on the 'a' in BS4?
3744                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3745                 } else {
3746                     this.triggerEl.addClass('dropdown-toggle');
3747                 }
3748             }
3749         }
3750         
3751         if (Roo.isTouch) {
3752             this.el.on('touchstart'  , this.onTouch, this);
3753         }
3754         this.el.on('click' , this.onClick, this);
3755
3756         this.el.on("mouseover", this.onMouseOver, this);
3757         this.el.on("mouseout", this.onMouseOut, this);
3758         
3759     },
3760     
3761     findTargetItem : function(e)
3762     {
3763         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3764         if(!t){
3765             return false;
3766         }
3767         //Roo.log(t);         Roo.log(t.id);
3768         if(t && t.id){
3769             //Roo.log(this.menuitems);
3770             return this.menuitems.get(t.id);
3771             
3772             //return this.items.get(t.menuItemId);
3773         }
3774         
3775         return false;
3776     },
3777     
3778     onTouch : function(e) 
3779     {
3780         Roo.log("menu.onTouch");
3781         //e.stopEvent(); this make the user popdown broken
3782         this.onClick(e);
3783     },
3784     
3785     onClick : function(e)
3786     {
3787         Roo.log("menu.onClick");
3788         
3789         var t = this.findTargetItem(e);
3790         if(!t || t.isContainer){
3791             return;
3792         }
3793         Roo.log(e);
3794         /*
3795         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3796             if(t == this.activeItem && t.shouldDeactivate(e)){
3797                 this.activeItem.deactivate();
3798                 delete this.activeItem;
3799                 return;
3800             }
3801             if(t.canActivate){
3802                 this.setActiveItem(t, true);
3803             }
3804             return;
3805             
3806             
3807         }
3808         */
3809        
3810         Roo.log('pass click event');
3811         
3812         t.onClick(e);
3813         
3814         this.fireEvent("click", this, t, e);
3815         
3816         var _this = this;
3817         
3818         if(!t.href.length || t.href == '#'){
3819             (function() { _this.hide(); }).defer(100);
3820         }
3821         
3822     },
3823     
3824     onMouseOver : function(e){
3825         var t  = this.findTargetItem(e);
3826         //Roo.log(t);
3827         //if(t){
3828         //    if(t.canActivate && !t.disabled){
3829         //        this.setActiveItem(t, true);
3830         //    }
3831         //}
3832         
3833         this.fireEvent("mouseover", this, e, t);
3834     },
3835     isVisible : function(){
3836         return !this.hidden;
3837     },
3838     onMouseOut : function(e){
3839         var t  = this.findTargetItem(e);
3840         
3841         //if(t ){
3842         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3843         //        this.activeItem.deactivate();
3844         //        delete this.activeItem;
3845         //    }
3846         //}
3847         this.fireEvent("mouseout", this, e, t);
3848     },
3849     
3850     
3851     /**
3852      * Displays this menu relative to another element
3853      * @param {String/HTMLElement/Roo.Element} element The element to align to
3854      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3855      * the element (defaults to this.defaultAlign)
3856      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3857      */
3858     show : function(el, pos, parentMenu)
3859     {
3860         if (false === this.fireEvent("beforeshow", this)) {
3861             Roo.log("show canceled");
3862             return;
3863         }
3864         this.parentMenu = parentMenu;
3865         if(!this.el){
3866             this.render();
3867         }
3868         this.el.addClass('show'); // show otherwise we do not know how big we are..
3869          
3870         var xy = this.el.getAlignToXY(el, pos);
3871         
3872         // bl-tl << left align  below
3873         // tl-bl << left align 
3874         
3875         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3876             // if it goes to far to the right.. -> align left.
3877             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3878         }
3879         if(xy[0] < 0){
3880             // was left align - go right?
3881             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3882         }
3883         
3884         // goes down the bottom
3885         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3886            xy[1]  < 0 ){
3887             var a = this.align.replace('?', '').split('-');
3888             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3889             
3890         }
3891         
3892         this.showAt(  xy , parentMenu, false);
3893     },
3894      /**
3895      * Displays this menu at a specific xy position
3896      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3897      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3898      */
3899     showAt : function(xy, parentMenu, /* private: */_e){
3900         this.parentMenu = parentMenu;
3901         if(!this.el){
3902             this.render();
3903         }
3904         if(_e !== false){
3905             this.fireEvent("beforeshow", this);
3906             //xy = this.el.adjustForConstraints(xy);
3907         }
3908         
3909         //this.el.show();
3910         this.hideMenuItems();
3911         this.hidden = false;
3912         if (this.triggerEl) {
3913             this.triggerEl.addClass('open');
3914         }
3915         
3916         this.el.addClass('show');
3917         
3918         
3919         
3920         // reassign x when hitting right
3921         
3922         // reassign y when hitting bottom
3923         
3924         // but the list may align on trigger left or trigger top... should it be a properity?
3925         
3926         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3927             this.el.setXY(xy);
3928         }
3929         
3930         this.focus();
3931         this.fireEvent("show", this);
3932     },
3933     
3934     focus : function(){
3935         return;
3936         if(!this.hidden){
3937             this.doFocus.defer(50, this);
3938         }
3939     },
3940
3941     doFocus : function(){
3942         if(!this.hidden){
3943             this.focusEl.focus();
3944         }
3945     },
3946
3947     /**
3948      * Hides this menu and optionally all parent menus
3949      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3950      */
3951     hide : function(deep)
3952     {
3953         if (false === this.fireEvent("beforehide", this)) {
3954             Roo.log("hide canceled");
3955             return;
3956         }
3957         this.hideMenuItems();
3958         if(this.el && this.isVisible()){
3959            
3960             if(this.activeItem){
3961                 this.activeItem.deactivate();
3962                 this.activeItem = null;
3963             }
3964             if (this.triggerEl) {
3965                 this.triggerEl.removeClass('open');
3966             }
3967             
3968             this.el.removeClass('show');
3969             this.hidden = true;
3970             this.fireEvent("hide", this);
3971         }
3972         if(deep === true && this.parentMenu){
3973             this.parentMenu.hide(true);
3974         }
3975     },
3976     
3977     onTriggerClick : function(e)
3978     {
3979         Roo.log('trigger click');
3980         
3981         var target = e.getTarget();
3982         
3983         Roo.log(target.nodeName.toLowerCase());
3984         
3985         if(target.nodeName.toLowerCase() === 'i'){
3986             e.preventDefault();
3987         }
3988         
3989     },
3990     
3991     onTriggerPress  : function(e)
3992     {
3993         Roo.log('trigger press');
3994         //Roo.log(e.getTarget());
3995        // Roo.log(this.triggerEl.dom);
3996        
3997         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3998         var pel = Roo.get(e.getTarget());
3999         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4000             Roo.log('is treeview or dropdown?');
4001             return;
4002         }
4003         
4004         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4005             return;
4006         }
4007         
4008         if (this.isVisible()) {
4009             Roo.log('hide');
4010             this.hide();
4011         } else {
4012             Roo.log('show');
4013             
4014             this.show(this.triggerEl, this.align, false);
4015         }
4016         
4017         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4018             e.stopEvent();
4019         }
4020         
4021     },
4022        
4023     
4024     hideMenuItems : function()
4025     {
4026         Roo.log("hide Menu Items");
4027         if (!this.el) { 
4028             return;
4029         }
4030         
4031         this.el.select('.open',true).each(function(aa) {
4032             
4033             aa.removeClass('open');
4034          
4035         });
4036     },
4037     addxtypeChild : function (tree, cntr) {
4038         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4039           
4040         this.menuitems.add(comp);
4041         return comp;
4042
4043     },
4044     getEl : function()
4045     {
4046         Roo.log(this.el);
4047         return this.el;
4048     },
4049     
4050     clear : function()
4051     {
4052         this.getEl().dom.innerHTML = '';
4053         this.menuitems.clear();
4054     }
4055 });
4056
4057  
4058  /*
4059  * - LGPL
4060  *
4061  * menu item
4062  * 
4063  */
4064
4065
4066 /**
4067  * @class Roo.bootstrap.MenuItem
4068  * @extends Roo.bootstrap.Component
4069  * Bootstrap MenuItem class
4070  * @cfg {String} html the menu label
4071  * @cfg {String} href the link
4072  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4073  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4074  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4075  * @cfg {String} fa favicon to show on left of menu item.
4076  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4077  * 
4078  * 
4079  * @constructor
4080  * Create a new MenuItem
4081  * @param {Object} config The config object
4082  */
4083
4084
4085 Roo.bootstrap.MenuItem = function(config){
4086     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4087     this.addEvents({
4088         // raw events
4089         /**
4090          * @event click
4091          * The raw click event for the entire grid.
4092          * @param {Roo.bootstrap.MenuItem} this
4093          * @param {Roo.EventObject} e
4094          */
4095         "click" : true
4096     });
4097 };
4098
4099 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4100     
4101     href : false,
4102     html : false,
4103     preventDefault: false,
4104     isContainer : false,
4105     active : false,
4106     fa: false,
4107     
4108     getAutoCreate : function(){
4109         
4110         if(this.isContainer){
4111             return {
4112                 tag: 'li',
4113                 cls: 'dropdown-menu-item '
4114             };
4115         }
4116         var ctag = {
4117             tag: 'span',
4118             html: 'Link'
4119         };
4120         
4121         var anc = {
4122             tag : 'a',
4123             cls : 'dropdown-item',
4124             href : '#',
4125             cn : [  ]
4126         };
4127         
4128         if (this.fa !== false) {
4129             anc.cn.push({
4130                 tag : 'i',
4131                 cls : 'fa fa-' + this.fa
4132             });
4133         }
4134         
4135         anc.cn.push(ctag);
4136         
4137         
4138         var cfg= {
4139             tag: 'li',
4140             cls: 'dropdown-menu-item',
4141             cn: [ anc ]
4142         };
4143         if (this.parent().type == 'treeview') {
4144             cfg.cls = 'treeview-menu';
4145         }
4146         if (this.active) {
4147             cfg.cls += ' active';
4148         }
4149         
4150         
4151         
4152         anc.href = this.href || cfg.cn[0].href ;
4153         ctag.html = this.html || cfg.cn[0].html ;
4154         return cfg;
4155     },
4156     
4157     initEvents: function()
4158     {
4159         if (this.parent().type == 'treeview') {
4160             this.el.select('a').on('click', this.onClick, this);
4161         }
4162         
4163         if (this.menu) {
4164             this.menu.parentType = this.xtype;
4165             this.menu.triggerEl = this.el;
4166             this.menu = this.addxtype(Roo.apply({}, this.menu));
4167         }
4168         
4169     },
4170     onClick : function(e)
4171     {
4172         Roo.log('item on click ');
4173         
4174         if(this.preventDefault){
4175             e.preventDefault();
4176         }
4177         //this.parent().hideMenuItems();
4178         
4179         this.fireEvent('click', this, e);
4180     },
4181     getEl : function()
4182     {
4183         return this.el;
4184     } 
4185 });
4186
4187  
4188
4189  /*
4190  * - LGPL
4191  *
4192  * menu separator
4193  * 
4194  */
4195
4196
4197 /**
4198  * @class Roo.bootstrap.MenuSeparator
4199  * @extends Roo.bootstrap.Component
4200  * Bootstrap MenuSeparator class
4201  * 
4202  * @constructor
4203  * Create a new MenuItem
4204  * @param {Object} config The config object
4205  */
4206
4207
4208 Roo.bootstrap.MenuSeparator = function(config){
4209     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4210 };
4211
4212 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4213     
4214     getAutoCreate : function(){
4215         var cfg = {
4216             cls: 'divider',
4217             tag : 'li'
4218         };
4219         
4220         return cfg;
4221     }
4222    
4223 });
4224
4225  
4226
4227  
4228 /*
4229 * Licence: LGPL
4230 */
4231
4232 /**
4233  * @class Roo.bootstrap.Modal
4234  * @extends Roo.bootstrap.Component
4235  * Bootstrap Modal class
4236  * @cfg {String} title Title of dialog
4237  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4238  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4239  * @cfg {Boolean} specificTitle default false
4240  * @cfg {Array} buttons Array of buttons or standard button set..
4241  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4242  * @cfg {Boolean} animate default true
4243  * @cfg {Boolean} allow_close default true
4244  * @cfg {Boolean} fitwindow default false
4245  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4246  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4247  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4248  * @cfg {String} size (sm|lg|xl) default empty
4249  * @cfg {Number} max_width set the max width of modal
4250  * @cfg {Boolean} editableTitle can the title be edited
4251
4252  *
4253  *
4254  * @constructor
4255  * Create a new Modal Dialog
4256  * @param {Object} config The config object
4257  */
4258
4259 Roo.bootstrap.Modal = function(config){
4260     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4261     this.addEvents({
4262         // raw events
4263         /**
4264          * @event btnclick
4265          * The raw btnclick event for the button
4266          * @param {Roo.EventObject} e
4267          */
4268         "btnclick" : true,
4269         /**
4270          * @event resize
4271          * Fire when dialog resize
4272          * @param {Roo.bootstrap.Modal} this
4273          * @param {Roo.EventObject} e
4274          */
4275         "resize" : true,
4276         /**
4277          * @event titlechanged
4278          * Fire when the editable title has been changed
4279          * @param {Roo.bootstrap.Modal} this
4280          * @param {Roo.EventObject} value
4281          */
4282         "titlechanged" : true 
4283         
4284     });
4285     this.buttons = this.buttons || [];
4286
4287     if (this.tmpl) {
4288         this.tmpl = Roo.factory(this.tmpl);
4289     }
4290
4291 };
4292
4293 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4294
4295     title : 'test dialog',
4296
4297     buttons : false,
4298
4299     // set on load...
4300
4301     html: false,
4302
4303     tmp: false,
4304
4305     specificTitle: false,
4306
4307     buttonPosition: 'right',
4308
4309     allow_close : true,
4310
4311     animate : true,
4312
4313     fitwindow: false,
4314     
4315      // private
4316     dialogEl: false,
4317     bodyEl:  false,
4318     footerEl:  false,
4319     titleEl:  false,
4320     closeEl:  false,
4321
4322     size: '',
4323     
4324     max_width: 0,
4325     
4326     max_height: 0,
4327     
4328     fit_content: false,
4329     editableTitle  : false,
4330
4331     onRender : function(ct, position)
4332     {
4333         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4334
4335         if(!this.el){
4336             var cfg = Roo.apply({},  this.getAutoCreate());
4337             cfg.id = Roo.id();
4338             //if(!cfg.name){
4339             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4340             //}
4341             //if (!cfg.name.length) {
4342             //    delete cfg.name;
4343            // }
4344             if (this.cls) {
4345                 cfg.cls += ' ' + this.cls;
4346             }
4347             if (this.style) {
4348                 cfg.style = this.style;
4349             }
4350             this.el = Roo.get(document.body).createChild(cfg, position);
4351         }
4352         //var type = this.el.dom.type;
4353
4354
4355         if(this.tabIndex !== undefined){
4356             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4357         }
4358
4359         this.dialogEl = this.el.select('.modal-dialog',true).first();
4360         this.bodyEl = this.el.select('.modal-body',true).first();
4361         this.closeEl = this.el.select('.modal-header .close', true).first();
4362         this.headerEl = this.el.select('.modal-header',true).first();
4363         this.titleEl = this.el.select('.modal-title',true).first();
4364         this.footerEl = this.el.select('.modal-footer',true).first();
4365
4366         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4367         
4368         //this.el.addClass("x-dlg-modal");
4369
4370         if (this.buttons.length) {
4371             Roo.each(this.buttons, function(bb) {
4372                 var b = Roo.apply({}, bb);
4373                 b.xns = b.xns || Roo.bootstrap;
4374                 b.xtype = b.xtype || 'Button';
4375                 if (typeof(b.listeners) == 'undefined') {
4376                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4377                 }
4378
4379                 var btn = Roo.factory(b);
4380
4381                 btn.render(this.getButtonContainer());
4382
4383             },this);
4384         }
4385         // render the children.
4386         var nitems = [];
4387
4388         if(typeof(this.items) != 'undefined'){
4389             var items = this.items;
4390             delete this.items;
4391
4392             for(var i =0;i < items.length;i++) {
4393                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4394             }
4395         }
4396
4397         this.items = nitems;
4398
4399         // where are these used - they used to be body/close/footer
4400
4401
4402         this.initEvents();
4403         //this.el.addClass([this.fieldClass, this.cls]);
4404
4405     },
4406
4407     getAutoCreate : function()
4408     {
4409         // we will default to modal-body-overflow - might need to remove or make optional later.
4410         var bdy = {
4411                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4412                 html : this.html || ''
4413         };
4414
4415         var title = {
4416             tag: 'h5',
4417             cls : 'modal-title',
4418             html : this.title
4419         };
4420
4421         if(this.specificTitle){ // WTF is this?
4422             title = this.title;
4423         }
4424
4425         var header = [];
4426         if (this.allow_close && Roo.bootstrap.version == 3) {
4427             header.push({
4428                 tag: 'button',
4429                 cls : 'close',
4430                 html : '&times'
4431             });
4432         }
4433
4434         header.push(title);
4435
4436         if (this.editableTitle) {
4437             header.push({
4438                 cls: 'form-control roo-editable-title d-none',
4439                 tag: 'input',
4440                 type: 'text'
4441             });
4442         }
4443         
4444         if (this.allow_close && Roo.bootstrap.version == 4) {
4445             header.push({
4446                 tag: 'button',
4447                 cls : 'close',
4448                 html : '&times'
4449             });
4450         }
4451         
4452         var size = '';
4453
4454         if(this.size.length){
4455             size = 'modal-' + this.size;
4456         }
4457         
4458         var footer = Roo.bootstrap.version == 3 ?
4459             {
4460                 cls : 'modal-footer',
4461                 cn : [
4462                     {
4463                         tag: 'div',
4464                         cls: 'btn-' + this.buttonPosition
4465                     }
4466                 ]
4467
4468             } :
4469             {  // BS4 uses mr-auto on left buttons....
4470                 cls : 'modal-footer'
4471             };
4472
4473             
4474
4475         
4476         
4477         var modal = {
4478             cls: "modal",
4479              cn : [
4480                 {
4481                     cls: "modal-dialog " + size,
4482                     cn : [
4483                         {
4484                             cls : "modal-content",
4485                             cn : [
4486                                 {
4487                                     cls : 'modal-header',
4488                                     cn : header
4489                                 },
4490                                 bdy,
4491                                 footer
4492                             ]
4493
4494                         }
4495                     ]
4496
4497                 }
4498             ]
4499         };
4500
4501         if(this.animate){
4502             modal.cls += ' fade';
4503         }
4504
4505         return modal;
4506
4507     },
4508     getChildContainer : function() {
4509
4510          return this.bodyEl;
4511
4512     },
4513     getButtonContainer : function() {
4514         
4515          return Roo.bootstrap.version == 4 ?
4516             this.el.select('.modal-footer',true).first()
4517             : this.el.select('.modal-footer div',true).first();
4518
4519     },
4520     initEvents : function()
4521     {
4522         if (this.allow_close) {
4523             this.closeEl.on('click', this.hide, this);
4524         }
4525         Roo.EventManager.onWindowResize(this.resize, this, true);
4526         if (this.editableTitle) {
4527             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4528             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4529             this.headerEditEl.on('keyup', function(e) {
4530                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4531                         this.toggleHeaderInput(false)
4532                     }
4533                 }, this);
4534             this.headerEditEl.on('blur', function(e) {
4535                 this.toggleHeaderInput(false)
4536             },this);
4537         }
4538
4539     },
4540   
4541
4542     resize : function()
4543     {
4544         this.maskEl.setSize(
4545             Roo.lib.Dom.getViewWidth(true),
4546             Roo.lib.Dom.getViewHeight(true)
4547         );
4548         
4549         if (this.fitwindow) {
4550             
4551            this.dialogEl.setStyle( { 'max-width' : '100%' });
4552             this.setSize(
4553                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4554                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4555             );
4556             return;
4557         }
4558         
4559         if(this.max_width !== 0) {
4560             
4561             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4562             
4563             if(this.height) {
4564                 this.setSize(w, this.height);
4565                 return;
4566             }
4567             
4568             if(this.max_height) {
4569                 this.setSize(w,Math.min(
4570                     this.max_height,
4571                     Roo.lib.Dom.getViewportHeight(true) - 60
4572                 ));
4573                 
4574                 return;
4575             }
4576             
4577             if(!this.fit_content) {
4578                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4579                 return;
4580             }
4581             
4582             this.setSize(w, Math.min(
4583                 60 +
4584                 this.headerEl.getHeight() + 
4585                 this.footerEl.getHeight() + 
4586                 this.getChildHeight(this.bodyEl.dom.childNodes),
4587                 Roo.lib.Dom.getViewportHeight(true) - 60)
4588             );
4589         }
4590         
4591     },
4592
4593     setSize : function(w,h)
4594     {
4595         if (!w && !h) {
4596             return;
4597         }
4598         
4599         this.resizeTo(w,h);
4600     },
4601
4602     show : function() {
4603
4604         if (!this.rendered) {
4605             this.render();
4606         }
4607         this.toggleHeaderInput(false);
4608         //this.el.setStyle('display', 'block');
4609         this.el.removeClass('hideing');
4610         this.el.dom.style.display='block';
4611         
4612         Roo.get(document.body).addClass('modal-open');
4613  
4614         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4615             
4616             (function(){
4617                 this.el.addClass('show');
4618                 this.el.addClass('in');
4619             }).defer(50, this);
4620         }else{
4621             this.el.addClass('show');
4622             this.el.addClass('in');
4623         }
4624
4625         // not sure how we can show data in here..
4626         //if (this.tmpl) {
4627         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4628         //}
4629
4630         Roo.get(document.body).addClass("x-body-masked");
4631         
4632         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4633         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4634         this.maskEl.dom.style.display = 'block';
4635         this.maskEl.addClass('show');
4636         
4637         
4638         this.resize();
4639         
4640         this.fireEvent('show', this);
4641
4642         // set zindex here - otherwise it appears to be ignored...
4643         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4644
4645         (function () {
4646             this.items.forEach( function(e) {
4647                 e.layout ? e.layout() : false;
4648
4649             });
4650         }).defer(100,this);
4651
4652     },
4653     hide : function()
4654     {
4655         if(this.fireEvent("beforehide", this) !== false){
4656             
4657             this.maskEl.removeClass('show');
4658             
4659             this.maskEl.dom.style.display = '';
4660             Roo.get(document.body).removeClass("x-body-masked");
4661             this.el.removeClass('in');
4662             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4663
4664             if(this.animate){ // why
4665                 this.el.addClass('hideing');
4666                 this.el.removeClass('show');
4667                 (function(){
4668                     if (!this.el.hasClass('hideing')) {
4669                         return; // it's been shown again...
4670                     }
4671                     
4672                     this.el.dom.style.display='';
4673
4674                     Roo.get(document.body).removeClass('modal-open');
4675                     this.el.removeClass('hideing');
4676                 }).defer(150,this);
4677                 
4678             }else{
4679                 this.el.removeClass('show');
4680                 this.el.dom.style.display='';
4681                 Roo.get(document.body).removeClass('modal-open');
4682
4683             }
4684             this.fireEvent('hide', this);
4685         }
4686     },
4687     isVisible : function()
4688     {
4689         
4690         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4691         
4692     },
4693
4694     addButton : function(str, cb)
4695     {
4696
4697
4698         var b = Roo.apply({}, { html : str } );
4699         b.xns = b.xns || Roo.bootstrap;
4700         b.xtype = b.xtype || 'Button';
4701         if (typeof(b.listeners) == 'undefined') {
4702             b.listeners = { click : cb.createDelegate(this)  };
4703         }
4704
4705         var btn = Roo.factory(b);
4706
4707         btn.render(this.getButtonContainer());
4708
4709         return btn;
4710
4711     },
4712
4713     setDefaultButton : function(btn)
4714     {
4715         //this.el.select('.modal-footer').()
4716     },
4717
4718     resizeTo: function(w,h)
4719     {
4720         this.dialogEl.setWidth(w);
4721         
4722         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4723
4724         this.bodyEl.setHeight(h - diff);
4725         
4726         this.fireEvent('resize', this);
4727     },
4728     
4729     setContentSize  : function(w, h)
4730     {
4731
4732     },
4733     onButtonClick: function(btn,e)
4734     {
4735         //Roo.log([a,b,c]);
4736         this.fireEvent('btnclick', btn.name, e);
4737     },
4738      /**
4739      * Set the title of the Dialog
4740      * @param {String} str new Title
4741      */
4742     setTitle: function(str) {
4743         this.titleEl.dom.innerHTML = str;
4744         this.title = str;
4745     },
4746     /**
4747      * Set the body of the Dialog
4748      * @param {String} str new Title
4749      */
4750     setBody: function(str) {
4751         this.bodyEl.dom.innerHTML = str;
4752     },
4753     /**
4754      * Set the body of the Dialog using the template
4755      * @param {Obj} data - apply this data to the template and replace the body contents.
4756      */
4757     applyBody: function(obj)
4758     {
4759         if (!this.tmpl) {
4760             Roo.log("Error - using apply Body without a template");
4761             //code
4762         }
4763         this.tmpl.overwrite(this.bodyEl, obj);
4764     },
4765     
4766     getChildHeight : function(child_nodes)
4767     {
4768         if(
4769             !child_nodes ||
4770             child_nodes.length == 0
4771         ) {
4772             return 0;
4773         }
4774         
4775         var child_height = 0;
4776         
4777         for(var i = 0; i < child_nodes.length; i++) {
4778             
4779             /*
4780             * for modal with tabs...
4781             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4782                 
4783                 var layout_childs = child_nodes[i].childNodes;
4784                 
4785                 for(var j = 0; j < layout_childs.length; j++) {
4786                     
4787                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4788                         
4789                         var layout_body_childs = layout_childs[j].childNodes;
4790                         
4791                         for(var k = 0; k < layout_body_childs.length; k++) {
4792                             
4793                             if(layout_body_childs[k].classList.contains('navbar')) {
4794                                 child_height += layout_body_childs[k].offsetHeight;
4795                                 continue;
4796                             }
4797                             
4798                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4799                                 
4800                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4801                                 
4802                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4803                                     
4804                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4805                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4806                                         continue;
4807                                     }
4808                                     
4809                                 }
4810                                 
4811                             }
4812                             
4813                         }
4814                     }
4815                 }
4816                 continue;
4817             }
4818             */
4819             
4820             child_height += child_nodes[i].offsetHeight;
4821             // Roo.log(child_nodes[i].offsetHeight);
4822         }
4823         
4824         return child_height;
4825     },
4826     toggleHeaderInput : function(is_edit)
4827     {
4828         if (!this.editableTitle) {
4829             return; // not editable.
4830         }
4831         if (is_edit && this.is_header_editing) {
4832             return; // already editing..
4833         }
4834         if (is_edit) {
4835     
4836             this.headerEditEl.dom.value = this.title;
4837             this.headerEditEl.removeClass('d-none');
4838             this.headerEditEl.dom.focus();
4839             this.titleEl.addClass('d-none');
4840             
4841             this.is_header_editing = true;
4842             return
4843         }
4844         // flip back to not editing.
4845         this.title = this.headerEditEl.dom.value;
4846         this.headerEditEl.addClass('d-none');
4847         this.titleEl.removeClass('d-none');
4848         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4849         this.is_header_editing = false;
4850         this.fireEvent('titlechanged', this, this.title);
4851     
4852             
4853         
4854     }
4855
4856 });
4857
4858
4859 Roo.apply(Roo.bootstrap.Modal,  {
4860     /**
4861          * Button config that displays a single OK button
4862          * @type Object
4863          */
4864         OK :  [{
4865             name : 'ok',
4866             weight : 'primary',
4867             html : 'OK'
4868         }],
4869         /**
4870          * Button config that displays Yes and No buttons
4871          * @type Object
4872          */
4873         YESNO : [
4874             {
4875                 name  : 'no',
4876                 html : 'No'
4877             },
4878             {
4879                 name  :'yes',
4880                 weight : 'primary',
4881                 html : 'Yes'
4882             }
4883         ],
4884
4885         /**
4886          * Button config that displays OK and Cancel buttons
4887          * @type Object
4888          */
4889         OKCANCEL : [
4890             {
4891                name : 'cancel',
4892                 html : 'Cancel'
4893             },
4894             {
4895                 name : 'ok',
4896                 weight : 'primary',
4897                 html : 'OK'
4898             }
4899         ],
4900         /**
4901          * Button config that displays Yes, No and Cancel buttons
4902          * @type Object
4903          */
4904         YESNOCANCEL : [
4905             {
4906                 name : 'yes',
4907                 weight : 'primary',
4908                 html : 'Yes'
4909             },
4910             {
4911                 name : 'no',
4912                 html : 'No'
4913             },
4914             {
4915                 name : 'cancel',
4916                 html : 'Cancel'
4917             }
4918         ],
4919         
4920         zIndex : 10001
4921 });
4922
4923 /*
4924  * - LGPL
4925  *
4926  * messagebox - can be used as a replace
4927  * 
4928  */
4929 /**
4930  * @class Roo.MessageBox
4931  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4932  * Example usage:
4933  *<pre><code>
4934 // Basic alert:
4935 Roo.Msg.alert('Status', 'Changes saved successfully.');
4936
4937 // Prompt for user data:
4938 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4939     if (btn == 'ok'){
4940         // process text value...
4941     }
4942 });
4943
4944 // Show a dialog using config options:
4945 Roo.Msg.show({
4946    title:'Save Changes?',
4947    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4948    buttons: Roo.Msg.YESNOCANCEL,
4949    fn: processResult,
4950    animEl: 'elId'
4951 });
4952 </code></pre>
4953  * @singleton
4954  */
4955 Roo.bootstrap.MessageBox = function(){
4956     var dlg, opt, mask, waitTimer;
4957     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4958     var buttons, activeTextEl, bwidth;
4959
4960     
4961     // private
4962     var handleButton = function(button){
4963         dlg.hide();
4964         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4965     };
4966
4967     // private
4968     var handleHide = function(){
4969         if(opt && opt.cls){
4970             dlg.el.removeClass(opt.cls);
4971         }
4972         //if(waitTimer){
4973         //    Roo.TaskMgr.stop(waitTimer);
4974         //    waitTimer = null;
4975         //}
4976     };
4977
4978     // private
4979     var updateButtons = function(b){
4980         var width = 0;
4981         if(!b){
4982             buttons["ok"].hide();
4983             buttons["cancel"].hide();
4984             buttons["yes"].hide();
4985             buttons["no"].hide();
4986             dlg.footerEl.hide();
4987             
4988             return width;
4989         }
4990         dlg.footerEl.show();
4991         for(var k in buttons){
4992             if(typeof buttons[k] != "function"){
4993                 if(b[k]){
4994                     buttons[k].show();
4995                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4996                     width += buttons[k].el.getWidth()+15;
4997                 }else{
4998                     buttons[k].hide();
4999                 }
5000             }
5001         }
5002         return width;
5003     };
5004
5005     // private
5006     var handleEsc = function(d, k, e){
5007         if(opt && opt.closable !== false){
5008             dlg.hide();
5009         }
5010         if(e){
5011             e.stopEvent();
5012         }
5013     };
5014
5015     return {
5016         /**
5017          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5018          * @return {Roo.BasicDialog} The BasicDialog element
5019          */
5020         getDialog : function(){
5021            if(!dlg){
5022                 dlg = new Roo.bootstrap.Modal( {
5023                     //draggable: true,
5024                     //resizable:false,
5025                     //constraintoviewport:false,
5026                     //fixedcenter:true,
5027                     //collapsible : false,
5028                     //shim:true,
5029                     //modal: true,
5030                 //    width: 'auto',
5031                   //  height:100,
5032                     //buttonAlign:"center",
5033                     closeClick : function(){
5034                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5035                             handleButton("no");
5036                         }else{
5037                             handleButton("cancel");
5038                         }
5039                     }
5040                 });
5041                 dlg.render();
5042                 dlg.on("hide", handleHide);
5043                 mask = dlg.mask;
5044                 //dlg.addKeyListener(27, handleEsc);
5045                 buttons = {};
5046                 this.buttons = buttons;
5047                 var bt = this.buttonText;
5048                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5049                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5050                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5051                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5052                 //Roo.log(buttons);
5053                 bodyEl = dlg.bodyEl.createChild({
5054
5055                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5056                         '<textarea class="roo-mb-textarea"></textarea>' +
5057                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5058                 });
5059                 msgEl = bodyEl.dom.firstChild;
5060                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5061                 textboxEl.enableDisplayMode();
5062                 textboxEl.addKeyListener([10,13], function(){
5063                     if(dlg.isVisible() && opt && opt.buttons){
5064                         if(opt.buttons.ok){
5065                             handleButton("ok");
5066                         }else if(opt.buttons.yes){
5067                             handleButton("yes");
5068                         }
5069                     }
5070                 });
5071                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5072                 textareaEl.enableDisplayMode();
5073                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5074                 progressEl.enableDisplayMode();
5075                 
5076                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5077                 var pf = progressEl.dom.firstChild;
5078                 if (pf) {
5079                     pp = Roo.get(pf.firstChild);
5080                     pp.setHeight(pf.offsetHeight);
5081                 }
5082                 
5083             }
5084             return dlg;
5085         },
5086
5087         /**
5088          * Updates the message box body text
5089          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5090          * the XHTML-compliant non-breaking space character '&amp;#160;')
5091          * @return {Roo.MessageBox} This message box
5092          */
5093         updateText : function(text)
5094         {
5095             if(!dlg.isVisible() && !opt.width){
5096                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5097                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5098             }
5099             msgEl.innerHTML = text || '&#160;';
5100       
5101             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5102             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5103             var w = Math.max(
5104                     Math.min(opt.width || cw , this.maxWidth), 
5105                     Math.max(opt.minWidth || this.minWidth, bwidth)
5106             );
5107             if(opt.prompt){
5108                 activeTextEl.setWidth(w);
5109             }
5110             if(dlg.isVisible()){
5111                 dlg.fixedcenter = false;
5112             }
5113             // to big, make it scroll. = But as usual stupid IE does not support
5114             // !important..
5115             
5116             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5117                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5118                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5119             } else {
5120                 bodyEl.dom.style.height = '';
5121                 bodyEl.dom.style.overflowY = '';
5122             }
5123             if (cw > w) {
5124                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5125             } else {
5126                 bodyEl.dom.style.overflowX = '';
5127             }
5128             
5129             dlg.setContentSize(w, bodyEl.getHeight());
5130             if(dlg.isVisible()){
5131                 dlg.fixedcenter = true;
5132             }
5133             return this;
5134         },
5135
5136         /**
5137          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5138          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5139          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5140          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5141          * @return {Roo.MessageBox} This message box
5142          */
5143         updateProgress : function(value, text){
5144             if(text){
5145                 this.updateText(text);
5146             }
5147             
5148             if (pp) { // weird bug on my firefox - for some reason this is not defined
5149                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5150                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5151             }
5152             return this;
5153         },        
5154
5155         /**
5156          * Returns true if the message box is currently displayed
5157          * @return {Boolean} True if the message box is visible, else false
5158          */
5159         isVisible : function(){
5160             return dlg && dlg.isVisible();  
5161         },
5162
5163         /**
5164          * Hides the message box if it is displayed
5165          */
5166         hide : function(){
5167             if(this.isVisible()){
5168                 dlg.hide();
5169             }  
5170         },
5171
5172         /**
5173          * Displays a new message box, or reinitializes an existing message box, based on the config options
5174          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5175          * The following config object properties are supported:
5176          * <pre>
5177 Property    Type             Description
5178 ----------  ---------------  ------------------------------------------------------------------------------------
5179 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5180                                    closes (defaults to undefined)
5181 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5182                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5183 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5184                                    progress and wait dialogs will ignore this property and always hide the
5185                                    close button as they can only be closed programmatically.
5186 cls               String           A custom CSS class to apply to the message box element
5187 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5188                                    displayed (defaults to 75)
5189 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5190                                    function will be btn (the name of the button that was clicked, if applicable,
5191                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5192                                    Progress and wait dialogs will ignore this option since they do not respond to
5193                                    user actions and can only be closed programmatically, so any required function
5194                                    should be called by the same code after it closes the dialog.
5195 icon              String           A CSS class that provides a background image to be used as an icon for
5196                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5197 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5198 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5199 modal             Boolean          False to allow user interaction with the page while the message box is
5200                                    displayed (defaults to true)
5201 msg               String           A string that will replace the existing message box body text (defaults
5202                                    to the XHTML-compliant non-breaking space character '&#160;')
5203 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5204 progress          Boolean          True to display a progress bar (defaults to false)
5205 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5206 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5207 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5208 title             String           The title text
5209 value             String           The string value to set into the active textbox element if displayed
5210 wait              Boolean          True to display a progress bar (defaults to false)
5211 width             Number           The width of the dialog in pixels
5212 </pre>
5213          *
5214          * Example usage:
5215          * <pre><code>
5216 Roo.Msg.show({
5217    title: 'Address',
5218    msg: 'Please enter your address:',
5219    width: 300,
5220    buttons: Roo.MessageBox.OKCANCEL,
5221    multiline: true,
5222    fn: saveAddress,
5223    animEl: 'addAddressBtn'
5224 });
5225 </code></pre>
5226          * @param {Object} config Configuration options
5227          * @return {Roo.MessageBox} This message box
5228          */
5229         show : function(options)
5230         {
5231             
5232             // this causes nightmares if you show one dialog after another
5233             // especially on callbacks..
5234              
5235             if(this.isVisible()){
5236                 
5237                 this.hide();
5238                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5239                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5240                 Roo.log("New Dialog Message:" +  options.msg )
5241                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5242                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5243                 
5244             }
5245             var d = this.getDialog();
5246             opt = options;
5247             d.setTitle(opt.title || "&#160;");
5248             d.closeEl.setDisplayed(opt.closable !== false);
5249             activeTextEl = textboxEl;
5250             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5251             if(opt.prompt){
5252                 if(opt.multiline){
5253                     textboxEl.hide();
5254                     textareaEl.show();
5255                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5256                         opt.multiline : this.defaultTextHeight);
5257                     activeTextEl = textareaEl;
5258                 }else{
5259                     textboxEl.show();
5260                     textareaEl.hide();
5261                 }
5262             }else{
5263                 textboxEl.hide();
5264                 textareaEl.hide();
5265             }
5266             progressEl.setDisplayed(opt.progress === true);
5267             if (opt.progress) {
5268                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5269             }
5270             this.updateProgress(0);
5271             activeTextEl.dom.value = opt.value || "";
5272             if(opt.prompt){
5273                 dlg.setDefaultButton(activeTextEl);
5274             }else{
5275                 var bs = opt.buttons;
5276                 var db = null;
5277                 if(bs && bs.ok){
5278                     db = buttons["ok"];
5279                 }else if(bs && bs.yes){
5280                     db = buttons["yes"];
5281                 }
5282                 dlg.setDefaultButton(db);
5283             }
5284             bwidth = updateButtons(opt.buttons);
5285             this.updateText(opt.msg);
5286             if(opt.cls){
5287                 d.el.addClass(opt.cls);
5288             }
5289             d.proxyDrag = opt.proxyDrag === true;
5290             d.modal = opt.modal !== false;
5291             d.mask = opt.modal !== false ? mask : false;
5292             if(!d.isVisible()){
5293                 // force it to the end of the z-index stack so it gets a cursor in FF
5294                 document.body.appendChild(dlg.el.dom);
5295                 d.animateTarget = null;
5296                 d.show(options.animEl);
5297             }
5298             return this;
5299         },
5300
5301         /**
5302          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5303          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5304          * and closing the message box when the process is complete.
5305          * @param {String} title The title bar text
5306          * @param {String} msg The message box body text
5307          * @return {Roo.MessageBox} This message box
5308          */
5309         progress : function(title, msg){
5310             this.show({
5311                 title : title,
5312                 msg : msg,
5313                 buttons: false,
5314                 progress:true,
5315                 closable:false,
5316                 minWidth: this.minProgressWidth,
5317                 modal : true
5318             });
5319             return this;
5320         },
5321
5322         /**
5323          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5324          * If a callback function is passed it will be called after the user clicks the button, and the
5325          * id of the button that was clicked will be passed as the only parameter to the callback
5326          * (could also be the top-right close button).
5327          * @param {String} title The title bar text
5328          * @param {String} msg The message box body text
5329          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5330          * @param {Object} scope (optional) The scope of the callback function
5331          * @return {Roo.MessageBox} This message box
5332          */
5333         alert : function(title, msg, fn, scope)
5334         {
5335             this.show({
5336                 title : title,
5337                 msg : msg,
5338                 buttons: this.OK,
5339                 fn: fn,
5340                 closable : false,
5341                 scope : scope,
5342                 modal : true
5343             });
5344             return this;
5345         },
5346
5347         /**
5348          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5349          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5350          * You are responsible for closing the message box when the process is complete.
5351          * @param {String} msg The message box body text
5352          * @param {String} title (optional) The title bar text
5353          * @return {Roo.MessageBox} This message box
5354          */
5355         wait : function(msg, title){
5356             this.show({
5357                 title : title,
5358                 msg : msg,
5359                 buttons: false,
5360                 closable:false,
5361                 progress:true,
5362                 modal:true,
5363                 width:300,
5364                 wait:true
5365             });
5366             waitTimer = Roo.TaskMgr.start({
5367                 run: function(i){
5368                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5369                 },
5370                 interval: 1000
5371             });
5372             return this;
5373         },
5374
5375         /**
5376          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5377          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5378          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5379          * @param {String} title The title bar text
5380          * @param {String} msg The message box body text
5381          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5382          * @param {Object} scope (optional) The scope of the callback function
5383          * @return {Roo.MessageBox} This message box
5384          */
5385         confirm : function(title, msg, fn, scope){
5386             this.show({
5387                 title : title,
5388                 msg : msg,
5389                 buttons: this.YESNO,
5390                 fn: fn,
5391                 scope : scope,
5392                 modal : true
5393             });
5394             return this;
5395         },
5396
5397         /**
5398          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5399          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5400          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5401          * (could also be the top-right close button) and the text that was entered will be passed as the two
5402          * parameters to the callback.
5403          * @param {String} title The title bar text
5404          * @param {String} msg The message box body text
5405          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5406          * @param {Object} scope (optional) The scope of the callback function
5407          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5408          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5409          * @return {Roo.MessageBox} This message box
5410          */
5411         prompt : function(title, msg, fn, scope, multiline){
5412             this.show({
5413                 title : title,
5414                 msg : msg,
5415                 buttons: this.OKCANCEL,
5416                 fn: fn,
5417                 minWidth:250,
5418                 scope : scope,
5419                 prompt:true,
5420                 multiline: multiline,
5421                 modal : true
5422             });
5423             return this;
5424         },
5425
5426         /**
5427          * Button config that displays a single OK button
5428          * @type Object
5429          */
5430         OK : {ok:true},
5431         /**
5432          * Button config that displays Yes and No buttons
5433          * @type Object
5434          */
5435         YESNO : {yes:true, no:true},
5436         /**
5437          * Button config that displays OK and Cancel buttons
5438          * @type Object
5439          */
5440         OKCANCEL : {ok:true, cancel:true},
5441         /**
5442          * Button config that displays Yes, No and Cancel buttons
5443          * @type Object
5444          */
5445         YESNOCANCEL : {yes:true, no:true, cancel:true},
5446
5447         /**
5448          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5449          * @type Number
5450          */
5451         defaultTextHeight : 75,
5452         /**
5453          * The maximum width in pixels of the message box (defaults to 600)
5454          * @type Number
5455          */
5456         maxWidth : 600,
5457         /**
5458          * The minimum width in pixels of the message box (defaults to 100)
5459          * @type Number
5460          */
5461         minWidth : 100,
5462         /**
5463          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5464          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5465          * @type Number
5466          */
5467         minProgressWidth : 250,
5468         /**
5469          * An object containing the default button text strings that can be overriden for localized language support.
5470          * Supported properties are: ok, cancel, yes and no.
5471          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5472          * @type Object
5473          */
5474         buttonText : {
5475             ok : "OK",
5476             cancel : "Cancel",
5477             yes : "Yes",
5478             no : "No"
5479         }
5480     };
5481 }();
5482
5483 /**
5484  * Shorthand for {@link Roo.MessageBox}
5485  */
5486 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5487 Roo.Msg = Roo.Msg || Roo.MessageBox;
5488 /*
5489  * - LGPL
5490  *
5491  * navbar
5492  * 
5493  */
5494
5495 /**
5496  * @class Roo.bootstrap.Navbar
5497  * @extends Roo.bootstrap.Component
5498  * Bootstrap Navbar class
5499
5500  * @constructor
5501  * Create a new Navbar
5502  * @param {Object} config The config object
5503  */
5504
5505
5506 Roo.bootstrap.Navbar = function(config){
5507     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5508     this.addEvents({
5509         // raw events
5510         /**
5511          * @event beforetoggle
5512          * Fire before toggle the menu
5513          * @param {Roo.EventObject} e
5514          */
5515         "beforetoggle" : true
5516     });
5517 };
5518
5519 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5520     
5521     
5522    
5523     // private
5524     navItems : false,
5525     loadMask : false,
5526     
5527     
5528     getAutoCreate : function(){
5529         
5530         
5531         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5532         
5533     },
5534     
5535     initEvents :function ()
5536     {
5537         //Roo.log(this.el.select('.navbar-toggle',true));
5538         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5539         
5540         var mark = {
5541             tag: "div",
5542             cls:"x-dlg-mask"
5543         };
5544         
5545         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5546         
5547         var size = this.el.getSize();
5548         this.maskEl.setSize(size.width, size.height);
5549         this.maskEl.enableDisplayMode("block");
5550         this.maskEl.hide();
5551         
5552         if(this.loadMask){
5553             this.maskEl.show();
5554         }
5555     },
5556     
5557     
5558     getChildContainer : function()
5559     {
5560         if (this.el && this.el.select('.collapse').getCount()) {
5561             return this.el.select('.collapse',true).first();
5562         }
5563         
5564         return this.el;
5565     },
5566     
5567     mask : function()
5568     {
5569         this.maskEl.show();
5570     },
5571     
5572     unmask : function()
5573     {
5574         this.maskEl.hide();
5575     },
5576     onToggle : function()
5577     {
5578         
5579         if(this.fireEvent('beforetoggle', this) === false){
5580             return;
5581         }
5582         var ce = this.el.select('.navbar-collapse',true).first();
5583       
5584         if (!ce.hasClass('show')) {
5585            this.expand();
5586         } else {
5587             this.collapse();
5588         }
5589         
5590         
5591     
5592     },
5593     /**
5594      * Expand the navbar pulldown 
5595      */
5596     expand : function ()
5597     {
5598        
5599         var ce = this.el.select('.navbar-collapse',true).first();
5600         if (ce.hasClass('collapsing')) {
5601             return;
5602         }
5603         ce.dom.style.height = '';
5604                // show it...
5605         ce.addClass('in'); // old...
5606         ce.removeClass('collapse');
5607         ce.addClass('show');
5608         var h = ce.getHeight();
5609         Roo.log(h);
5610         ce.removeClass('show');
5611         // at this point we should be able to see it..
5612         ce.addClass('collapsing');
5613         
5614         ce.setHeight(0); // resize it ...
5615         ce.on('transitionend', function() {
5616             //Roo.log('done transition');
5617             ce.removeClass('collapsing');
5618             ce.addClass('show');
5619             ce.removeClass('collapse');
5620
5621             ce.dom.style.height = '';
5622         }, this, { single: true} );
5623         ce.setHeight(h);
5624         ce.dom.scrollTop = 0;
5625     },
5626     /**
5627      * Collapse the navbar pulldown 
5628      */
5629     collapse : function()
5630     {
5631          var ce = this.el.select('.navbar-collapse',true).first();
5632        
5633         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5634             // it's collapsed or collapsing..
5635             return;
5636         }
5637         ce.removeClass('in'); // old...
5638         ce.setHeight(ce.getHeight());
5639         ce.removeClass('show');
5640         ce.addClass('collapsing');
5641         
5642         ce.on('transitionend', function() {
5643             ce.dom.style.height = '';
5644             ce.removeClass('collapsing');
5645             ce.addClass('collapse');
5646         }, this, { single: true} );
5647         ce.setHeight(0);
5648     }
5649     
5650     
5651     
5652 });
5653
5654
5655
5656  
5657
5658  /*
5659  * - LGPL
5660  *
5661  * navbar
5662  * 
5663  */
5664
5665 /**
5666  * @class Roo.bootstrap.NavSimplebar
5667  * @extends Roo.bootstrap.Navbar
5668  * Bootstrap Sidebar class
5669  *
5670  * @cfg {Boolean} inverse is inverted color
5671  * 
5672  * @cfg {String} type (nav | pills | tabs)
5673  * @cfg {Boolean} arrangement stacked | justified
5674  * @cfg {String} align (left | right) alignment
5675  * 
5676  * @cfg {Boolean} main (true|false) main nav bar? default false
5677  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5678  * 
5679  * @cfg {String} tag (header|footer|nav|div) default is nav 
5680
5681  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5682  * 
5683  * 
5684  * @constructor
5685  * Create a new Sidebar
5686  * @param {Object} config The config object
5687  */
5688
5689
5690 Roo.bootstrap.NavSimplebar = function(config){
5691     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5692 };
5693
5694 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5695     
5696     inverse: false,
5697     
5698     type: false,
5699     arrangement: '',
5700     align : false,
5701     
5702     weight : 'light',
5703     
5704     main : false,
5705     
5706     
5707     tag : false,
5708     
5709     
5710     getAutoCreate : function(){
5711         
5712         
5713         var cfg = {
5714             tag : this.tag || 'div',
5715             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5716         };
5717         if (['light','white'].indexOf(this.weight) > -1) {
5718             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5719         }
5720         cfg.cls += ' bg-' + this.weight;
5721         
5722         if (this.inverse) {
5723             cfg.cls += ' navbar-inverse';
5724             
5725         }
5726         
5727         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5728         
5729         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5730             return cfg;
5731         }
5732         
5733         
5734     
5735         
5736         cfg.cn = [
5737             {
5738                 cls: 'nav nav-' + this.xtype,
5739                 tag : 'ul'
5740             }
5741         ];
5742         
5743          
5744         this.type = this.type || 'nav';
5745         if (['tabs','pills'].indexOf(this.type) != -1) {
5746             cfg.cn[0].cls += ' nav-' + this.type
5747         
5748         
5749         } else {
5750             if (this.type!=='nav') {
5751                 Roo.log('nav type must be nav/tabs/pills')
5752             }
5753             cfg.cn[0].cls += ' navbar-nav'
5754         }
5755         
5756         
5757         
5758         
5759         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5760             cfg.cn[0].cls += ' nav-' + this.arrangement;
5761         }
5762         
5763         
5764         if (this.align === 'right') {
5765             cfg.cn[0].cls += ' navbar-right';
5766         }
5767         
5768         
5769         
5770         
5771         return cfg;
5772     
5773         
5774     }
5775     
5776     
5777     
5778 });
5779
5780
5781
5782  
5783
5784  
5785        /*
5786  * - LGPL
5787  *
5788  * navbar
5789  * navbar-fixed-top
5790  * navbar-expand-md  fixed-top 
5791  */
5792
5793 /**
5794  * @class Roo.bootstrap.NavHeaderbar
5795  * @extends Roo.bootstrap.NavSimplebar
5796  * Bootstrap Sidebar class
5797  *
5798  * @cfg {String} brand what is brand
5799  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5800  * @cfg {String} brand_href href of the brand
5801  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5802  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5803  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5804  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5805  * 
5806  * @constructor
5807  * Create a new Sidebar
5808  * @param {Object} config The config object
5809  */
5810
5811
5812 Roo.bootstrap.NavHeaderbar = function(config){
5813     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5814       
5815 };
5816
5817 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5818     
5819     position: '',
5820     brand: '',
5821     brand_href: false,
5822     srButton : true,
5823     autohide : false,
5824     desktopCenter : false,
5825    
5826     
5827     getAutoCreate : function(){
5828         
5829         var   cfg = {
5830             tag: this.nav || 'nav',
5831             cls: 'navbar navbar-expand-md',
5832             role: 'navigation',
5833             cn: []
5834         };
5835         
5836         var cn = cfg.cn;
5837         if (this.desktopCenter) {
5838             cn.push({cls : 'container', cn : []});
5839             cn = cn[0].cn;
5840         }
5841         
5842         if(this.srButton){
5843             var btn = {
5844                 tag: 'button',
5845                 type: 'button',
5846                 cls: 'navbar-toggle navbar-toggler',
5847                 'data-toggle': 'collapse',
5848                 cn: [
5849                     {
5850                         tag: 'span',
5851                         cls: 'sr-only',
5852                         html: 'Toggle navigation'
5853                     },
5854                     {
5855                         tag: 'span',
5856                         cls: 'icon-bar navbar-toggler-icon'
5857                     },
5858                     {
5859                         tag: 'span',
5860                         cls: 'icon-bar'
5861                     },
5862                     {
5863                         tag: 'span',
5864                         cls: 'icon-bar'
5865                     }
5866                 ]
5867             };
5868             
5869             cn.push( Roo.bootstrap.version == 4 ? btn : {
5870                 tag: 'div',
5871                 cls: 'navbar-header',
5872                 cn: [
5873                     btn
5874                 ]
5875             });
5876         }
5877         
5878         cn.push({
5879             tag: 'div',
5880             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5881             cn : []
5882         });
5883         
5884         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5885         
5886         if (['light','white'].indexOf(this.weight) > -1) {
5887             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5888         }
5889         cfg.cls += ' bg-' + this.weight;
5890         
5891         
5892         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5893             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5894             
5895             // tag can override this..
5896             
5897             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5898         }
5899         
5900         if (this.brand !== '') {
5901             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5902             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5903                 tag: 'a',
5904                 href: this.brand_href ? this.brand_href : '#',
5905                 cls: 'navbar-brand',
5906                 cn: [
5907                 this.brand
5908                 ]
5909             });
5910         }
5911         
5912         if(this.main){
5913             cfg.cls += ' main-nav';
5914         }
5915         
5916         
5917         return cfg;
5918
5919         
5920     },
5921     getHeaderChildContainer : function()
5922     {
5923         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5924             return this.el.select('.navbar-header',true).first();
5925         }
5926         
5927         return this.getChildContainer();
5928     },
5929     
5930     getChildContainer : function()
5931     {
5932          
5933         return this.el.select('.roo-navbar-collapse',true).first();
5934          
5935         
5936     },
5937     
5938     initEvents : function()
5939     {
5940         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5941         
5942         if (this.autohide) {
5943             
5944             var prevScroll = 0;
5945             var ft = this.el;
5946             
5947             Roo.get(document).on('scroll',function(e) {
5948                 var ns = Roo.get(document).getScroll().top;
5949                 var os = prevScroll;
5950                 prevScroll = ns;
5951                 
5952                 if(ns > os){
5953                     ft.removeClass('slideDown');
5954                     ft.addClass('slideUp');
5955                     return;
5956                 }
5957                 ft.removeClass('slideUp');
5958                 ft.addClass('slideDown');
5959                  
5960               
5961           },this);
5962         }
5963     }    
5964     
5965 });
5966
5967
5968
5969  
5970
5971  /*
5972  * - LGPL
5973  *
5974  * navbar
5975  * 
5976  */
5977
5978 /**
5979  * @class Roo.bootstrap.NavSidebar
5980  * @extends Roo.bootstrap.Navbar
5981  * Bootstrap Sidebar class
5982  * 
5983  * @constructor
5984  * Create a new Sidebar
5985  * @param {Object} config The config object
5986  */
5987
5988
5989 Roo.bootstrap.NavSidebar = function(config){
5990     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5991 };
5992
5993 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5994     
5995     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5996     
5997     getAutoCreate : function(){
5998         
5999         
6000         return  {
6001             tag: 'div',
6002             cls: 'sidebar sidebar-nav'
6003         };
6004     
6005         
6006     }
6007     
6008     
6009     
6010 });
6011
6012
6013
6014  
6015
6016  /*
6017  * - LGPL
6018  *
6019  * nav group
6020  * 
6021  */
6022
6023 /**
6024  * @class Roo.bootstrap.NavGroup
6025  * @extends Roo.bootstrap.Component
6026  * Bootstrap NavGroup class
6027  * @cfg {String} align (left|right)
6028  * @cfg {Boolean} inverse
6029  * @cfg {String} type (nav|pills|tab) default nav
6030  * @cfg {String} navId - reference Id for navbar.
6031  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6032  * 
6033  * @constructor
6034  * Create a new nav group
6035  * @param {Object} config The config object
6036  */
6037
6038 Roo.bootstrap.NavGroup = function(config){
6039     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6040     this.navItems = [];
6041    
6042     Roo.bootstrap.NavGroup.register(this);
6043      this.addEvents({
6044         /**
6045              * @event changed
6046              * Fires when the active item changes
6047              * @param {Roo.bootstrap.NavGroup} this
6048              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6049              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6050          */
6051         'changed': true
6052      });
6053     
6054 };
6055
6056 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6057     
6058     align: '',
6059     inverse: false,
6060     form: false,
6061     type: 'nav',
6062     navId : '',
6063     // private
6064     pilltype : true,
6065     
6066     navItems : false, 
6067     
6068     getAutoCreate : function()
6069     {
6070         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6071         
6072         cfg = {
6073             tag : 'ul',
6074             cls: 'nav' 
6075         };
6076         if (Roo.bootstrap.version == 4) {
6077             if (['tabs','pills'].indexOf(this.type) != -1) {
6078                 cfg.cls += ' nav-' + this.type; 
6079             } else {
6080                 // trying to remove so header bar can right align top?
6081                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6082                     // do not use on header bar... 
6083                     cfg.cls += ' navbar-nav';
6084                 }
6085             }
6086             
6087         } else {
6088             if (['tabs','pills'].indexOf(this.type) != -1) {
6089                 cfg.cls += ' nav-' + this.type
6090             } else {
6091                 if (this.type !== 'nav') {
6092                     Roo.log('nav type must be nav/tabs/pills')
6093                 }
6094                 cfg.cls += ' navbar-nav'
6095             }
6096         }
6097         
6098         if (this.parent() && this.parent().sidebar) {
6099             cfg = {
6100                 tag: 'ul',
6101                 cls: 'dashboard-menu sidebar-menu'
6102             };
6103             
6104             return cfg;
6105         }
6106         
6107         if (this.form === true) {
6108             cfg = {
6109                 tag: 'form',
6110                 cls: 'navbar-form form-inline'
6111             };
6112             //nav navbar-right ml-md-auto
6113             if (this.align === 'right') {
6114                 cfg.cls += ' navbar-right ml-md-auto';
6115             } else {
6116                 cfg.cls += ' navbar-left';
6117             }
6118         }
6119         
6120         if (this.align === 'right') {
6121             cfg.cls += ' navbar-right ml-md-auto';
6122         } else {
6123             cfg.cls += ' mr-auto';
6124         }
6125         
6126         if (this.inverse) {
6127             cfg.cls += ' navbar-inverse';
6128             
6129         }
6130         
6131         
6132         return cfg;
6133     },
6134     /**
6135     * sets the active Navigation item
6136     * @param {Roo.bootstrap.NavItem} the new current navitem
6137     */
6138     setActiveItem : function(item)
6139     {
6140         var prev = false;
6141         Roo.each(this.navItems, function(v){
6142             if (v == item) {
6143                 return ;
6144             }
6145             if (v.isActive()) {
6146                 v.setActive(false, true);
6147                 prev = v;
6148                 
6149             }
6150             
6151         });
6152
6153         item.setActive(true, true);
6154         this.fireEvent('changed', this, item, prev);
6155         
6156         
6157     },
6158     /**
6159     * gets the active Navigation item
6160     * @return {Roo.bootstrap.NavItem} the current navitem
6161     */
6162     getActive : function()
6163     {
6164         
6165         var prev = false;
6166         Roo.each(this.navItems, function(v){
6167             
6168             if (v.isActive()) {
6169                 prev = v;
6170                 
6171             }
6172             
6173         });
6174         return prev;
6175     },
6176     
6177     indexOfNav : function()
6178     {
6179         
6180         var prev = false;
6181         Roo.each(this.navItems, function(v,i){
6182             
6183             if (v.isActive()) {
6184                 prev = i;
6185                 
6186             }
6187             
6188         });
6189         return prev;
6190     },
6191     /**
6192     * adds a Navigation item
6193     * @param {Roo.bootstrap.NavItem} the navitem to add
6194     */
6195     addItem : function(cfg)
6196     {
6197         if (this.form && Roo.bootstrap.version == 4) {
6198             cfg.tag = 'div';
6199         }
6200         var cn = new Roo.bootstrap.NavItem(cfg);
6201         this.register(cn);
6202         cn.parentId = this.id;
6203         cn.onRender(this.el, null);
6204         return cn;
6205     },
6206     /**
6207     * register a Navigation item
6208     * @param {Roo.bootstrap.NavItem} the navitem to add
6209     */
6210     register : function(item)
6211     {
6212         this.navItems.push( item);
6213         item.navId = this.navId;
6214     
6215     },
6216     
6217     /**
6218     * clear all the Navigation item
6219     */
6220    
6221     clearAll : function()
6222     {
6223         this.navItems = [];
6224         this.el.dom.innerHTML = '';
6225     },
6226     
6227     getNavItem: function(tabId)
6228     {
6229         var ret = false;
6230         Roo.each(this.navItems, function(e) {
6231             if (e.tabId == tabId) {
6232                ret =  e;
6233                return false;
6234             }
6235             return true;
6236             
6237         });
6238         return ret;
6239     },
6240     
6241     setActiveNext : function()
6242     {
6243         var i = this.indexOfNav(this.getActive());
6244         if (i > this.navItems.length) {
6245             return;
6246         }
6247         this.setActiveItem(this.navItems[i+1]);
6248     },
6249     setActivePrev : function()
6250     {
6251         var i = this.indexOfNav(this.getActive());
6252         if (i  < 1) {
6253             return;
6254         }
6255         this.setActiveItem(this.navItems[i-1]);
6256     },
6257     clearWasActive : function(except) {
6258         Roo.each(this.navItems, function(e) {
6259             if (e.tabId != except.tabId && e.was_active) {
6260                e.was_active = false;
6261                return false;
6262             }
6263             return true;
6264             
6265         });
6266     },
6267     getWasActive : function ()
6268     {
6269         var r = false;
6270         Roo.each(this.navItems, function(e) {
6271             if (e.was_active) {
6272                r = e;
6273                return false;
6274             }
6275             return true;
6276             
6277         });
6278         return r;
6279     }
6280     
6281     
6282 });
6283
6284  
6285 Roo.apply(Roo.bootstrap.NavGroup, {
6286     
6287     groups: {},
6288      /**
6289     * register a Navigation Group
6290     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6291     */
6292     register : function(navgrp)
6293     {
6294         this.groups[navgrp.navId] = navgrp;
6295         
6296     },
6297     /**
6298     * fetch a Navigation Group based on the navigation ID
6299     * @param {string} the navgroup to add
6300     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6301     */
6302     get: function(navId) {
6303         if (typeof(this.groups[navId]) == 'undefined') {
6304             return false;
6305             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6306         }
6307         return this.groups[navId] ;
6308     }
6309     
6310     
6311     
6312 });
6313
6314  /*
6315  * - LGPL
6316  *
6317  * row
6318  * 
6319  */
6320
6321 /**
6322  * @class Roo.bootstrap.NavItem
6323  * @extends Roo.bootstrap.Component
6324  * Bootstrap Navbar.NavItem class
6325  * @cfg {String} href  link to
6326  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6327  * @cfg {Boolean} button_outline show and outlined button
6328  * @cfg {String} html content of button
6329  * @cfg {String} badge text inside badge
6330  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6331  * @cfg {String} glyphicon DEPRICATED - use fa
6332  * @cfg {String} icon DEPRICATED - use fa
6333  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6334  * @cfg {Boolean} active Is item active
6335  * @cfg {Boolean} disabled Is item disabled
6336  * @cfg {String} linkcls  Link Class
6337  * @cfg {Boolean} preventDefault (true | false) default false
6338  * @cfg {String} tabId the tab that this item activates.
6339  * @cfg {String} tagtype (a|span) render as a href or span?
6340  * @cfg {Boolean} animateRef (true|false) link to element default false  
6341   
6342  * @constructor
6343  * Create a new Navbar Item
6344  * @param {Object} config The config object
6345  */
6346 Roo.bootstrap.NavItem = function(config){
6347     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6348     this.addEvents({
6349         // raw events
6350         /**
6351          * @event click
6352          * The raw click event for the entire grid.
6353          * @param {Roo.EventObject} e
6354          */
6355         "click" : true,
6356          /**
6357             * @event changed
6358             * Fires when the active item active state changes
6359             * @param {Roo.bootstrap.NavItem} this
6360             * @param {boolean} state the new state
6361              
6362          */
6363         'changed': true,
6364         /**
6365             * @event scrollto
6366             * Fires when scroll to element
6367             * @param {Roo.bootstrap.NavItem} this
6368             * @param {Object} options
6369             * @param {Roo.EventObject} e
6370              
6371          */
6372         'scrollto': true
6373     });
6374    
6375 };
6376
6377 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6378     
6379     href: false,
6380     html: '',
6381     badge: '',
6382     icon: false,
6383     fa : false,
6384     glyphicon: false,
6385     active: false,
6386     preventDefault : false,
6387     tabId : false,
6388     tagtype : 'a',
6389     tag: 'li',
6390     disabled : false,
6391     animateRef : false,
6392     was_active : false,
6393     button_weight : '',
6394     button_outline : false,
6395     linkcls : '',
6396     navLink: false,
6397     
6398     getAutoCreate : function(){
6399          
6400         var cfg = {
6401             tag: this.tag,
6402             cls: 'nav-item'
6403         };
6404         
6405         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6406         
6407         if (this.active) {
6408             cfg.cls +=  ' active' ;
6409         }
6410         if (this.disabled) {
6411             cfg.cls += ' disabled';
6412         }
6413         
6414         // BS4 only?
6415         if (this.button_weight.length) {
6416             cfg.tag = this.href ? 'a' : 'button';
6417             cfg.html = this.html || '';
6418             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6419             if (this.href) {
6420                 cfg.href = this.href;
6421             }
6422             if (this.fa) {
6423                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6424             } else {
6425                 cfg.cls += " nav-html";
6426             }
6427             
6428             // menu .. should add dropdown-menu class - so no need for carat..
6429             
6430             if (this.badge !== '') {
6431                  
6432                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6433             }
6434             return cfg;
6435         }
6436         
6437         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6438             cfg.cn = [
6439                 {
6440                     tag: this.tagtype,
6441                     href : this.href || "#",
6442                     html: this.html || '',
6443                     cls : ''
6444                 }
6445             ];
6446             if (this.tagtype == 'a') {
6447                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6448         
6449             }
6450             if (this.icon) {
6451                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6452             } else  if (this.fa) {
6453                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6454             } else if(this.glyphicon) {
6455                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6456             } else {
6457                 cfg.cn[0].cls += " nav-html";
6458             }
6459             
6460             if (this.menu) {
6461                 cfg.cn[0].html += " <span class='caret'></span>";
6462              
6463             }
6464             
6465             if (this.badge !== '') {
6466                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6467             }
6468         }
6469         
6470         
6471         
6472         return cfg;
6473     },
6474     onRender : function(ct, position)
6475     {
6476        // Roo.log("Call onRender: " + this.xtype);
6477         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6478             this.tag = 'div';
6479         }
6480         
6481         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6482         this.navLink = this.el.select('.nav-link',true).first();
6483         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6484         return ret;
6485     },
6486       
6487     
6488     initEvents: function() 
6489     {
6490         if (typeof (this.menu) != 'undefined') {
6491             this.menu.parentType = this.xtype;
6492             this.menu.triggerEl = this.el;
6493             this.menu = this.addxtype(Roo.apply({}, this.menu));
6494         }
6495         
6496         this.el.on('click', this.onClick, this);
6497         
6498         //if(this.tagtype == 'span'){
6499         //    this.el.select('span',true).on('click', this.onClick, this);
6500         //}
6501        
6502         // at this point parent should be available..
6503         this.parent().register(this);
6504     },
6505     
6506     onClick : function(e)
6507     {
6508         if (e.getTarget('.dropdown-menu-item')) {
6509             // did you click on a menu itemm.... - then don't trigger onclick..
6510             return;
6511         }
6512         
6513         if(
6514                 this.preventDefault || 
6515                 this.href == '#' 
6516         ){
6517             Roo.log("NavItem - prevent Default?");
6518             e.preventDefault();
6519         }
6520         
6521         if (this.disabled) {
6522             return;
6523         }
6524         
6525         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6526         if (tg && tg.transition) {
6527             Roo.log("waiting for the transitionend");
6528             return;
6529         }
6530         
6531         
6532         
6533         //Roo.log("fire event clicked");
6534         if(this.fireEvent('click', this, e) === false){
6535             return;
6536         };
6537         
6538         if(this.tagtype == 'span'){
6539             return;
6540         }
6541         
6542         //Roo.log(this.href);
6543         var ael = this.el.select('a',true).first();
6544         //Roo.log(ael);
6545         
6546         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6547             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6548             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6549                 return; // ignore... - it's a 'hash' to another page.
6550             }
6551             Roo.log("NavItem - prevent Default?");
6552             e.preventDefault();
6553             this.scrollToElement(e);
6554         }
6555         
6556         
6557         var p =  this.parent();
6558    
6559         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6560             if (typeof(p.setActiveItem) !== 'undefined') {
6561                 p.setActiveItem(this);
6562             }
6563         }
6564         
6565         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6566         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6567             // remove the collapsed menu expand...
6568             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6569         }
6570     },
6571     
6572     isActive: function () {
6573         return this.active
6574     },
6575     setActive : function(state, fire, is_was_active)
6576     {
6577         if (this.active && !state && this.navId) {
6578             this.was_active = true;
6579             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6580             if (nv) {
6581                 nv.clearWasActive(this);
6582             }
6583             
6584         }
6585         this.active = state;
6586         
6587         if (!state ) {
6588             this.el.removeClass('active');
6589             this.navLink ? this.navLink.removeClass('active') : false;
6590         } else if (!this.el.hasClass('active')) {
6591             
6592             this.el.addClass('active');
6593             if (Roo.bootstrap.version == 4 && this.navLink ) {
6594                 this.navLink.addClass('active');
6595             }
6596             
6597         }
6598         if (fire) {
6599             this.fireEvent('changed', this, state);
6600         }
6601         
6602         // show a panel if it's registered and related..
6603         
6604         if (!this.navId || !this.tabId || !state || is_was_active) {
6605             return;
6606         }
6607         
6608         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6609         if (!tg) {
6610             return;
6611         }
6612         var pan = tg.getPanelByName(this.tabId);
6613         if (!pan) {
6614             return;
6615         }
6616         // if we can not flip to new panel - go back to old nav highlight..
6617         if (false == tg.showPanel(pan)) {
6618             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6619             if (nv) {
6620                 var onav = nv.getWasActive();
6621                 if (onav) {
6622                     onav.setActive(true, false, true);
6623                 }
6624             }
6625             
6626         }
6627         
6628         
6629         
6630     },
6631      // this should not be here...
6632     setDisabled : function(state)
6633     {
6634         this.disabled = state;
6635         if (!state ) {
6636             this.el.removeClass('disabled');
6637         } else if (!this.el.hasClass('disabled')) {
6638             this.el.addClass('disabled');
6639         }
6640         
6641     },
6642     
6643     /**
6644      * Fetch the element to display the tooltip on.
6645      * @return {Roo.Element} defaults to this.el
6646      */
6647     tooltipEl : function()
6648     {
6649         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6650     },
6651     
6652     scrollToElement : function(e)
6653     {
6654         var c = document.body;
6655         
6656         /*
6657          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6658          */
6659         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6660             c = document.documentElement;
6661         }
6662         
6663         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6664         
6665         if(!target){
6666             return;
6667         }
6668
6669         var o = target.calcOffsetsTo(c);
6670         
6671         var options = {
6672             target : target,
6673             value : o[1]
6674         };
6675         
6676         this.fireEvent('scrollto', this, options, e);
6677         
6678         Roo.get(c).scrollTo('top', options.value, true);
6679         
6680         return;
6681     },
6682     /**
6683      * Set the HTML (text content) of the item
6684      * @param {string} html  content for the nav item
6685      */
6686     setHtml : function(html)
6687     {
6688         this.html = html;
6689         this.htmlEl.dom.innerHTML = html;
6690         
6691     } 
6692 });
6693  
6694
6695  /*
6696  * - LGPL
6697  *
6698  * sidebar item
6699  *
6700  *  li
6701  *    <span> icon </span>
6702  *    <span> text </span>
6703  *    <span>badge </span>
6704  */
6705
6706 /**
6707  * @class Roo.bootstrap.NavSidebarItem
6708  * @extends Roo.bootstrap.NavItem
6709  * Bootstrap Navbar.NavSidebarItem class
6710  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6711  * {Boolean} open is the menu open
6712  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6713  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6714  * {String} buttonSize (sm|md|lg)the extra classes for the button
6715  * {Boolean} showArrow show arrow next to the text (default true)
6716  * @constructor
6717  * Create a new Navbar Button
6718  * @param {Object} config The config object
6719  */
6720 Roo.bootstrap.NavSidebarItem = function(config){
6721     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6722     this.addEvents({
6723         // raw events
6724         /**
6725          * @event click
6726          * The raw click event for the entire grid.
6727          * @param {Roo.EventObject} e
6728          */
6729         "click" : true,
6730          /**
6731             * @event changed
6732             * Fires when the active item active state changes
6733             * @param {Roo.bootstrap.NavSidebarItem} this
6734             * @param {boolean} state the new state
6735              
6736          */
6737         'changed': true
6738     });
6739    
6740 };
6741
6742 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6743     
6744     badgeWeight : 'default',
6745     
6746     open: false,
6747     
6748     buttonView : false,
6749     
6750     buttonWeight : 'default',
6751     
6752     buttonSize : 'md',
6753     
6754     showArrow : true,
6755     
6756     getAutoCreate : function(){
6757         
6758         
6759         var a = {
6760                 tag: 'a',
6761                 href : this.href || '#',
6762                 cls: '',
6763                 html : '',
6764                 cn : []
6765         };
6766         
6767         if(this.buttonView){
6768             a = {
6769                 tag: 'button',
6770                 href : this.href || '#',
6771                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6772                 html : this.html,
6773                 cn : []
6774             };
6775         }
6776         
6777         var cfg = {
6778             tag: 'li',
6779             cls: '',
6780             cn: [ a ]
6781         };
6782         
6783         if (this.active) {
6784             cfg.cls += ' active';
6785         }
6786         
6787         if (this.disabled) {
6788             cfg.cls += ' disabled';
6789         }
6790         if (this.open) {
6791             cfg.cls += ' open x-open';
6792         }
6793         // left icon..
6794         if (this.glyphicon || this.icon) {
6795             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6796             a.cn.push({ tag : 'i', cls : c }) ;
6797         }
6798         
6799         if(!this.buttonView){
6800             var span = {
6801                 tag: 'span',
6802                 html : this.html || ''
6803             };
6804
6805             a.cn.push(span);
6806             
6807         }
6808         
6809         if (this.badge !== '') {
6810             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6811         }
6812         
6813         if (this.menu) {
6814             
6815             if(this.showArrow){
6816                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6817             }
6818             
6819             a.cls += ' dropdown-toggle treeview' ;
6820         }
6821         
6822         return cfg;
6823     },
6824     
6825     initEvents : function()
6826     { 
6827         if (typeof (this.menu) != 'undefined') {
6828             this.menu.parentType = this.xtype;
6829             this.menu.triggerEl = this.el;
6830             this.menu = this.addxtype(Roo.apply({}, this.menu));
6831         }
6832         
6833         this.el.on('click', this.onClick, this);
6834         
6835         if(this.badge !== ''){
6836             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6837         }
6838         
6839     },
6840     
6841     onClick : function(e)
6842     {
6843         if(this.disabled){
6844             e.preventDefault();
6845             return;
6846         }
6847         
6848         if(this.preventDefault){
6849             e.preventDefault();
6850         }
6851         
6852         this.fireEvent('click', this, e);
6853     },
6854     
6855     disable : function()
6856     {
6857         this.setDisabled(true);
6858     },
6859     
6860     enable : function()
6861     {
6862         this.setDisabled(false);
6863     },
6864     
6865     setDisabled : function(state)
6866     {
6867         if(this.disabled == state){
6868             return;
6869         }
6870         
6871         this.disabled = state;
6872         
6873         if (state) {
6874             this.el.addClass('disabled');
6875             return;
6876         }
6877         
6878         this.el.removeClass('disabled');
6879         
6880         return;
6881     },
6882     
6883     setActive : function(state)
6884     {
6885         if(this.active == state){
6886             return;
6887         }
6888         
6889         this.active = state;
6890         
6891         if (state) {
6892             this.el.addClass('active');
6893             return;
6894         }
6895         
6896         this.el.removeClass('active');
6897         
6898         return;
6899     },
6900     
6901     isActive: function () 
6902     {
6903         return this.active;
6904     },
6905     
6906     setBadge : function(str)
6907     {
6908         if(!this.badgeEl){
6909             return;
6910         }
6911         
6912         this.badgeEl.dom.innerHTML = str;
6913     }
6914     
6915    
6916      
6917  
6918 });
6919  
6920
6921  /*
6922  * - LGPL
6923  *
6924  *  Breadcrumb Nav
6925  * 
6926  */
6927 Roo.namespace('Roo.bootstrap.breadcrumb');
6928
6929
6930 /**
6931  * @class Roo.bootstrap.breadcrumb.Nav
6932  * @extends Roo.bootstrap.Component
6933  * Bootstrap Breadcrumb Nav Class
6934  *  
6935  * @children Roo.bootstrap.breadcrumb.Item
6936  * 
6937  * @constructor
6938  * Create a new breadcrumb.Nav
6939  * @param {Object} config The config object
6940  */
6941
6942
6943 Roo.bootstrap.breadcrumb.Nav = function(config){
6944     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6945     
6946     
6947 };
6948
6949 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6950     
6951     getAutoCreate : function()
6952     {
6953
6954         var cfg = {
6955             tag: 'nav',
6956             cn : [
6957                 {
6958                     tag : 'ol',
6959                     cls : 'breadcrumb'
6960                 }
6961             ]
6962             
6963         };
6964           
6965         return cfg;
6966     },
6967     
6968     initEvents: function()
6969     {
6970         this.olEl = this.el.select('ol',true).first();    
6971     },
6972     getChildContainer : function()
6973     {
6974         return this.olEl;  
6975     }
6976     
6977 });
6978
6979  /*
6980  * - LGPL
6981  *
6982  *  Breadcrumb Item
6983  * 
6984  */
6985
6986
6987 /**
6988  * @class Roo.bootstrap.breadcrumb.Nav
6989  * @extends Roo.bootstrap.Component
6990  * Bootstrap Breadcrumb Nav Class
6991  *  
6992  * @children Roo.bootstrap.breadcrumb.Component
6993  * @cfg {String} html the content of the link.
6994  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6995  * @cfg {Boolean} active is it active
6996
6997  * 
6998  * @constructor
6999  * Create a new breadcrumb.Nav
7000  * @param {Object} config The config object
7001  */
7002
7003 Roo.bootstrap.breadcrumb.Item = function(config){
7004     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7005     this.addEvents({
7006         // img events
7007         /**
7008          * @event click
7009          * The img click event for the img.
7010          * @param {Roo.EventObject} e
7011          */
7012         "click" : true
7013     });
7014     
7015 };
7016
7017 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7018     
7019     href: false,
7020     html : '',
7021     
7022     getAutoCreate : function()
7023     {
7024
7025         var cfg = {
7026             tag: 'li',
7027             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7028         };
7029         if (this.href !== false) {
7030             cfg.cn = [{
7031                 tag : 'a',
7032                 href : this.href,
7033                 html : this.html
7034             }];
7035         } else {
7036             cfg.html = this.html;
7037         }
7038         
7039         return cfg;
7040     },
7041     
7042     initEvents: function()
7043     {
7044         if (this.href) {
7045             this.el.select('a', true).first().on('click',this.onClick, this)
7046         }
7047         
7048     },
7049     onClick : function(e)
7050     {
7051         e.preventDefault();
7052         this.fireEvent('click',this,  e);
7053     }
7054     
7055 });
7056
7057  /*
7058  * - LGPL
7059  *
7060  * row
7061  * 
7062  */
7063
7064 /**
7065  * @class Roo.bootstrap.Row
7066  * @extends Roo.bootstrap.Component
7067  * Bootstrap Row class (contains columns...)
7068  * 
7069  * @constructor
7070  * Create a new Row
7071  * @param {Object} config The config object
7072  */
7073
7074 Roo.bootstrap.Row = function(config){
7075     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7076 };
7077
7078 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7079     
7080     getAutoCreate : function(){
7081        return {
7082             cls: 'row clearfix'
7083        };
7084     }
7085     
7086     
7087 });
7088
7089  
7090
7091  /*
7092  * - LGPL
7093  *
7094  * pagination
7095  * 
7096  */
7097
7098 /**
7099  * @class Roo.bootstrap.Pagination
7100  * @extends Roo.bootstrap.Component
7101  * Bootstrap Pagination class
7102  * @cfg {String} size xs | sm | md | lg
7103  * @cfg {Boolean} inverse false | true
7104  * 
7105  * @constructor
7106  * Create a new Pagination
7107  * @param {Object} config The config object
7108  */
7109
7110 Roo.bootstrap.Pagination = function(config){
7111     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7112 };
7113
7114 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7115     
7116     cls: false,
7117     size: false,
7118     inverse: false,
7119     
7120     getAutoCreate : function(){
7121         var cfg = {
7122             tag: 'ul',
7123                 cls: 'pagination'
7124         };
7125         if (this.inverse) {
7126             cfg.cls += ' inverse';
7127         }
7128         if (this.html) {
7129             cfg.html=this.html;
7130         }
7131         if (this.cls) {
7132             cfg.cls += " " + this.cls;
7133         }
7134         return cfg;
7135     }
7136    
7137 });
7138
7139  
7140
7141  /*
7142  * - LGPL
7143  *
7144  * Pagination item
7145  * 
7146  */
7147
7148
7149 /**
7150  * @class Roo.bootstrap.PaginationItem
7151  * @extends Roo.bootstrap.Component
7152  * Bootstrap PaginationItem class
7153  * @cfg {String} html text
7154  * @cfg {String} href the link
7155  * @cfg {Boolean} preventDefault (true | false) default true
7156  * @cfg {Boolean} active (true | false) default false
7157  * @cfg {Boolean} disabled default false
7158  * 
7159  * 
7160  * @constructor
7161  * Create a new PaginationItem
7162  * @param {Object} config The config object
7163  */
7164
7165
7166 Roo.bootstrap.PaginationItem = function(config){
7167     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7168     this.addEvents({
7169         // raw events
7170         /**
7171          * @event click
7172          * The raw click event for the entire grid.
7173          * @param {Roo.EventObject} e
7174          */
7175         "click" : true
7176     });
7177 };
7178
7179 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7180     
7181     href : false,
7182     html : false,
7183     preventDefault: true,
7184     active : false,
7185     cls : false,
7186     disabled: false,
7187     
7188     getAutoCreate : function(){
7189         var cfg= {
7190             tag: 'li',
7191             cn: [
7192                 {
7193                     tag : 'a',
7194                     href : this.href ? this.href : '#',
7195                     html : this.html ? this.html : ''
7196                 }
7197             ]
7198         };
7199         
7200         if(this.cls){
7201             cfg.cls = this.cls;
7202         }
7203         
7204         if(this.disabled){
7205             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7206         }
7207         
7208         if(this.active){
7209             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7210         }
7211         
7212         return cfg;
7213     },
7214     
7215     initEvents: function() {
7216         
7217         this.el.on('click', this.onClick, this);
7218         
7219     },
7220     onClick : function(e)
7221     {
7222         Roo.log('PaginationItem on click ');
7223         if(this.preventDefault){
7224             e.preventDefault();
7225         }
7226         
7227         if(this.disabled){
7228             return;
7229         }
7230         
7231         this.fireEvent('click', this, e);
7232     }
7233    
7234 });
7235
7236  
7237
7238  /*
7239  * - LGPL
7240  *
7241  * slider
7242  * 
7243  */
7244
7245
7246 /**
7247  * @class Roo.bootstrap.Slider
7248  * @extends Roo.bootstrap.Component
7249  * Bootstrap Slider class
7250  *    
7251  * @constructor
7252  * Create a new Slider
7253  * @param {Object} config The config object
7254  */
7255
7256 Roo.bootstrap.Slider = function(config){
7257     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7258 };
7259
7260 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7261     
7262     getAutoCreate : function(){
7263         
7264         var cfg = {
7265             tag: 'div',
7266             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7267             cn: [
7268                 {
7269                     tag: 'a',
7270                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7271                 }
7272             ]
7273         };
7274         
7275         return cfg;
7276     }
7277    
7278 });
7279
7280  /*
7281  * Based on:
7282  * Ext JS Library 1.1.1
7283  * Copyright(c) 2006-2007, Ext JS, LLC.
7284  *
7285  * Originally Released Under LGPL - original licence link has changed is not relivant.
7286  *
7287  * Fork - LGPL
7288  * <script type="text/javascript">
7289  */
7290  
7291
7292 /**
7293  * @class Roo.grid.ColumnModel
7294  * @extends Roo.util.Observable
7295  * This is the default implementation of a ColumnModel used by the Grid. It defines
7296  * the columns in the grid.
7297  * <br>Usage:<br>
7298  <pre><code>
7299  var colModel = new Roo.grid.ColumnModel([
7300         {header: "Ticker", width: 60, sortable: true, locked: true},
7301         {header: "Company Name", width: 150, sortable: true},
7302         {header: "Market Cap.", width: 100, sortable: true},
7303         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7304         {header: "Employees", width: 100, sortable: true, resizable: false}
7305  ]);
7306  </code></pre>
7307  * <p>
7308  
7309  * The config options listed for this class are options which may appear in each
7310  * individual column definition.
7311  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7312  * @constructor
7313  * @param {Object} config An Array of column config objects. See this class's
7314  * config objects for details.
7315 */
7316 Roo.grid.ColumnModel = function(config){
7317         /**
7318      * The config passed into the constructor
7319      */
7320     this.config = []; //config;
7321     this.lookup = {};
7322
7323     // if no id, create one
7324     // if the column does not have a dataIndex mapping,
7325     // map it to the order it is in the config
7326     for(var i = 0, len = config.length; i < len; i++){
7327         this.addColumn(config[i]);
7328         
7329     }
7330
7331     /**
7332      * The width of columns which have no width specified (defaults to 100)
7333      * @type Number
7334      */
7335     this.defaultWidth = 100;
7336
7337     /**
7338      * Default sortable of columns which have no sortable specified (defaults to false)
7339      * @type Boolean
7340      */
7341     this.defaultSortable = false;
7342
7343     this.addEvents({
7344         /**
7345              * @event widthchange
7346              * Fires when the width of a column changes.
7347              * @param {ColumnModel} this
7348              * @param {Number} columnIndex The column index
7349              * @param {Number} newWidth The new width
7350              */
7351             "widthchange": true,
7352         /**
7353              * @event headerchange
7354              * Fires when the text of a header changes.
7355              * @param {ColumnModel} this
7356              * @param {Number} columnIndex The column index
7357              * @param {Number} newText The new header text
7358              */
7359             "headerchange": true,
7360         /**
7361              * @event hiddenchange
7362              * Fires when a column is hidden or "unhidden".
7363              * @param {ColumnModel} this
7364              * @param {Number} columnIndex The column index
7365              * @param {Boolean} hidden true if hidden, false otherwise
7366              */
7367             "hiddenchange": true,
7368             /**
7369          * @event columnmoved
7370          * Fires when a column is moved.
7371          * @param {ColumnModel} this
7372          * @param {Number} oldIndex
7373          * @param {Number} newIndex
7374          */
7375         "columnmoved" : true,
7376         /**
7377          * @event columlockchange
7378          * Fires when a column's locked state is changed
7379          * @param {ColumnModel} this
7380          * @param {Number} colIndex
7381          * @param {Boolean} locked true if locked
7382          */
7383         "columnlockchange" : true
7384     });
7385     Roo.grid.ColumnModel.superclass.constructor.call(this);
7386 };
7387 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7388     /**
7389      * @cfg {String} header The header text to display in the Grid view.
7390      */
7391     /**
7392      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7393      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7394      * specified, the column's index is used as an index into the Record's data Array.
7395      */
7396     /**
7397      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7398      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7399      */
7400     /**
7401      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7402      * Defaults to the value of the {@link #defaultSortable} property.
7403      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7404      */
7405     /**
7406      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7407      */
7408     /**
7409      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7410      */
7411     /**
7412      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7413      */
7414     /**
7415      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7416      */
7417     /**
7418      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7419      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7420      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7421      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7422      */
7423        /**
7424      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7425      */
7426     /**
7427      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7428      */
7429     /**
7430      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7431      */
7432     /**
7433      * @cfg {String} cursor (Optional)
7434      */
7435     /**
7436      * @cfg {String} tooltip (Optional)
7437      */
7438     /**
7439      * @cfg {Number} xs (Optional)
7440      */
7441     /**
7442      * @cfg {Number} sm (Optional)
7443      */
7444     /**
7445      * @cfg {Number} md (Optional)
7446      */
7447     /**
7448      * @cfg {Number} lg (Optional)
7449      */
7450     /**
7451      * Returns the id of the column at the specified index.
7452      * @param {Number} index The column index
7453      * @return {String} the id
7454      */
7455     getColumnId : function(index){
7456         return this.config[index].id;
7457     },
7458
7459     /**
7460      * Returns the column for a specified id.
7461      * @param {String} id The column id
7462      * @return {Object} the column
7463      */
7464     getColumnById : function(id){
7465         return this.lookup[id];
7466     },
7467
7468     
7469     /**
7470      * Returns the column Object for a specified dataIndex.
7471      * @param {String} dataIndex The column dataIndex
7472      * @return {Object|Boolean} the column or false if not found
7473      */
7474     getColumnByDataIndex: function(dataIndex){
7475         var index = this.findColumnIndex(dataIndex);
7476         return index > -1 ? this.config[index] : false;
7477     },
7478     
7479     /**
7480      * Returns the index for a specified column id.
7481      * @param {String} id The column id
7482      * @return {Number} the index, or -1 if not found
7483      */
7484     getIndexById : function(id){
7485         for(var i = 0, len = this.config.length; i < len; i++){
7486             if(this.config[i].id == id){
7487                 return i;
7488             }
7489         }
7490         return -1;
7491     },
7492     
7493     /**
7494      * Returns the index for a specified column dataIndex.
7495      * @param {String} dataIndex The column dataIndex
7496      * @return {Number} the index, or -1 if not found
7497      */
7498     
7499     findColumnIndex : function(dataIndex){
7500         for(var i = 0, len = this.config.length; i < len; i++){
7501             if(this.config[i].dataIndex == dataIndex){
7502                 return i;
7503             }
7504         }
7505         return -1;
7506     },
7507     
7508     
7509     moveColumn : function(oldIndex, newIndex){
7510         var c = this.config[oldIndex];
7511         this.config.splice(oldIndex, 1);
7512         this.config.splice(newIndex, 0, c);
7513         this.dataMap = null;
7514         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7515     },
7516
7517     isLocked : function(colIndex){
7518         return this.config[colIndex].locked === true;
7519     },
7520
7521     setLocked : function(colIndex, value, suppressEvent){
7522         if(this.isLocked(colIndex) == value){
7523             return;
7524         }
7525         this.config[colIndex].locked = value;
7526         if(!suppressEvent){
7527             this.fireEvent("columnlockchange", this, colIndex, value);
7528         }
7529     },
7530
7531     getTotalLockedWidth : function(){
7532         var totalWidth = 0;
7533         for(var i = 0; i < this.config.length; i++){
7534             if(this.isLocked(i) && !this.isHidden(i)){
7535                 this.totalWidth += this.getColumnWidth(i);
7536             }
7537         }
7538         return totalWidth;
7539     },
7540
7541     getLockedCount : function(){
7542         for(var i = 0, len = this.config.length; i < len; i++){
7543             if(!this.isLocked(i)){
7544                 return i;
7545             }
7546         }
7547         
7548         return this.config.length;
7549     },
7550
7551     /**
7552      * Returns the number of columns.
7553      * @return {Number}
7554      */
7555     getColumnCount : function(visibleOnly){
7556         if(visibleOnly === true){
7557             var c = 0;
7558             for(var i = 0, len = this.config.length; i < len; i++){
7559                 if(!this.isHidden(i)){
7560                     c++;
7561                 }
7562             }
7563             return c;
7564         }
7565         return this.config.length;
7566     },
7567
7568     /**
7569      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7570      * @param {Function} fn
7571      * @param {Object} scope (optional)
7572      * @return {Array} result
7573      */
7574     getColumnsBy : function(fn, scope){
7575         var r = [];
7576         for(var i = 0, len = this.config.length; i < len; i++){
7577             var c = this.config[i];
7578             if(fn.call(scope||this, c, i) === true){
7579                 r[r.length] = c;
7580             }
7581         }
7582         return r;
7583     },
7584
7585     /**
7586      * Returns true if the specified column is sortable.
7587      * @param {Number} col The column index
7588      * @return {Boolean}
7589      */
7590     isSortable : function(col){
7591         if(typeof this.config[col].sortable == "undefined"){
7592             return this.defaultSortable;
7593         }
7594         return this.config[col].sortable;
7595     },
7596
7597     /**
7598      * Returns the rendering (formatting) function defined for the column.
7599      * @param {Number} col The column index.
7600      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7601      */
7602     getRenderer : function(col){
7603         if(!this.config[col].renderer){
7604             return Roo.grid.ColumnModel.defaultRenderer;
7605         }
7606         return this.config[col].renderer;
7607     },
7608
7609     /**
7610      * Sets the rendering (formatting) function for a column.
7611      * @param {Number} col The column index
7612      * @param {Function} fn The function to use to process the cell's raw data
7613      * to return HTML markup for the grid view. The render function is called with
7614      * the following parameters:<ul>
7615      * <li>Data value.</li>
7616      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7617      * <li>css A CSS style string to apply to the table cell.</li>
7618      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7619      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7620      * <li>Row index</li>
7621      * <li>Column index</li>
7622      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7623      */
7624     setRenderer : function(col, fn){
7625         this.config[col].renderer = fn;
7626     },
7627
7628     /**
7629      * Returns the width for the specified column.
7630      * @param {Number} col The column index
7631      * @return {Number}
7632      */
7633     getColumnWidth : function(col){
7634         return this.config[col].width * 1 || this.defaultWidth;
7635     },
7636
7637     /**
7638      * Sets the width for a column.
7639      * @param {Number} col The column index
7640      * @param {Number} width The new width
7641      */
7642     setColumnWidth : function(col, width, suppressEvent){
7643         this.config[col].width = width;
7644         this.totalWidth = null;
7645         if(!suppressEvent){
7646              this.fireEvent("widthchange", this, col, width);
7647         }
7648     },
7649
7650     /**
7651      * Returns the total width of all columns.
7652      * @param {Boolean} includeHidden True to include hidden column widths
7653      * @return {Number}
7654      */
7655     getTotalWidth : function(includeHidden){
7656         if(!this.totalWidth){
7657             this.totalWidth = 0;
7658             for(var i = 0, len = this.config.length; i < len; i++){
7659                 if(includeHidden || !this.isHidden(i)){
7660                     this.totalWidth += this.getColumnWidth(i);
7661                 }
7662             }
7663         }
7664         return this.totalWidth;
7665     },
7666
7667     /**
7668      * Returns the header for the specified column.
7669      * @param {Number} col The column index
7670      * @return {String}
7671      */
7672     getColumnHeader : function(col){
7673         return this.config[col].header;
7674     },
7675
7676     /**
7677      * Sets the header for a column.
7678      * @param {Number} col The column index
7679      * @param {String} header The new header
7680      */
7681     setColumnHeader : function(col, header){
7682         this.config[col].header = header;
7683         this.fireEvent("headerchange", this, col, header);
7684     },
7685
7686     /**
7687      * Returns the tooltip for the specified column.
7688      * @param {Number} col The column index
7689      * @return {String}
7690      */
7691     getColumnTooltip : function(col){
7692             return this.config[col].tooltip;
7693     },
7694     /**
7695      * Sets the tooltip for a column.
7696      * @param {Number} col The column index
7697      * @param {String} tooltip The new tooltip
7698      */
7699     setColumnTooltip : function(col, tooltip){
7700             this.config[col].tooltip = tooltip;
7701     },
7702
7703     /**
7704      * Returns the dataIndex for the specified column.
7705      * @param {Number} col The column index
7706      * @return {Number}
7707      */
7708     getDataIndex : function(col){
7709         return this.config[col].dataIndex;
7710     },
7711
7712     /**
7713      * Sets the dataIndex for a column.
7714      * @param {Number} col The column index
7715      * @param {Number} dataIndex The new dataIndex
7716      */
7717     setDataIndex : function(col, dataIndex){
7718         this.config[col].dataIndex = dataIndex;
7719     },
7720
7721     
7722     
7723     /**
7724      * Returns true if the cell is editable.
7725      * @param {Number} colIndex The column index
7726      * @param {Number} rowIndex The row index - this is nto actually used..?
7727      * @return {Boolean}
7728      */
7729     isCellEditable : function(colIndex, rowIndex){
7730         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7731     },
7732
7733     /**
7734      * Returns the editor defined for the cell/column.
7735      * return false or null to disable editing.
7736      * @param {Number} colIndex The column index
7737      * @param {Number} rowIndex The row index
7738      * @return {Object}
7739      */
7740     getCellEditor : function(colIndex, rowIndex){
7741         return this.config[colIndex].editor;
7742     },
7743
7744     /**
7745      * Sets if a column is editable.
7746      * @param {Number} col The column index
7747      * @param {Boolean} editable True if the column is editable
7748      */
7749     setEditable : function(col, editable){
7750         this.config[col].editable = editable;
7751     },
7752
7753
7754     /**
7755      * Returns true if the column is hidden.
7756      * @param {Number} colIndex The column index
7757      * @return {Boolean}
7758      */
7759     isHidden : function(colIndex){
7760         return this.config[colIndex].hidden;
7761     },
7762
7763
7764     /**
7765      * Returns true if the column width cannot be changed
7766      */
7767     isFixed : function(colIndex){
7768         return this.config[colIndex].fixed;
7769     },
7770
7771     /**
7772      * Returns true if the column can be resized
7773      * @return {Boolean}
7774      */
7775     isResizable : function(colIndex){
7776         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7777     },
7778     /**
7779      * Sets if a column is hidden.
7780      * @param {Number} colIndex The column index
7781      * @param {Boolean} hidden True if the column is hidden
7782      */
7783     setHidden : function(colIndex, hidden){
7784         this.config[colIndex].hidden = hidden;
7785         this.totalWidth = null;
7786         this.fireEvent("hiddenchange", this, colIndex, hidden);
7787     },
7788
7789     /**
7790      * Sets the editor for a column.
7791      * @param {Number} col The column index
7792      * @param {Object} editor The editor object
7793      */
7794     setEditor : function(col, editor){
7795         this.config[col].editor = editor;
7796     },
7797     /**
7798      * Add a column (experimental...) - defaults to adding to the end..
7799      * @param {Object} config 
7800     */
7801     addColumn : function(c)
7802     {
7803     
7804         var i = this.config.length;
7805         this.config[i] = c;
7806         
7807         if(typeof c.dataIndex == "undefined"){
7808             c.dataIndex = i;
7809         }
7810         if(typeof c.renderer == "string"){
7811             c.renderer = Roo.util.Format[c.renderer];
7812         }
7813         if(typeof c.id == "undefined"){
7814             c.id = Roo.id();
7815         }
7816         if(c.editor && c.editor.xtype){
7817             c.editor  = Roo.factory(c.editor, Roo.grid);
7818         }
7819         if(c.editor && c.editor.isFormField){
7820             c.editor = new Roo.grid.GridEditor(c.editor);
7821         }
7822         this.lookup[c.id] = c;
7823     }
7824     
7825 });
7826
7827 Roo.grid.ColumnModel.defaultRenderer = function(value)
7828 {
7829     if(typeof value == "object") {
7830         return value;
7831     }
7832         if(typeof value == "string" && value.length < 1){
7833             return "&#160;";
7834         }
7835     
7836         return String.format("{0}", value);
7837 };
7838
7839 // Alias for backwards compatibility
7840 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7841 /*
7842  * Based on:
7843  * Ext JS Library 1.1.1
7844  * Copyright(c) 2006-2007, Ext JS, LLC.
7845  *
7846  * Originally Released Under LGPL - original licence link has changed is not relivant.
7847  *
7848  * Fork - LGPL
7849  * <script type="text/javascript">
7850  */
7851  
7852 /**
7853  * @class Roo.LoadMask
7854  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7855  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7856  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7857  * element's UpdateManager load indicator and will be destroyed after the initial load.
7858  * @constructor
7859  * Create a new LoadMask
7860  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7861  * @param {Object} config The config object
7862  */
7863 Roo.LoadMask = function(el, config){
7864     this.el = Roo.get(el);
7865     Roo.apply(this, config);
7866     if(this.store){
7867         this.store.on('beforeload', this.onBeforeLoad, this);
7868         this.store.on('load', this.onLoad, this);
7869         this.store.on('loadexception', this.onLoadException, this);
7870         this.removeMask = false;
7871     }else{
7872         var um = this.el.getUpdateManager();
7873         um.showLoadIndicator = false; // disable the default indicator
7874         um.on('beforeupdate', this.onBeforeLoad, this);
7875         um.on('update', this.onLoad, this);
7876         um.on('failure', this.onLoad, this);
7877         this.removeMask = true;
7878     }
7879 };
7880
7881 Roo.LoadMask.prototype = {
7882     /**
7883      * @cfg {Boolean} removeMask
7884      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7885      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7886      */
7887     /**
7888      * @cfg {String} msg
7889      * The text to display in a centered loading message box (defaults to 'Loading...')
7890      */
7891     msg : 'Loading...',
7892     /**
7893      * @cfg {String} msgCls
7894      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7895      */
7896     msgCls : 'x-mask-loading',
7897
7898     /**
7899      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7900      * @type Boolean
7901      */
7902     disabled: false,
7903
7904     /**
7905      * Disables the mask to prevent it from being displayed
7906      */
7907     disable : function(){
7908        this.disabled = true;
7909     },
7910
7911     /**
7912      * Enables the mask so that it can be displayed
7913      */
7914     enable : function(){
7915         this.disabled = false;
7916     },
7917     
7918     onLoadException : function()
7919     {
7920         Roo.log(arguments);
7921         
7922         if (typeof(arguments[3]) != 'undefined') {
7923             Roo.MessageBox.alert("Error loading",arguments[3]);
7924         } 
7925         /*
7926         try {
7927             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7928                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7929             }   
7930         } catch(e) {
7931             
7932         }
7933         */
7934     
7935         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7936     },
7937     // private
7938     onLoad : function()
7939     {
7940         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7941     },
7942
7943     // private
7944     onBeforeLoad : function(){
7945         if(!this.disabled){
7946             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7947         }
7948     },
7949
7950     // private
7951     destroy : function(){
7952         if(this.store){
7953             this.store.un('beforeload', this.onBeforeLoad, this);
7954             this.store.un('load', this.onLoad, this);
7955             this.store.un('loadexception', this.onLoadException, this);
7956         }else{
7957             var um = this.el.getUpdateManager();
7958             um.un('beforeupdate', this.onBeforeLoad, this);
7959             um.un('update', this.onLoad, this);
7960             um.un('failure', this.onLoad, this);
7961         }
7962     }
7963 };/*
7964  * - LGPL
7965  *
7966  * table
7967  * 
7968  */
7969
7970 /**
7971  * @class Roo.bootstrap.Table
7972  * @extends Roo.bootstrap.Component
7973  * Bootstrap Table class
7974  * @cfg {String} cls table class
7975  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7976  * @cfg {String} bgcolor Specifies the background color for a table
7977  * @cfg {Number} border Specifies whether the table cells should have borders or not
7978  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7979  * @cfg {Number} cellspacing Specifies the space between cells
7980  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7981  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7982  * @cfg {String} sortable Specifies that the table should be sortable
7983  * @cfg {String} summary Specifies a summary of the content of a table
7984  * @cfg {Number} width Specifies the width of a table
7985  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7986  * 
7987  * @cfg {boolean} striped Should the rows be alternative striped
7988  * @cfg {boolean} bordered Add borders to the table
7989  * @cfg {boolean} hover Add hover highlighting
7990  * @cfg {boolean} condensed Format condensed
7991  * @cfg {boolean} responsive Format condensed
7992  * @cfg {Boolean} loadMask (true|false) default false
7993  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7994  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7995  * @cfg {Boolean} rowSelection (true|false) default false
7996  * @cfg {Boolean} cellSelection (true|false) default false
7997  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7998  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7999  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8000  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8001  
8002  * 
8003  * @constructor
8004  * Create a new Table
8005  * @param {Object} config The config object
8006  */
8007
8008 Roo.bootstrap.Table = function(config){
8009     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8010     
8011   
8012     
8013     // BC...
8014     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8015     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8016     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8017     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8018     
8019     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8020     if (this.sm) {
8021         this.sm.grid = this;
8022         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8023         this.sm = this.selModel;
8024         this.sm.xmodule = this.xmodule || false;
8025     }
8026     
8027     if (this.cm && typeof(this.cm.config) == 'undefined') {
8028         this.colModel = new Roo.grid.ColumnModel(this.cm);
8029         this.cm = this.colModel;
8030         this.cm.xmodule = this.xmodule || false;
8031     }
8032     if (this.store) {
8033         this.store= Roo.factory(this.store, Roo.data);
8034         this.ds = this.store;
8035         this.ds.xmodule = this.xmodule || false;
8036          
8037     }
8038     if (this.footer && this.store) {
8039         this.footer.dataSource = this.ds;
8040         this.footer = Roo.factory(this.footer);
8041     }
8042     
8043     /** @private */
8044     this.addEvents({
8045         /**
8046          * @event cellclick
8047          * Fires when a cell is clicked
8048          * @param {Roo.bootstrap.Table} this
8049          * @param {Roo.Element} el
8050          * @param {Number} rowIndex
8051          * @param {Number} columnIndex
8052          * @param {Roo.EventObject} e
8053          */
8054         "cellclick" : true,
8055         /**
8056          * @event celldblclick
8057          * Fires when a cell is double clicked
8058          * @param {Roo.bootstrap.Table} this
8059          * @param {Roo.Element} el
8060          * @param {Number} rowIndex
8061          * @param {Number} columnIndex
8062          * @param {Roo.EventObject} e
8063          */
8064         "celldblclick" : true,
8065         /**
8066          * @event rowclick
8067          * Fires when a row is clicked
8068          * @param {Roo.bootstrap.Table} this
8069          * @param {Roo.Element} el
8070          * @param {Number} rowIndex
8071          * @param {Roo.EventObject} e
8072          */
8073         "rowclick" : true,
8074         /**
8075          * @event rowdblclick
8076          * Fires when a row is double clicked
8077          * @param {Roo.bootstrap.Table} this
8078          * @param {Roo.Element} el
8079          * @param {Number} rowIndex
8080          * @param {Roo.EventObject} e
8081          */
8082         "rowdblclick" : true,
8083         /**
8084          * @event mouseover
8085          * Fires when a mouseover occur
8086          * @param {Roo.bootstrap.Table} this
8087          * @param {Roo.Element} el
8088          * @param {Number} rowIndex
8089          * @param {Number} columnIndex
8090          * @param {Roo.EventObject} e
8091          */
8092         "mouseover" : true,
8093         /**
8094          * @event mouseout
8095          * Fires when a mouseout occur
8096          * @param {Roo.bootstrap.Table} this
8097          * @param {Roo.Element} el
8098          * @param {Number} rowIndex
8099          * @param {Number} columnIndex
8100          * @param {Roo.EventObject} e
8101          */
8102         "mouseout" : true,
8103         /**
8104          * @event rowclass
8105          * Fires when a row is rendered, so you can change add a style to it.
8106          * @param {Roo.bootstrap.Table} this
8107          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8108          */
8109         'rowclass' : true,
8110           /**
8111          * @event rowsrendered
8112          * Fires when all the  rows have been rendered
8113          * @param {Roo.bootstrap.Table} this
8114          */
8115         'rowsrendered' : true,
8116         /**
8117          * @event contextmenu
8118          * The raw contextmenu event for the entire grid.
8119          * @param {Roo.EventObject} e
8120          */
8121         "contextmenu" : true,
8122         /**
8123          * @event rowcontextmenu
8124          * Fires when a row is right clicked
8125          * @param {Roo.bootstrap.Table} this
8126          * @param {Number} rowIndex
8127          * @param {Roo.EventObject} e
8128          */
8129         "rowcontextmenu" : true,
8130         /**
8131          * @event cellcontextmenu
8132          * Fires when a cell is right clicked
8133          * @param {Roo.bootstrap.Table} this
8134          * @param {Number} rowIndex
8135          * @param {Number} cellIndex
8136          * @param {Roo.EventObject} e
8137          */
8138          "cellcontextmenu" : true,
8139          /**
8140          * @event headercontextmenu
8141          * Fires when a header is right clicked
8142          * @param {Roo.bootstrap.Table} this
8143          * @param {Number} columnIndex
8144          * @param {Roo.EventObject} e
8145          */
8146         "headercontextmenu" : true
8147     });
8148 };
8149
8150 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8151     
8152     cls: false,
8153     align: false,
8154     bgcolor: false,
8155     border: false,
8156     cellpadding: false,
8157     cellspacing: false,
8158     frame: false,
8159     rules: false,
8160     sortable: false,
8161     summary: false,
8162     width: false,
8163     striped : false,
8164     scrollBody : false,
8165     bordered: false,
8166     hover:  false,
8167     condensed : false,
8168     responsive : false,
8169     sm : false,
8170     cm : false,
8171     store : false,
8172     loadMask : false,
8173     footerShow : true,
8174     headerShow : true,
8175   
8176     rowSelection : false,
8177     cellSelection : false,
8178     layout : false,
8179     
8180     // Roo.Element - the tbody
8181     mainBody: false,
8182     // Roo.Element - thead element
8183     mainHead: false,
8184     
8185     container: false, // used by gridpanel...
8186     
8187     lazyLoad : false,
8188     
8189     CSS : Roo.util.CSS,
8190     
8191     auto_hide_footer : false,
8192     
8193     getAutoCreate : function()
8194     {
8195         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8196         
8197         cfg = {
8198             tag: 'table',
8199             cls : 'table',
8200             cn : []
8201         };
8202         if (this.scrollBody) {
8203             cfg.cls += ' table-body-fixed';
8204         }    
8205         if (this.striped) {
8206             cfg.cls += ' table-striped';
8207         }
8208         
8209         if (this.hover) {
8210             cfg.cls += ' table-hover';
8211         }
8212         if (this.bordered) {
8213             cfg.cls += ' table-bordered';
8214         }
8215         if (this.condensed) {
8216             cfg.cls += ' table-condensed';
8217         }
8218         if (this.responsive) {
8219             cfg.cls += ' table-responsive';
8220         }
8221         
8222         if (this.cls) {
8223             cfg.cls+=  ' ' +this.cls;
8224         }
8225         
8226         // this lot should be simplifed...
8227         var _t = this;
8228         var cp = [
8229             'align',
8230             'bgcolor',
8231             'border',
8232             'cellpadding',
8233             'cellspacing',
8234             'frame',
8235             'rules',
8236             'sortable',
8237             'summary',
8238             'width'
8239         ].forEach(function(k) {
8240             if (_t[k]) {
8241                 cfg[k] = _t[k];
8242             }
8243         });
8244         
8245         
8246         if (this.layout) {
8247             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8248         }
8249         
8250         if(this.store || this.cm){
8251             if(this.headerShow){
8252                 cfg.cn.push(this.renderHeader());
8253             }
8254             
8255             cfg.cn.push(this.renderBody());
8256             
8257             if(this.footerShow){
8258                 cfg.cn.push(this.renderFooter());
8259             }
8260             // where does this come from?
8261             //cfg.cls+=  ' TableGrid';
8262         }
8263         
8264         return { cn : [ cfg ] };
8265     },
8266     
8267     initEvents : function()
8268     {   
8269         if(!this.store || !this.cm){
8270             return;
8271         }
8272         if (this.selModel) {
8273             this.selModel.initEvents();
8274         }
8275         
8276         
8277         //Roo.log('initEvents with ds!!!!');
8278         
8279         this.mainBody = this.el.select('tbody', true).first();
8280         this.mainHead = this.el.select('thead', true).first();
8281         this.mainFoot = this.el.select('tfoot', true).first();
8282         
8283         
8284         
8285         
8286         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8287             e.on('click', this.sort, this);
8288         }, this);
8289         
8290         this.mainBody.on("click", this.onClick, this);
8291         this.mainBody.on("dblclick", this.onDblClick, this);
8292         
8293         // why is this done????? = it breaks dialogs??
8294         //this.parent().el.setStyle('position', 'relative');
8295         
8296         
8297         if (this.footer) {
8298             this.footer.parentId = this.id;
8299             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8300             
8301             if(this.lazyLoad){
8302                 this.el.select('tfoot tr td').first().addClass('hide');
8303             }
8304         } 
8305         
8306         if(this.loadMask) {
8307             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8308         }
8309         
8310         this.store.on('load', this.onLoad, this);
8311         this.store.on('beforeload', this.onBeforeLoad, this);
8312         this.store.on('update', this.onUpdate, this);
8313         this.store.on('add', this.onAdd, this);
8314         this.store.on("clear", this.clear, this);
8315         
8316         this.el.on("contextmenu", this.onContextMenu, this);
8317         
8318         this.mainBody.on('scroll', this.onBodyScroll, this);
8319         
8320         this.cm.on("headerchange", this.onHeaderChange, this);
8321         
8322         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8323         
8324     },
8325     
8326     onContextMenu : function(e, t)
8327     {
8328         this.processEvent("contextmenu", e);
8329     },
8330     
8331     processEvent : function(name, e)
8332     {
8333         if (name != 'touchstart' ) {
8334             this.fireEvent(name, e);    
8335         }
8336         
8337         var t = e.getTarget();
8338         
8339         var cell = Roo.get(t);
8340         
8341         if(!cell){
8342             return;
8343         }
8344         
8345         if(cell.findParent('tfoot', false, true)){
8346             return;
8347         }
8348         
8349         if(cell.findParent('thead', false, true)){
8350             
8351             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8352                 cell = Roo.get(t).findParent('th', false, true);
8353                 if (!cell) {
8354                     Roo.log("failed to find th in thead?");
8355                     Roo.log(e.getTarget());
8356                     return;
8357                 }
8358             }
8359             
8360             var cellIndex = cell.dom.cellIndex;
8361             
8362             var ename = name == 'touchstart' ? 'click' : name;
8363             this.fireEvent("header" + ename, this, cellIndex, e);
8364             
8365             return;
8366         }
8367         
8368         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8369             cell = Roo.get(t).findParent('td', false, true);
8370             if (!cell) {
8371                 Roo.log("failed to find th in tbody?");
8372                 Roo.log(e.getTarget());
8373                 return;
8374             }
8375         }
8376         
8377         var row = cell.findParent('tr', false, true);
8378         var cellIndex = cell.dom.cellIndex;
8379         var rowIndex = row.dom.rowIndex - 1;
8380         
8381         if(row !== false){
8382             
8383             this.fireEvent("row" + name, this, rowIndex, e);
8384             
8385             if(cell !== false){
8386             
8387                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8388             }
8389         }
8390         
8391     },
8392     
8393     onMouseover : function(e, el)
8394     {
8395         var cell = Roo.get(el);
8396         
8397         if(!cell){
8398             return;
8399         }
8400         
8401         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8402             cell = cell.findParent('td', false, true);
8403         }
8404         
8405         var row = cell.findParent('tr', false, true);
8406         var cellIndex = cell.dom.cellIndex;
8407         var rowIndex = row.dom.rowIndex - 1; // start from 0
8408         
8409         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8410         
8411     },
8412     
8413     onMouseout : function(e, el)
8414     {
8415         var cell = Roo.get(el);
8416         
8417         if(!cell){
8418             return;
8419         }
8420         
8421         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8422             cell = cell.findParent('td', false, true);
8423         }
8424         
8425         var row = cell.findParent('tr', false, true);
8426         var cellIndex = cell.dom.cellIndex;
8427         var rowIndex = row.dom.rowIndex - 1; // start from 0
8428         
8429         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8430         
8431     },
8432     
8433     onClick : function(e, el)
8434     {
8435         var cell = Roo.get(el);
8436         
8437         if(!cell || (!this.cellSelection && !this.rowSelection)){
8438             return;
8439         }
8440         
8441         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8442             cell = cell.findParent('td', false, true);
8443         }
8444         
8445         if(!cell || typeof(cell) == 'undefined'){
8446             return;
8447         }
8448         
8449         var row = cell.findParent('tr', false, true);
8450         
8451         if(!row || typeof(row) == 'undefined'){
8452             return;
8453         }
8454         
8455         var cellIndex = cell.dom.cellIndex;
8456         var rowIndex = this.getRowIndex(row);
8457         
8458         // why??? - should these not be based on SelectionModel?
8459         //if(this.cellSelection){
8460             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8461         //}
8462         
8463         //if(this.rowSelection){
8464             this.fireEvent('rowclick', this, row, rowIndex, e);
8465         //}
8466          
8467     },
8468         
8469     onDblClick : function(e,el)
8470     {
8471         var cell = Roo.get(el);
8472         
8473         if(!cell || (!this.cellSelection && !this.rowSelection)){
8474             return;
8475         }
8476         
8477         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8478             cell = cell.findParent('td', false, true);
8479         }
8480         
8481         if(!cell || typeof(cell) == 'undefined'){
8482             return;
8483         }
8484         
8485         var row = cell.findParent('tr', false, true);
8486         
8487         if(!row || typeof(row) == 'undefined'){
8488             return;
8489         }
8490         
8491         var cellIndex = cell.dom.cellIndex;
8492         var rowIndex = this.getRowIndex(row);
8493         
8494         if(this.cellSelection){
8495             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8496         }
8497         
8498         if(this.rowSelection){
8499             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8500         }
8501     },
8502     
8503     sort : function(e,el)
8504     {
8505         var col = Roo.get(el);
8506         
8507         if(!col.hasClass('sortable')){
8508             return;
8509         }
8510         
8511         var sort = col.attr('sort');
8512         var dir = 'ASC';
8513         
8514         if(col.select('i', true).first().hasClass('fa-arrow-up')){
8515             dir = 'DESC';
8516         }
8517         
8518         this.store.sortInfo = {field : sort, direction : dir};
8519         
8520         if (this.footer) {
8521             Roo.log("calling footer first");
8522             this.footer.onClick('first');
8523         } else {
8524         
8525             this.store.load({ params : { start : 0 } });
8526         }
8527     },
8528     
8529     renderHeader : function()
8530     {
8531         var header = {
8532             tag: 'thead',
8533             cn : []
8534         };
8535         
8536         var cm = this.cm;
8537         this.totalWidth = 0;
8538         
8539         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8540             
8541             var config = cm.config[i];
8542             
8543             var c = {
8544                 tag: 'th',
8545                 cls : 'x-hcol-' + i,
8546                 style : '',
8547                 
8548                 html: cm.getColumnHeader(i)
8549             };
8550             
8551             var tooltip = cm.getColumnTooltip(i);
8552             if (tooltip) {
8553                 c.tooltip = tooltip;
8554             }
8555             
8556             
8557             var hh = '';
8558             
8559             if(typeof(config.sortable) != 'undefined' && config.sortable){
8560                 c.cls = 'sortable';
8561                 c.html = '<i class="fa"></i>' + c.html;
8562             }
8563             
8564             // could use BS4 hidden-..-down 
8565             
8566             if(typeof(config.lgHeader) != 'undefined'){
8567                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8568             }
8569             
8570             if(typeof(config.mdHeader) != 'undefined'){
8571                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8572             }
8573             
8574             if(typeof(config.smHeader) != 'undefined'){
8575                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8576             }
8577             
8578             if(typeof(config.xsHeader) != 'undefined'){
8579                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8580             }
8581             
8582             if(hh.length){
8583                 c.html = hh;
8584             }
8585             
8586             if(typeof(config.tooltip) != 'undefined'){
8587                 c.tooltip = config.tooltip;
8588             }
8589             
8590             if(typeof(config.colspan) != 'undefined'){
8591                 c.colspan = config.colspan;
8592             }
8593             
8594             if(typeof(config.hidden) != 'undefined' && config.hidden){
8595                 c.style += ' display:none;';
8596             }
8597             
8598             if(typeof(config.dataIndex) != 'undefined'){
8599                 c.sort = config.dataIndex;
8600             }
8601             
8602            
8603             
8604             if(typeof(config.align) != 'undefined' && config.align.length){
8605                 c.style += ' text-align:' + config.align + ';';
8606             }
8607             
8608             if(typeof(config.width) != 'undefined'){
8609                 c.style += ' width:' + config.width + 'px;';
8610                 this.totalWidth += config.width;
8611             } else {
8612                 this.totalWidth += 100; // assume minimum of 100 per column?
8613             }
8614             
8615             if(typeof(config.cls) != 'undefined'){
8616                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8617             }
8618             
8619             ['xs','sm','md','lg'].map(function(size){
8620                 
8621                 if(typeof(config[size]) == 'undefined'){
8622                     return;
8623                 }
8624                  
8625                 if (!config[size]) { // 0 = hidden
8626                     // BS 4 '0' is treated as hide that column and below.
8627                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8628                     return;
8629                 }
8630                 
8631                 c.cls += ' col-' + size + '-' + config[size] + (
8632                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8633                 );
8634                 
8635                 
8636             });
8637             
8638             header.cn.push(c)
8639         }
8640         
8641         return header;
8642     },
8643     
8644     renderBody : function()
8645     {
8646         var body = {
8647             tag: 'tbody',
8648             cn : [
8649                 {
8650                     tag: 'tr',
8651                     cn : [
8652                         {
8653                             tag : 'td',
8654                             colspan :  this.cm.getColumnCount()
8655                         }
8656                     ]
8657                 }
8658             ]
8659         };
8660         
8661         return body;
8662     },
8663     
8664     renderFooter : function()
8665     {
8666         var footer = {
8667             tag: 'tfoot',
8668             cn : [
8669                 {
8670                     tag: 'tr',
8671                     cn : [
8672                         {
8673                             tag : 'td',
8674                             colspan :  this.cm.getColumnCount()
8675                         }
8676                     ]
8677                 }
8678             ]
8679         };
8680         
8681         return footer;
8682     },
8683     
8684     
8685     
8686     onLoad : function()
8687     {
8688 //        Roo.log('ds onload');
8689         this.clear();
8690         
8691         var _this = this;
8692         var cm = this.cm;
8693         var ds = this.store;
8694         
8695         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8696             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8697             if (_this.store.sortInfo) {
8698                     
8699                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8700                     e.select('i', true).addClass(['fa-arrow-up']);
8701                 }
8702                 
8703                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8704                     e.select('i', true).addClass(['fa-arrow-down']);
8705                 }
8706             }
8707         });
8708         
8709         var tbody =  this.mainBody;
8710               
8711         if(ds.getCount() > 0){
8712             ds.data.each(function(d,rowIndex){
8713                 var row =  this.renderRow(cm, ds, rowIndex);
8714                 
8715                 tbody.createChild(row);
8716                 
8717                 var _this = this;
8718                 
8719                 if(row.cellObjects.length){
8720                     Roo.each(row.cellObjects, function(r){
8721                         _this.renderCellObject(r);
8722                     })
8723                 }
8724                 
8725             }, this);
8726         }
8727         
8728         var tfoot = this.el.select('tfoot', true).first();
8729         
8730         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8731             
8732             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8733             
8734             var total = this.ds.getTotalCount();
8735             
8736             if(this.footer.pageSize < total){
8737                 this.mainFoot.show();
8738             }
8739         }
8740         
8741         Roo.each(this.el.select('tbody td', true).elements, function(e){
8742             e.on('mouseover', _this.onMouseover, _this);
8743         });
8744         
8745         Roo.each(this.el.select('tbody td', true).elements, function(e){
8746             e.on('mouseout', _this.onMouseout, _this);
8747         });
8748         this.fireEvent('rowsrendered', this);
8749         
8750         this.autoSize();
8751     },
8752     
8753     
8754     onUpdate : function(ds,record)
8755     {
8756         this.refreshRow(record);
8757         this.autoSize();
8758     },
8759     
8760     onRemove : function(ds, record, index, isUpdate){
8761         if(isUpdate !== true){
8762             this.fireEvent("beforerowremoved", this, index, record);
8763         }
8764         var bt = this.mainBody.dom;
8765         
8766         var rows = this.el.select('tbody > tr', true).elements;
8767         
8768         if(typeof(rows[index]) != 'undefined'){
8769             bt.removeChild(rows[index].dom);
8770         }
8771         
8772 //        if(bt.rows[index]){
8773 //            bt.removeChild(bt.rows[index]);
8774 //        }
8775         
8776         if(isUpdate !== true){
8777             //this.stripeRows(index);
8778             //this.syncRowHeights(index, index);
8779             //this.layout();
8780             this.fireEvent("rowremoved", this, index, record);
8781         }
8782     },
8783     
8784     onAdd : function(ds, records, rowIndex)
8785     {
8786         //Roo.log('on Add called');
8787         // - note this does not handle multiple adding very well..
8788         var bt = this.mainBody.dom;
8789         for (var i =0 ; i < records.length;i++) {
8790             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8791             //Roo.log(records[i]);
8792             //Roo.log(this.store.getAt(rowIndex+i));
8793             this.insertRow(this.store, rowIndex + i, false);
8794             return;
8795         }
8796         
8797     },
8798     
8799     
8800     refreshRow : function(record){
8801         var ds = this.store, index;
8802         if(typeof record == 'number'){
8803             index = record;
8804             record = ds.getAt(index);
8805         }else{
8806             index = ds.indexOf(record);
8807             if (index < 0) {
8808                 return; // should not happen - but seems to 
8809             }
8810         }
8811         this.insertRow(ds, index, true);
8812         this.autoSize();
8813         this.onRemove(ds, record, index+1, true);
8814         this.autoSize();
8815         //this.syncRowHeights(index, index);
8816         //this.layout();
8817         this.fireEvent("rowupdated", this, index, record);
8818     },
8819     
8820     insertRow : function(dm, rowIndex, isUpdate){
8821         
8822         if(!isUpdate){
8823             this.fireEvent("beforerowsinserted", this, rowIndex);
8824         }
8825             //var s = this.getScrollState();
8826         var row = this.renderRow(this.cm, this.store, rowIndex);
8827         // insert before rowIndex..
8828         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8829         
8830         var _this = this;
8831                 
8832         if(row.cellObjects.length){
8833             Roo.each(row.cellObjects, function(r){
8834                 _this.renderCellObject(r);
8835             })
8836         }
8837             
8838         if(!isUpdate){
8839             this.fireEvent("rowsinserted", this, rowIndex);
8840             //this.syncRowHeights(firstRow, lastRow);
8841             //this.stripeRows(firstRow);
8842             //this.layout();
8843         }
8844         
8845     },
8846     
8847     
8848     getRowDom : function(rowIndex)
8849     {
8850         var rows = this.el.select('tbody > tr', true).elements;
8851         
8852         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8853         
8854     },
8855     // returns the object tree for a tr..
8856   
8857     
8858     renderRow : function(cm, ds, rowIndex) 
8859     {
8860         var d = ds.getAt(rowIndex);
8861         
8862         var row = {
8863             tag : 'tr',
8864             cls : 'x-row-' + rowIndex,
8865             cn : []
8866         };
8867             
8868         var cellObjects = [];
8869         
8870         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8871             var config = cm.config[i];
8872             
8873             var renderer = cm.getRenderer(i);
8874             var value = '';
8875             var id = false;
8876             
8877             if(typeof(renderer) !== 'undefined'){
8878                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8879             }
8880             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8881             // and are rendered into the cells after the row is rendered - using the id for the element.
8882             
8883             if(typeof(value) === 'object'){
8884                 id = Roo.id();
8885                 cellObjects.push({
8886                     container : id,
8887                     cfg : value 
8888                 })
8889             }
8890             
8891             var rowcfg = {
8892                 record: d,
8893                 rowIndex : rowIndex,
8894                 colIndex : i,
8895                 rowClass : ''
8896             };
8897
8898             this.fireEvent('rowclass', this, rowcfg);
8899             
8900             var td = {
8901                 tag: 'td',
8902                 // this might end up displaying HTML?
8903                 // this is too messy... - better to only do it on columsn you know are going to be too long
8904                 //tooltip : (typeof(value) === 'object') ? '' : value,
8905                 cls : rowcfg.rowClass + ' x-col-' + i,
8906                 style: '',
8907                 html: (typeof(value) === 'object') ? '' : value
8908             };
8909             
8910             if (id) {
8911                 td.id = id;
8912             }
8913             
8914             if(typeof(config.colspan) != 'undefined'){
8915                 td.colspan = config.colspan;
8916             }
8917             
8918             if(typeof(config.hidden) != 'undefined' && config.hidden){
8919                 td.style += ' display:none;';
8920             }
8921             
8922             if(typeof(config.align) != 'undefined' && config.align.length){
8923                 td.style += ' text-align:' + config.align + ';';
8924             }
8925             if(typeof(config.valign) != 'undefined' && config.valign.length){
8926                 td.style += ' vertical-align:' + config.valign + ';';
8927             }
8928             
8929             if(typeof(config.width) != 'undefined'){
8930                 td.style += ' width:' +  config.width + 'px;';
8931             }
8932             
8933             if(typeof(config.cursor) != 'undefined'){
8934                 td.style += ' cursor:' +  config.cursor + ';';
8935             }
8936             
8937             if(typeof(config.cls) != 'undefined'){
8938                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8939             }
8940             
8941             ['xs','sm','md','lg'].map(function(size){
8942                 
8943                 if(typeof(config[size]) == 'undefined'){
8944                     return;
8945                 }
8946                 
8947                 
8948                   
8949                 if (!config[size]) { // 0 = hidden
8950                     // BS 4 '0' is treated as hide that column and below.
8951                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8952                     return;
8953                 }
8954                 
8955                 td.cls += ' col-' + size + '-' + config[size] + (
8956                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8957                 );
8958                  
8959
8960             });
8961             
8962             row.cn.push(td);
8963            
8964         }
8965         
8966         row.cellObjects = cellObjects;
8967         
8968         return row;
8969           
8970     },
8971     
8972     
8973     
8974     onBeforeLoad : function()
8975     {
8976         
8977     },
8978      /**
8979      * Remove all rows
8980      */
8981     clear : function()
8982     {
8983         this.el.select('tbody', true).first().dom.innerHTML = '';
8984     },
8985     /**
8986      * Show or hide a row.
8987      * @param {Number} rowIndex to show or hide
8988      * @param {Boolean} state hide
8989      */
8990     setRowVisibility : function(rowIndex, state)
8991     {
8992         var bt = this.mainBody.dom;
8993         
8994         var rows = this.el.select('tbody > tr', true).elements;
8995         
8996         if(typeof(rows[rowIndex]) == 'undefined'){
8997             return;
8998         }
8999         rows[rowIndex].dom.style.display = state ? '' : 'none';
9000     },
9001     
9002     
9003     getSelectionModel : function(){
9004         if(!this.selModel){
9005             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9006         }
9007         return this.selModel;
9008     },
9009     /*
9010      * Render the Roo.bootstrap object from renderder
9011      */
9012     renderCellObject : function(r)
9013     {
9014         var _this = this;
9015         
9016         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9017         
9018         var t = r.cfg.render(r.container);
9019         
9020         if(r.cfg.cn){
9021             Roo.each(r.cfg.cn, function(c){
9022                 var child = {
9023                     container: t.getChildContainer(),
9024                     cfg: c
9025                 };
9026                 _this.renderCellObject(child);
9027             })
9028         }
9029     },
9030     
9031     getRowIndex : function(row)
9032     {
9033         var rowIndex = -1;
9034         
9035         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9036             if(el != row){
9037                 return;
9038             }
9039             
9040             rowIndex = index;
9041         });
9042         
9043         return rowIndex;
9044     },
9045      /**
9046      * Returns the grid's underlying element = used by panel.Grid
9047      * @return {Element} The element
9048      */
9049     getGridEl : function(){
9050         return this.el;
9051     },
9052      /**
9053      * Forces a resize - used by panel.Grid
9054      * @return {Element} The element
9055      */
9056     autoSize : function()
9057     {
9058         //var ctr = Roo.get(this.container.dom.parentElement);
9059         var ctr = Roo.get(this.el.dom);
9060         
9061         var thd = this.getGridEl().select('thead',true).first();
9062         var tbd = this.getGridEl().select('tbody', true).first();
9063         var tfd = this.getGridEl().select('tfoot', true).first();
9064         
9065         var cw = ctr.getWidth();
9066         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9067         
9068         if (tbd) {
9069             
9070             tbd.setWidth(ctr.getWidth());
9071             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9072             // this needs fixing for various usage - currently only hydra job advers I think..
9073             //tdb.setHeight(
9074             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9075             //); 
9076             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9077             cw -= barsize;
9078         }
9079         cw = Math.max(cw, this.totalWidth);
9080         this.getGridEl().select('tbody tr',true).setWidth(cw);
9081         
9082         // resize 'expandable coloumn?
9083         
9084         return; // we doe not have a view in this design..
9085         
9086     },
9087     onBodyScroll: function()
9088     {
9089         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9090         if(this.mainHead){
9091             this.mainHead.setStyle({
9092                 'position' : 'relative',
9093                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9094             });
9095         }
9096         
9097         if(this.lazyLoad){
9098             
9099             var scrollHeight = this.mainBody.dom.scrollHeight;
9100             
9101             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9102             
9103             var height = this.mainBody.getHeight();
9104             
9105             if(scrollHeight - height == scrollTop) {
9106                 
9107                 var total = this.ds.getTotalCount();
9108                 
9109                 if(this.footer.cursor + this.footer.pageSize < total){
9110                     
9111                     this.footer.ds.load({
9112                         params : {
9113                             start : this.footer.cursor + this.footer.pageSize,
9114                             limit : this.footer.pageSize
9115                         },
9116                         add : true
9117                     });
9118                 }
9119             }
9120             
9121         }
9122     },
9123     
9124     onHeaderChange : function()
9125     {
9126         var header = this.renderHeader();
9127         var table = this.el.select('table', true).first();
9128         
9129         this.mainHead.remove();
9130         this.mainHead = table.createChild(header, this.mainBody, false);
9131         
9132         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9133             e.on('click', this.sort, this);
9134         }, this);
9135         
9136         
9137     },
9138     
9139     onHiddenChange : function(colModel, colIndex, hidden)
9140     {
9141         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9142         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9143         
9144         this.CSS.updateRule(thSelector, "display", "");
9145         this.CSS.updateRule(tdSelector, "display", "");
9146         
9147         if(hidden){
9148             this.CSS.updateRule(thSelector, "display", "none");
9149             this.CSS.updateRule(tdSelector, "display", "none");
9150         }
9151         
9152         this.onHeaderChange();
9153         this.onLoad();
9154     },
9155     
9156     setColumnWidth: function(col_index, width)
9157     {
9158         // width = "md-2 xs-2..."
9159         if(!this.colModel.config[col_index]) {
9160             return;
9161         }
9162         
9163         var w = width.split(" ");
9164         
9165         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9166         
9167         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9168         
9169         
9170         for(var j = 0; j < w.length; j++) {
9171             
9172             if(!w[j]) {
9173                 continue;
9174             }
9175             
9176             var size_cls = w[j].split("-");
9177             
9178             if(!Number.isInteger(size_cls[1] * 1)) {
9179                 continue;
9180             }
9181             
9182             if(!this.colModel.config[col_index][size_cls[0]]) {
9183                 continue;
9184             }
9185             
9186             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9187                 continue;
9188             }
9189             
9190             h_row[0].classList.replace(
9191                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9192                 "col-"+size_cls[0]+"-"+size_cls[1]
9193             );
9194             
9195             for(var i = 0; i < rows.length; i++) {
9196                 
9197                 var size_cls = w[j].split("-");
9198                 
9199                 if(!Number.isInteger(size_cls[1] * 1)) {
9200                     continue;
9201                 }
9202                 
9203                 if(!this.colModel.config[col_index][size_cls[0]]) {
9204                     continue;
9205                 }
9206                 
9207                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9208                     continue;
9209                 }
9210                 
9211                 rows[i].classList.replace(
9212                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9213                     "col-"+size_cls[0]+"-"+size_cls[1]
9214                 );
9215             }
9216             
9217             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9218         }
9219     }
9220 });
9221
9222  
9223
9224  /*
9225  * - LGPL
9226  *
9227  * table cell
9228  * 
9229  */
9230
9231 /**
9232  * @class Roo.bootstrap.TableCell
9233  * @extends Roo.bootstrap.Component
9234  * Bootstrap TableCell class
9235  * @cfg {String} html cell contain text
9236  * @cfg {String} cls cell class
9237  * @cfg {String} tag cell tag (td|th) default td
9238  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9239  * @cfg {String} align Aligns the content in a cell
9240  * @cfg {String} axis Categorizes cells
9241  * @cfg {String} bgcolor Specifies the background color of a cell
9242  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9243  * @cfg {Number} colspan Specifies the number of columns a cell should span
9244  * @cfg {String} headers Specifies one or more header cells a cell is related to
9245  * @cfg {Number} height Sets the height of a cell
9246  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9247  * @cfg {Number} rowspan Sets the number of rows a cell should span
9248  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9249  * @cfg {String} valign Vertical aligns the content in a cell
9250  * @cfg {Number} width Specifies the width of a cell
9251  * 
9252  * @constructor
9253  * Create a new TableCell
9254  * @param {Object} config The config object
9255  */
9256
9257 Roo.bootstrap.TableCell = function(config){
9258     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9259 };
9260
9261 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9262     
9263     html: false,
9264     cls: false,
9265     tag: false,
9266     abbr: false,
9267     align: false,
9268     axis: false,
9269     bgcolor: false,
9270     charoff: false,
9271     colspan: false,
9272     headers: false,
9273     height: false,
9274     nowrap: false,
9275     rowspan: false,
9276     scope: false,
9277     valign: false,
9278     width: false,
9279     
9280     
9281     getAutoCreate : function(){
9282         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9283         
9284         cfg = {
9285             tag: 'td'
9286         };
9287         
9288         if(this.tag){
9289             cfg.tag = this.tag;
9290         }
9291         
9292         if (this.html) {
9293             cfg.html=this.html
9294         }
9295         if (this.cls) {
9296             cfg.cls=this.cls
9297         }
9298         if (this.abbr) {
9299             cfg.abbr=this.abbr
9300         }
9301         if (this.align) {
9302             cfg.align=this.align
9303         }
9304         if (this.axis) {
9305             cfg.axis=this.axis
9306         }
9307         if (this.bgcolor) {
9308             cfg.bgcolor=this.bgcolor
9309         }
9310         if (this.charoff) {
9311             cfg.charoff=this.charoff
9312         }
9313         if (this.colspan) {
9314             cfg.colspan=this.colspan
9315         }
9316         if (this.headers) {
9317             cfg.headers=this.headers
9318         }
9319         if (this.height) {
9320             cfg.height=this.height
9321         }
9322         if (this.nowrap) {
9323             cfg.nowrap=this.nowrap
9324         }
9325         if (this.rowspan) {
9326             cfg.rowspan=this.rowspan
9327         }
9328         if (this.scope) {
9329             cfg.scope=this.scope
9330         }
9331         if (this.valign) {
9332             cfg.valign=this.valign
9333         }
9334         if (this.width) {
9335             cfg.width=this.width
9336         }
9337         
9338         
9339         return cfg;
9340     }
9341    
9342 });
9343
9344  
9345
9346  /*
9347  * - LGPL
9348  *
9349  * table row
9350  * 
9351  */
9352
9353 /**
9354  * @class Roo.bootstrap.TableRow
9355  * @extends Roo.bootstrap.Component
9356  * Bootstrap TableRow class
9357  * @cfg {String} cls row class
9358  * @cfg {String} align Aligns the content in a table row
9359  * @cfg {String} bgcolor Specifies a background color for a table row
9360  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9361  * @cfg {String} valign Vertical aligns the content in a table row
9362  * 
9363  * @constructor
9364  * Create a new TableRow
9365  * @param {Object} config The config object
9366  */
9367
9368 Roo.bootstrap.TableRow = function(config){
9369     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9370 };
9371
9372 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9373     
9374     cls: false,
9375     align: false,
9376     bgcolor: false,
9377     charoff: false,
9378     valign: false,
9379     
9380     getAutoCreate : function(){
9381         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9382         
9383         cfg = {
9384             tag: 'tr'
9385         };
9386             
9387         if(this.cls){
9388             cfg.cls = this.cls;
9389         }
9390         if(this.align){
9391             cfg.align = this.align;
9392         }
9393         if(this.bgcolor){
9394             cfg.bgcolor = this.bgcolor;
9395         }
9396         if(this.charoff){
9397             cfg.charoff = this.charoff;
9398         }
9399         if(this.valign){
9400             cfg.valign = this.valign;
9401         }
9402         
9403         return cfg;
9404     }
9405    
9406 });
9407
9408  
9409
9410  /*
9411  * - LGPL
9412  *
9413  * table body
9414  * 
9415  */
9416
9417 /**
9418  * @class Roo.bootstrap.TableBody
9419  * @extends Roo.bootstrap.Component
9420  * Bootstrap TableBody class
9421  * @cfg {String} cls element class
9422  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9423  * @cfg {String} align Aligns the content inside the element
9424  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9425  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9426  * 
9427  * @constructor
9428  * Create a new TableBody
9429  * @param {Object} config The config object
9430  */
9431
9432 Roo.bootstrap.TableBody = function(config){
9433     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9434 };
9435
9436 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9437     
9438     cls: false,
9439     tag: false,
9440     align: false,
9441     charoff: false,
9442     valign: false,
9443     
9444     getAutoCreate : function(){
9445         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9446         
9447         cfg = {
9448             tag: 'tbody'
9449         };
9450             
9451         if (this.cls) {
9452             cfg.cls=this.cls
9453         }
9454         if(this.tag){
9455             cfg.tag = this.tag;
9456         }
9457         
9458         if(this.align){
9459             cfg.align = this.align;
9460         }
9461         if(this.charoff){
9462             cfg.charoff = this.charoff;
9463         }
9464         if(this.valign){
9465             cfg.valign = this.valign;
9466         }
9467         
9468         return cfg;
9469     }
9470     
9471     
9472 //    initEvents : function()
9473 //    {
9474 //        
9475 //        if(!this.store){
9476 //            return;
9477 //        }
9478 //        
9479 //        this.store = Roo.factory(this.store, Roo.data);
9480 //        this.store.on('load', this.onLoad, this);
9481 //        
9482 //        this.store.load();
9483 //        
9484 //    },
9485 //    
9486 //    onLoad: function () 
9487 //    {   
9488 //        this.fireEvent('load', this);
9489 //    }
9490 //    
9491 //   
9492 });
9493
9494  
9495
9496  /*
9497  * Based on:
9498  * Ext JS Library 1.1.1
9499  * Copyright(c) 2006-2007, Ext JS, LLC.
9500  *
9501  * Originally Released Under LGPL - original licence link has changed is not relivant.
9502  *
9503  * Fork - LGPL
9504  * <script type="text/javascript">
9505  */
9506
9507 // as we use this in bootstrap.
9508 Roo.namespace('Roo.form');
9509  /**
9510  * @class Roo.form.Action
9511  * Internal Class used to handle form actions
9512  * @constructor
9513  * @param {Roo.form.BasicForm} el The form element or its id
9514  * @param {Object} config Configuration options
9515  */
9516
9517  
9518  
9519 // define the action interface
9520 Roo.form.Action = function(form, options){
9521     this.form = form;
9522     this.options = options || {};
9523 };
9524 /**
9525  * Client Validation Failed
9526  * @const 
9527  */
9528 Roo.form.Action.CLIENT_INVALID = 'client';
9529 /**
9530  * Server Validation Failed
9531  * @const 
9532  */
9533 Roo.form.Action.SERVER_INVALID = 'server';
9534  /**
9535  * Connect to Server Failed
9536  * @const 
9537  */
9538 Roo.form.Action.CONNECT_FAILURE = 'connect';
9539 /**
9540  * Reading Data from Server Failed
9541  * @const 
9542  */
9543 Roo.form.Action.LOAD_FAILURE = 'load';
9544
9545 Roo.form.Action.prototype = {
9546     type : 'default',
9547     failureType : undefined,
9548     response : undefined,
9549     result : undefined,
9550
9551     // interface method
9552     run : function(options){
9553
9554     },
9555
9556     // interface method
9557     success : function(response){
9558
9559     },
9560
9561     // interface method
9562     handleResponse : function(response){
9563
9564     },
9565
9566     // default connection failure
9567     failure : function(response){
9568         
9569         this.response = response;
9570         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9571         this.form.afterAction(this, false);
9572     },
9573
9574     processResponse : function(response){
9575         this.response = response;
9576         if(!response.responseText){
9577             return true;
9578         }
9579         this.result = this.handleResponse(response);
9580         return this.result;
9581     },
9582
9583     // utility functions used internally
9584     getUrl : function(appendParams){
9585         var url = this.options.url || this.form.url || this.form.el.dom.action;
9586         if(appendParams){
9587             var p = this.getParams();
9588             if(p){
9589                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9590             }
9591         }
9592         return url;
9593     },
9594
9595     getMethod : function(){
9596         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9597     },
9598
9599     getParams : function(){
9600         var bp = this.form.baseParams;
9601         var p = this.options.params;
9602         if(p){
9603             if(typeof p == "object"){
9604                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9605             }else if(typeof p == 'string' && bp){
9606                 p += '&' + Roo.urlEncode(bp);
9607             }
9608         }else if(bp){
9609             p = Roo.urlEncode(bp);
9610         }
9611         return p;
9612     },
9613
9614     createCallback : function(){
9615         return {
9616             success: this.success,
9617             failure: this.failure,
9618             scope: this,
9619             timeout: (this.form.timeout*1000),
9620             upload: this.form.fileUpload ? this.success : undefined
9621         };
9622     }
9623 };
9624
9625 Roo.form.Action.Submit = function(form, options){
9626     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9627 };
9628
9629 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9630     type : 'submit',
9631
9632     haveProgress : false,
9633     uploadComplete : false,
9634     
9635     // uploadProgress indicator.
9636     uploadProgress : function()
9637     {
9638         if (!this.form.progressUrl) {
9639             return;
9640         }
9641         
9642         if (!this.haveProgress) {
9643             Roo.MessageBox.progress("Uploading", "Uploading");
9644         }
9645         if (this.uploadComplete) {
9646            Roo.MessageBox.hide();
9647            return;
9648         }
9649         
9650         this.haveProgress = true;
9651    
9652         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9653         
9654         var c = new Roo.data.Connection();
9655         c.request({
9656             url : this.form.progressUrl,
9657             params: {
9658                 id : uid
9659             },
9660             method: 'GET',
9661             success : function(req){
9662                //console.log(data);
9663                 var rdata = false;
9664                 var edata;
9665                 try  {
9666                    rdata = Roo.decode(req.responseText)
9667                 } catch (e) {
9668                     Roo.log("Invalid data from server..");
9669                     Roo.log(edata);
9670                     return;
9671                 }
9672                 if (!rdata || !rdata.success) {
9673                     Roo.log(rdata);
9674                     Roo.MessageBox.alert(Roo.encode(rdata));
9675                     return;
9676                 }
9677                 var data = rdata.data;
9678                 
9679                 if (this.uploadComplete) {
9680                    Roo.MessageBox.hide();
9681                    return;
9682                 }
9683                    
9684                 if (data){
9685                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9686                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9687                     );
9688                 }
9689                 this.uploadProgress.defer(2000,this);
9690             },
9691        
9692             failure: function(data) {
9693                 Roo.log('progress url failed ');
9694                 Roo.log(data);
9695             },
9696             scope : this
9697         });
9698            
9699     },
9700     
9701     
9702     run : function()
9703     {
9704         // run get Values on the form, so it syncs any secondary forms.
9705         this.form.getValues();
9706         
9707         var o = this.options;
9708         var method = this.getMethod();
9709         var isPost = method == 'POST';
9710         if(o.clientValidation === false || this.form.isValid()){
9711             
9712             if (this.form.progressUrl) {
9713                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9714                     (new Date() * 1) + '' + Math.random());
9715                     
9716             } 
9717             
9718             
9719             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9720                 form:this.form.el.dom,
9721                 url:this.getUrl(!isPost),
9722                 method: method,
9723                 params:isPost ? this.getParams() : null,
9724                 isUpload: this.form.fileUpload,
9725                 formData : this.form.formData
9726             }));
9727             
9728             this.uploadProgress();
9729
9730         }else if (o.clientValidation !== false){ // client validation failed
9731             this.failureType = Roo.form.Action.CLIENT_INVALID;
9732             this.form.afterAction(this, false);
9733         }
9734     },
9735
9736     success : function(response)
9737     {
9738         this.uploadComplete= true;
9739         if (this.haveProgress) {
9740             Roo.MessageBox.hide();
9741         }
9742         
9743         
9744         var result = this.processResponse(response);
9745         if(result === true || result.success){
9746             this.form.afterAction(this, true);
9747             return;
9748         }
9749         if(result.errors){
9750             this.form.markInvalid(result.errors);
9751             this.failureType = Roo.form.Action.SERVER_INVALID;
9752         }
9753         this.form.afterAction(this, false);
9754     },
9755     failure : function(response)
9756     {
9757         this.uploadComplete= true;
9758         if (this.haveProgress) {
9759             Roo.MessageBox.hide();
9760         }
9761         
9762         this.response = response;
9763         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9764         this.form.afterAction(this, false);
9765     },
9766     
9767     handleResponse : function(response){
9768         if(this.form.errorReader){
9769             var rs = this.form.errorReader.read(response);
9770             var errors = [];
9771             if(rs.records){
9772                 for(var i = 0, len = rs.records.length; i < len; i++) {
9773                     var r = rs.records[i];
9774                     errors[i] = r.data;
9775                 }
9776             }
9777             if(errors.length < 1){
9778                 errors = null;
9779             }
9780             return {
9781                 success : rs.success,
9782                 errors : errors
9783             };
9784         }
9785         var ret = false;
9786         try {
9787             ret = Roo.decode(response.responseText);
9788         } catch (e) {
9789             ret = {
9790                 success: false,
9791                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9792                 errors : []
9793             };
9794         }
9795         return ret;
9796         
9797     }
9798 });
9799
9800
9801 Roo.form.Action.Load = function(form, options){
9802     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9803     this.reader = this.form.reader;
9804 };
9805
9806 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9807     type : 'load',
9808
9809     run : function(){
9810         
9811         Roo.Ajax.request(Roo.apply(
9812                 this.createCallback(), {
9813                     method:this.getMethod(),
9814                     url:this.getUrl(false),
9815                     params:this.getParams()
9816         }));
9817     },
9818
9819     success : function(response){
9820         
9821         var result = this.processResponse(response);
9822         if(result === true || !result.success || !result.data){
9823             this.failureType = Roo.form.Action.LOAD_FAILURE;
9824             this.form.afterAction(this, false);
9825             return;
9826         }
9827         this.form.clearInvalid();
9828         this.form.setValues(result.data);
9829         this.form.afterAction(this, true);
9830     },
9831
9832     handleResponse : function(response){
9833         if(this.form.reader){
9834             var rs = this.form.reader.read(response);
9835             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9836             return {
9837                 success : rs.success,
9838                 data : data
9839             };
9840         }
9841         return Roo.decode(response.responseText);
9842     }
9843 });
9844
9845 Roo.form.Action.ACTION_TYPES = {
9846     'load' : Roo.form.Action.Load,
9847     'submit' : Roo.form.Action.Submit
9848 };/*
9849  * - LGPL
9850  *
9851  * form
9852  *
9853  */
9854
9855 /**
9856  * @class Roo.bootstrap.Form
9857  * @extends Roo.bootstrap.Component
9858  * Bootstrap Form class
9859  * @cfg {String} method  GET | POST (default POST)
9860  * @cfg {String} labelAlign top | left (default top)
9861  * @cfg {String} align left  | right - for navbars
9862  * @cfg {Boolean} loadMask load mask when submit (default true)
9863
9864  *
9865  * @constructor
9866  * Create a new Form
9867  * @param {Object} config The config object
9868  */
9869
9870
9871 Roo.bootstrap.Form = function(config){
9872     
9873     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9874     
9875     Roo.bootstrap.Form.popover.apply();
9876     
9877     this.addEvents({
9878         /**
9879          * @event clientvalidation
9880          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9881          * @param {Form} this
9882          * @param {Boolean} valid true if the form has passed client-side validation
9883          */
9884         clientvalidation: true,
9885         /**
9886          * @event beforeaction
9887          * Fires before any action is performed. Return false to cancel the action.
9888          * @param {Form} this
9889          * @param {Action} action The action to be performed
9890          */
9891         beforeaction: true,
9892         /**
9893          * @event actionfailed
9894          * Fires when an action fails.
9895          * @param {Form} this
9896          * @param {Action} action The action that failed
9897          */
9898         actionfailed : true,
9899         /**
9900          * @event actioncomplete
9901          * Fires when an action is completed.
9902          * @param {Form} this
9903          * @param {Action} action The action that completed
9904          */
9905         actioncomplete : true
9906     });
9907 };
9908
9909 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9910
9911      /**
9912      * @cfg {String} method
9913      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9914      */
9915     method : 'POST',
9916     /**
9917      * @cfg {String} url
9918      * The URL to use for form actions if one isn't supplied in the action options.
9919      */
9920     /**
9921      * @cfg {Boolean} fileUpload
9922      * Set to true if this form is a file upload.
9923      */
9924
9925     /**
9926      * @cfg {Object} baseParams
9927      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9928      */
9929
9930     /**
9931      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9932      */
9933     timeout: 30,
9934     /**
9935      * @cfg {Sting} align (left|right) for navbar forms
9936      */
9937     align : 'left',
9938
9939     // private
9940     activeAction : null,
9941
9942     /**
9943      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9944      * element by passing it or its id or mask the form itself by passing in true.
9945      * @type Mixed
9946      */
9947     waitMsgTarget : false,
9948
9949     loadMask : true,
9950     
9951     /**
9952      * @cfg {Boolean} errorMask (true|false) default false
9953      */
9954     errorMask : false,
9955     
9956     /**
9957      * @cfg {Number} maskOffset Default 100
9958      */
9959     maskOffset : 100,
9960     
9961     /**
9962      * @cfg {Boolean} maskBody
9963      */
9964     maskBody : false,
9965
9966     getAutoCreate : function(){
9967
9968         var cfg = {
9969             tag: 'form',
9970             method : this.method || 'POST',
9971             id : this.id || Roo.id(),
9972             cls : ''
9973         };
9974         if (this.parent().xtype.match(/^Nav/)) {
9975             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9976
9977         }
9978
9979         if (this.labelAlign == 'left' ) {
9980             cfg.cls += ' form-horizontal';
9981         }
9982
9983
9984         return cfg;
9985     },
9986     initEvents : function()
9987     {
9988         this.el.on('submit', this.onSubmit, this);
9989         // this was added as random key presses on the form where triggering form submit.
9990         this.el.on('keypress', function(e) {
9991             if (e.getCharCode() != 13) {
9992                 return true;
9993             }
9994             // we might need to allow it for textareas.. and some other items.
9995             // check e.getTarget().
9996
9997             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9998                 return true;
9999             }
10000
10001             Roo.log("keypress blocked");
10002
10003             e.preventDefault();
10004             return false;
10005         });
10006         
10007     },
10008     // private
10009     onSubmit : function(e){
10010         e.stopEvent();
10011     },
10012
10013      /**
10014      * Returns true if client-side validation on the form is successful.
10015      * @return Boolean
10016      */
10017     isValid : function(){
10018         var items = this.getItems();
10019         var valid = true;
10020         var target = false;
10021         
10022         items.each(function(f){
10023             
10024             if(f.validate()){
10025                 return;
10026             }
10027             
10028             Roo.log('invalid field: ' + f.name);
10029             
10030             valid = false;
10031
10032             if(!target && f.el.isVisible(true)){
10033                 target = f;
10034             }
10035            
10036         });
10037         
10038         if(this.errorMask && !valid){
10039             Roo.bootstrap.Form.popover.mask(this, target);
10040         }
10041         
10042         return valid;
10043     },
10044     
10045     /**
10046      * Returns true if any fields in this form have changed since their original load.
10047      * @return Boolean
10048      */
10049     isDirty : function(){
10050         var dirty = false;
10051         var items = this.getItems();
10052         items.each(function(f){
10053            if(f.isDirty()){
10054                dirty = true;
10055                return false;
10056            }
10057            return true;
10058         });
10059         return dirty;
10060     },
10061      /**
10062      * Performs a predefined action (submit or load) or custom actions you define on this form.
10063      * @param {String} actionName The name of the action type
10064      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10065      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10066      * accept other config options):
10067      * <pre>
10068 Property          Type             Description
10069 ----------------  ---------------  ----------------------------------------------------------------------------------
10070 url               String           The url for the action (defaults to the form's url)
10071 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10072 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10073 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10074                                    validate the form on the client (defaults to false)
10075      * </pre>
10076      * @return {BasicForm} this
10077      */
10078     doAction : function(action, options){
10079         if(typeof action == 'string'){
10080             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10081         }
10082         if(this.fireEvent('beforeaction', this, action) !== false){
10083             this.beforeAction(action);
10084             action.run.defer(100, action);
10085         }
10086         return this;
10087     },
10088
10089     // private
10090     beforeAction : function(action){
10091         var o = action.options;
10092         
10093         if(this.loadMask){
10094             
10095             if(this.maskBody){
10096                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10097             } else {
10098                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10099             }
10100         }
10101         // not really supported yet.. ??
10102
10103         //if(this.waitMsgTarget === true){
10104         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10105         //}else if(this.waitMsgTarget){
10106         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10107         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10108         //}else {
10109         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10110        // }
10111
10112     },
10113
10114     // private
10115     afterAction : function(action, success){
10116         this.activeAction = null;
10117         var o = action.options;
10118
10119         if(this.loadMask){
10120             
10121             if(this.maskBody){
10122                 Roo.get(document.body).unmask();
10123             } else {
10124                 this.el.unmask();
10125             }
10126         }
10127         
10128         //if(this.waitMsgTarget === true){
10129 //            this.el.unmask();
10130         //}else if(this.waitMsgTarget){
10131         //    this.waitMsgTarget.unmask();
10132         //}else{
10133         //    Roo.MessageBox.updateProgress(1);
10134         //    Roo.MessageBox.hide();
10135        // }
10136         //
10137         if(success){
10138             if(o.reset){
10139                 this.reset();
10140             }
10141             Roo.callback(o.success, o.scope, [this, action]);
10142             this.fireEvent('actioncomplete', this, action);
10143
10144         }else{
10145
10146             // failure condition..
10147             // we have a scenario where updates need confirming.
10148             // eg. if a locking scenario exists..
10149             // we look for { errors : { needs_confirm : true }} in the response.
10150             if (
10151                 (typeof(action.result) != 'undefined')  &&
10152                 (typeof(action.result.errors) != 'undefined')  &&
10153                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10154            ){
10155                 var _t = this;
10156                 Roo.log("not supported yet");
10157                  /*
10158
10159                 Roo.MessageBox.confirm(
10160                     "Change requires confirmation",
10161                     action.result.errorMsg,
10162                     function(r) {
10163                         if (r != 'yes') {
10164                             return;
10165                         }
10166                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10167                     }
10168
10169                 );
10170                 */
10171
10172
10173                 return;
10174             }
10175
10176             Roo.callback(o.failure, o.scope, [this, action]);
10177             // show an error message if no failed handler is set..
10178             if (!this.hasListener('actionfailed')) {
10179                 Roo.log("need to add dialog support");
10180                 /*
10181                 Roo.MessageBox.alert("Error",
10182                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10183                         action.result.errorMsg :
10184                         "Saving Failed, please check your entries or try again"
10185                 );
10186                 */
10187             }
10188
10189             this.fireEvent('actionfailed', this, action);
10190         }
10191
10192     },
10193     /**
10194      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10195      * @param {String} id The value to search for
10196      * @return Field
10197      */
10198     findField : function(id){
10199         var items = this.getItems();
10200         var field = items.get(id);
10201         if(!field){
10202              items.each(function(f){
10203                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10204                     field = f;
10205                     return false;
10206                 }
10207                 return true;
10208             });
10209         }
10210         return field || null;
10211     },
10212      /**
10213      * Mark fields in this form invalid in bulk.
10214      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10215      * @return {BasicForm} this
10216      */
10217     markInvalid : function(errors){
10218         if(errors instanceof Array){
10219             for(var i = 0, len = errors.length; i < len; i++){
10220                 var fieldError = errors[i];
10221                 var f = this.findField(fieldError.id);
10222                 if(f){
10223                     f.markInvalid(fieldError.msg);
10224                 }
10225             }
10226         }else{
10227             var field, id;
10228             for(id in errors){
10229                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10230                     field.markInvalid(errors[id]);
10231                 }
10232             }
10233         }
10234         //Roo.each(this.childForms || [], function (f) {
10235         //    f.markInvalid(errors);
10236         //});
10237
10238         return this;
10239     },
10240
10241     /**
10242      * Set values for fields in this form in bulk.
10243      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10244      * @return {BasicForm} this
10245      */
10246     setValues : function(values){
10247         if(values instanceof Array){ // array of objects
10248             for(var i = 0, len = values.length; i < len; i++){
10249                 var v = values[i];
10250                 var f = this.findField(v.id);
10251                 if(f){
10252                     f.setValue(v.value);
10253                     if(this.trackResetOnLoad){
10254                         f.originalValue = f.getValue();
10255                     }
10256                 }
10257             }
10258         }else{ // object hash
10259             var field, id;
10260             for(id in values){
10261                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10262
10263                     if (field.setFromData &&
10264                         field.valueField &&
10265                         field.displayField &&
10266                         // combos' with local stores can
10267                         // be queried via setValue()
10268                         // to set their value..
10269                         (field.store && !field.store.isLocal)
10270                         ) {
10271                         // it's a combo
10272                         var sd = { };
10273                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10274                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10275                         field.setFromData(sd);
10276
10277                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10278                         
10279                         field.setFromData(values);
10280                         
10281                     } else {
10282                         field.setValue(values[id]);
10283                     }
10284
10285
10286                     if(this.trackResetOnLoad){
10287                         field.originalValue = field.getValue();
10288                     }
10289                 }
10290             }
10291         }
10292
10293         //Roo.each(this.childForms || [], function (f) {
10294         //    f.setValues(values);
10295         //});
10296
10297         return this;
10298     },
10299
10300     /**
10301      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10302      * they are returned as an array.
10303      * @param {Boolean} asString
10304      * @return {Object}
10305      */
10306     getValues : function(asString){
10307         //if (this.childForms) {
10308             // copy values from the child forms
10309         //    Roo.each(this.childForms, function (f) {
10310         //        this.setValues(f.getValues());
10311         //    }, this);
10312         //}
10313
10314
10315
10316         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10317         if(asString === true){
10318             return fs;
10319         }
10320         return Roo.urlDecode(fs);
10321     },
10322
10323     /**
10324      * Returns the fields in this form as an object with key/value pairs.
10325      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10326      * @return {Object}
10327      */
10328     getFieldValues : function(with_hidden)
10329     {
10330         var items = this.getItems();
10331         var ret = {};
10332         items.each(function(f){
10333             
10334             if (!f.getName()) {
10335                 return;
10336             }
10337             
10338             var v = f.getValue();
10339             
10340             if (f.inputType =='radio') {
10341                 if (typeof(ret[f.getName()]) == 'undefined') {
10342                     ret[f.getName()] = ''; // empty..
10343                 }
10344
10345                 if (!f.el.dom.checked) {
10346                     return;
10347
10348                 }
10349                 v = f.el.dom.value;
10350
10351             }
10352             
10353             if(f.xtype == 'MoneyField'){
10354                 ret[f.currencyName] = f.getCurrency();
10355             }
10356
10357             // not sure if this supported any more..
10358             if ((typeof(v) == 'object') && f.getRawValue) {
10359                 v = f.getRawValue() ; // dates..
10360             }
10361             // combo boxes where name != hiddenName...
10362             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10363                 ret[f.name] = f.getRawValue();
10364             }
10365             ret[f.getName()] = v;
10366         });
10367
10368         return ret;
10369     },
10370
10371     /**
10372      * Clears all invalid messages in this form.
10373      * @return {BasicForm} this
10374      */
10375     clearInvalid : function(){
10376         var items = this.getItems();
10377
10378         items.each(function(f){
10379            f.clearInvalid();
10380         });
10381
10382         return this;
10383     },
10384
10385     /**
10386      * Resets this form.
10387      * @return {BasicForm} this
10388      */
10389     reset : function(){
10390         var items = this.getItems();
10391         items.each(function(f){
10392             f.reset();
10393         });
10394
10395         Roo.each(this.childForms || [], function (f) {
10396             f.reset();
10397         });
10398
10399
10400         return this;
10401     },
10402     
10403     getItems : function()
10404     {
10405         var r=new Roo.util.MixedCollection(false, function(o){
10406             return o.id || (o.id = Roo.id());
10407         });
10408         var iter = function(el) {
10409             if (el.inputEl) {
10410                 r.add(el);
10411             }
10412             if (!el.items) {
10413                 return;
10414             }
10415             Roo.each(el.items,function(e) {
10416                 iter(e);
10417             });
10418         };
10419
10420         iter(this);
10421         return r;
10422     },
10423     
10424     hideFields : function(items)
10425     {
10426         Roo.each(items, function(i){
10427             
10428             var f = this.findField(i);
10429             
10430             if(!f){
10431                 return;
10432             }
10433             
10434             f.hide();
10435             
10436         }, this);
10437     },
10438     
10439     showFields : function(items)
10440     {
10441         Roo.each(items, function(i){
10442             
10443             var f = this.findField(i);
10444             
10445             if(!f){
10446                 return;
10447             }
10448             
10449             f.show();
10450             
10451         }, this);
10452     }
10453
10454 });
10455
10456 Roo.apply(Roo.bootstrap.Form, {
10457     
10458     popover : {
10459         
10460         padding : 5,
10461         
10462         isApplied : false,
10463         
10464         isMasked : false,
10465         
10466         form : false,
10467         
10468         target : false,
10469         
10470         toolTip : false,
10471         
10472         intervalID : false,
10473         
10474         maskEl : false,
10475         
10476         apply : function()
10477         {
10478             if(this.isApplied){
10479                 return;
10480             }
10481             
10482             this.maskEl = {
10483                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10484                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10485                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10486                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10487             };
10488             
10489             this.maskEl.top.enableDisplayMode("block");
10490             this.maskEl.left.enableDisplayMode("block");
10491             this.maskEl.bottom.enableDisplayMode("block");
10492             this.maskEl.right.enableDisplayMode("block");
10493             
10494             this.toolTip = new Roo.bootstrap.Tooltip({
10495                 cls : 'roo-form-error-popover',
10496                 alignment : {
10497                     'left' : ['r-l', [-2,0], 'right'],
10498                     'right' : ['l-r', [2,0], 'left'],
10499                     'bottom' : ['tl-bl', [0,2], 'top'],
10500                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10501                 }
10502             });
10503             
10504             this.toolTip.render(Roo.get(document.body));
10505
10506             this.toolTip.el.enableDisplayMode("block");
10507             
10508             Roo.get(document.body).on('click', function(){
10509                 this.unmask();
10510             }, this);
10511             
10512             Roo.get(document.body).on('touchstart', function(){
10513                 this.unmask();
10514             }, this);
10515             
10516             this.isApplied = true
10517         },
10518         
10519         mask : function(form, target)
10520         {
10521             this.form = form;
10522             
10523             this.target = target;
10524             
10525             if(!this.form.errorMask || !target.el){
10526                 return;
10527             }
10528             
10529             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10530             
10531             Roo.log(scrollable);
10532             
10533             var ot = this.target.el.calcOffsetsTo(scrollable);
10534             
10535             var scrollTo = ot[1] - this.form.maskOffset;
10536             
10537             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10538             
10539             scrollable.scrollTo('top', scrollTo);
10540             
10541             var box = this.target.el.getBox();
10542             Roo.log(box);
10543             var zIndex = Roo.bootstrap.Modal.zIndex++;
10544
10545             
10546             this.maskEl.top.setStyle('position', 'absolute');
10547             this.maskEl.top.setStyle('z-index', zIndex);
10548             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10549             this.maskEl.top.setLeft(0);
10550             this.maskEl.top.setTop(0);
10551             this.maskEl.top.show();
10552             
10553             this.maskEl.left.setStyle('position', 'absolute');
10554             this.maskEl.left.setStyle('z-index', zIndex);
10555             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10556             this.maskEl.left.setLeft(0);
10557             this.maskEl.left.setTop(box.y - this.padding);
10558             this.maskEl.left.show();
10559
10560             this.maskEl.bottom.setStyle('position', 'absolute');
10561             this.maskEl.bottom.setStyle('z-index', zIndex);
10562             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10563             this.maskEl.bottom.setLeft(0);
10564             this.maskEl.bottom.setTop(box.bottom + this.padding);
10565             this.maskEl.bottom.show();
10566
10567             this.maskEl.right.setStyle('position', 'absolute');
10568             this.maskEl.right.setStyle('z-index', zIndex);
10569             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10570             this.maskEl.right.setLeft(box.right + this.padding);
10571             this.maskEl.right.setTop(box.y - this.padding);
10572             this.maskEl.right.show();
10573
10574             this.toolTip.bindEl = this.target.el;
10575
10576             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10577
10578             var tip = this.target.blankText;
10579
10580             if(this.target.getValue() !== '' ) {
10581                 
10582                 if (this.target.invalidText.length) {
10583                     tip = this.target.invalidText;
10584                 } else if (this.target.regexText.length){
10585                     tip = this.target.regexText;
10586                 }
10587             }
10588
10589             this.toolTip.show(tip);
10590
10591             this.intervalID = window.setInterval(function() {
10592                 Roo.bootstrap.Form.popover.unmask();
10593             }, 10000);
10594
10595             window.onwheel = function(){ return false;};
10596             
10597             (function(){ this.isMasked = true; }).defer(500, this);
10598             
10599         },
10600         
10601         unmask : function()
10602         {
10603             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10604                 return;
10605             }
10606             
10607             this.maskEl.top.setStyle('position', 'absolute');
10608             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10609             this.maskEl.top.hide();
10610
10611             this.maskEl.left.setStyle('position', 'absolute');
10612             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10613             this.maskEl.left.hide();
10614
10615             this.maskEl.bottom.setStyle('position', 'absolute');
10616             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10617             this.maskEl.bottom.hide();
10618
10619             this.maskEl.right.setStyle('position', 'absolute');
10620             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10621             this.maskEl.right.hide();
10622             
10623             this.toolTip.hide();
10624             
10625             this.toolTip.el.hide();
10626             
10627             window.onwheel = function(){ return true;};
10628             
10629             if(this.intervalID){
10630                 window.clearInterval(this.intervalID);
10631                 this.intervalID = false;
10632             }
10633             
10634             this.isMasked = false;
10635             
10636         }
10637         
10638     }
10639     
10640 });
10641
10642 /*
10643  * Based on:
10644  * Ext JS Library 1.1.1
10645  * Copyright(c) 2006-2007, Ext JS, LLC.
10646  *
10647  * Originally Released Under LGPL - original licence link has changed is not relivant.
10648  *
10649  * Fork - LGPL
10650  * <script type="text/javascript">
10651  */
10652 /**
10653  * @class Roo.form.VTypes
10654  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10655  * @singleton
10656  */
10657 Roo.form.VTypes = function(){
10658     // closure these in so they are only created once.
10659     var alpha = /^[a-zA-Z_]+$/;
10660     var alphanum = /^[a-zA-Z0-9_]+$/;
10661     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10662     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10663
10664     // All these messages and functions are configurable
10665     return {
10666         /**
10667          * The function used to validate email addresses
10668          * @param {String} value The email address
10669          */
10670         'email' : function(v){
10671             return email.test(v);
10672         },
10673         /**
10674          * The error text to display when the email validation function returns false
10675          * @type String
10676          */
10677         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10678         /**
10679          * The keystroke filter mask to be applied on email input
10680          * @type RegExp
10681          */
10682         'emailMask' : /[a-z0-9_\.\-@]/i,
10683
10684         /**
10685          * The function used to validate URLs
10686          * @param {String} value The URL
10687          */
10688         'url' : function(v){
10689             return url.test(v);
10690         },
10691         /**
10692          * The error text to display when the url validation function returns false
10693          * @type String
10694          */
10695         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10696         
10697         /**
10698          * The function used to validate alpha values
10699          * @param {String} value The value
10700          */
10701         'alpha' : function(v){
10702             return alpha.test(v);
10703         },
10704         /**
10705          * The error text to display when the alpha validation function returns false
10706          * @type String
10707          */
10708         'alphaText' : 'This field should only contain letters and _',
10709         /**
10710          * The keystroke filter mask to be applied on alpha input
10711          * @type RegExp
10712          */
10713         'alphaMask' : /[a-z_]/i,
10714
10715         /**
10716          * The function used to validate alphanumeric values
10717          * @param {String} value The value
10718          */
10719         'alphanum' : function(v){
10720             return alphanum.test(v);
10721         },
10722         /**
10723          * The error text to display when the alphanumeric validation function returns false
10724          * @type String
10725          */
10726         'alphanumText' : 'This field should only contain letters, numbers and _',
10727         /**
10728          * The keystroke filter mask to be applied on alphanumeric input
10729          * @type RegExp
10730          */
10731         'alphanumMask' : /[a-z0-9_]/i
10732     };
10733 }();/*
10734  * - LGPL
10735  *
10736  * Input
10737  * 
10738  */
10739
10740 /**
10741  * @class Roo.bootstrap.Input
10742  * @extends Roo.bootstrap.Component
10743  * Bootstrap Input class
10744  * @cfg {Boolean} disabled is it disabled
10745  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10746  * @cfg {String} name name of the input
10747  * @cfg {string} fieldLabel - the label associated
10748  * @cfg {string} placeholder - placeholder to put in text.
10749  * @cfg {string}  before - input group add on before
10750  * @cfg {string} after - input group add on after
10751  * @cfg {string} size - (lg|sm) or leave empty..
10752  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10753  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10754  * @cfg {Number} md colspan out of 12 for computer-sized screens
10755  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10756  * @cfg {string} value default value of the input
10757  * @cfg {Number} labelWidth set the width of label 
10758  * @cfg {Number} labellg set the width of label (1-12)
10759  * @cfg {Number} labelmd set the width of label (1-12)
10760  * @cfg {Number} labelsm set the width of label (1-12)
10761  * @cfg {Number} labelxs set the width of label (1-12)
10762  * @cfg {String} labelAlign (top|left)
10763  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10764  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10765  * @cfg {String} indicatorpos (left|right) default left
10766  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10767  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10768  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10769
10770  * @cfg {String} align (left|center|right) Default left
10771  * @cfg {Boolean} forceFeedback (true|false) Default false
10772  * 
10773  * @constructor
10774  * Create a new Input
10775  * @param {Object} config The config object
10776  */
10777
10778 Roo.bootstrap.Input = function(config){
10779     
10780     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10781     
10782     this.addEvents({
10783         /**
10784          * @event focus
10785          * Fires when this field receives input focus.
10786          * @param {Roo.form.Field} this
10787          */
10788         focus : true,
10789         /**
10790          * @event blur
10791          * Fires when this field loses input focus.
10792          * @param {Roo.form.Field} this
10793          */
10794         blur : true,
10795         /**
10796          * @event specialkey
10797          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10798          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10799          * @param {Roo.form.Field} this
10800          * @param {Roo.EventObject} e The event object
10801          */
10802         specialkey : true,
10803         /**
10804          * @event change
10805          * Fires just before the field blurs if the field value has changed.
10806          * @param {Roo.form.Field} this
10807          * @param {Mixed} newValue The new value
10808          * @param {Mixed} oldValue The original value
10809          */
10810         change : true,
10811         /**
10812          * @event invalid
10813          * Fires after the field has been marked as invalid.
10814          * @param {Roo.form.Field} this
10815          * @param {String} msg The validation message
10816          */
10817         invalid : true,
10818         /**
10819          * @event valid
10820          * Fires after the field has been validated with no errors.
10821          * @param {Roo.form.Field} this
10822          */
10823         valid : true,
10824          /**
10825          * @event keyup
10826          * Fires after the key up
10827          * @param {Roo.form.Field} this
10828          * @param {Roo.EventObject}  e The event Object
10829          */
10830         keyup : true,
10831         /**
10832          * @event paste
10833          * Fires after the user pastes into input
10834          * @param {Roo.form.Field} this
10835          * @param {Roo.EventObject}  e The event Object
10836          */
10837         paste : true
10838     });
10839 };
10840
10841 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10842      /**
10843      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10844       automatic validation (defaults to "keyup").
10845      */
10846     validationEvent : "keyup",
10847      /**
10848      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10849      */
10850     validateOnBlur : true,
10851     /**
10852      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10853      */
10854     validationDelay : 250,
10855      /**
10856      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10857      */
10858     focusClass : "x-form-focus",  // not needed???
10859     
10860        
10861     /**
10862      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10863      */
10864     invalidClass : "has-warning",
10865     
10866     /**
10867      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10868      */
10869     validClass : "has-success",
10870     
10871     /**
10872      * @cfg {Boolean} hasFeedback (true|false) default true
10873      */
10874     hasFeedback : true,
10875     
10876     /**
10877      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10878      */
10879     invalidFeedbackClass : "glyphicon-warning-sign",
10880     
10881     /**
10882      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10883      */
10884     validFeedbackClass : "glyphicon-ok",
10885     
10886     /**
10887      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10888      */
10889     selectOnFocus : false,
10890     
10891      /**
10892      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10893      */
10894     maskRe : null,
10895        /**
10896      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10897      */
10898     vtype : null,
10899     
10900       /**
10901      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10902      */
10903     disableKeyFilter : false,
10904     
10905        /**
10906      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10907      */
10908     disabled : false,
10909      /**
10910      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10911      */
10912     allowBlank : true,
10913     /**
10914      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10915      */
10916     blankText : "Please complete this mandatory field",
10917     
10918      /**
10919      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10920      */
10921     minLength : 0,
10922     /**
10923      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10924      */
10925     maxLength : Number.MAX_VALUE,
10926     /**
10927      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10928      */
10929     minLengthText : "The minimum length for this field is {0}",
10930     /**
10931      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10932      */
10933     maxLengthText : "The maximum length for this field is {0}",
10934   
10935     
10936     /**
10937      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10938      * If available, this function will be called only after the basic validators all return true, and will be passed the
10939      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10940      */
10941     validator : null,
10942     /**
10943      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10944      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10945      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10946      */
10947     regex : null,
10948     /**
10949      * @cfg {String} regexText -- Depricated - use Invalid Text
10950      */
10951     regexText : "",
10952     
10953     /**
10954      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10955      */
10956     invalidText : "",
10957     
10958     
10959     
10960     autocomplete: false,
10961     
10962     
10963     fieldLabel : '',
10964     inputType : 'text',
10965     
10966     name : false,
10967     placeholder: false,
10968     before : false,
10969     after : false,
10970     size : false,
10971     hasFocus : false,
10972     preventMark: false,
10973     isFormField : true,
10974     value : '',
10975     labelWidth : 2,
10976     labelAlign : false,
10977     readOnly : false,
10978     align : false,
10979     formatedValue : false,
10980     forceFeedback : false,
10981     
10982     indicatorpos : 'left',
10983     
10984     labellg : 0,
10985     labelmd : 0,
10986     labelsm : 0,
10987     labelxs : 0,
10988     
10989     capture : '',
10990     accept : '',
10991     
10992     parentLabelAlign : function()
10993     {
10994         var parent = this;
10995         while (parent.parent()) {
10996             parent = parent.parent();
10997             if (typeof(parent.labelAlign) !='undefined') {
10998                 return parent.labelAlign;
10999             }
11000         }
11001         return 'left';
11002         
11003     },
11004     
11005     getAutoCreate : function()
11006     {
11007         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11008         
11009         var id = Roo.id();
11010         
11011         var cfg = {};
11012         
11013         if(this.inputType != 'hidden'){
11014             cfg.cls = 'form-group' //input-group
11015         }
11016         
11017         var input =  {
11018             tag: 'input',
11019             id : id,
11020             type : this.inputType,
11021             value : this.value,
11022             cls : 'form-control',
11023             placeholder : this.placeholder || '',
11024             autocomplete : this.autocomplete || 'new-password'
11025         };
11026         if (this.inputType == 'file') {
11027             input.style = 'overflow:hidden'; // why not in CSS?
11028         }
11029         
11030         if(this.capture.length){
11031             input.capture = this.capture;
11032         }
11033         
11034         if(this.accept.length){
11035             input.accept = this.accept + "/*";
11036         }
11037         
11038         if(this.align){
11039             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11040         }
11041         
11042         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11043             input.maxLength = this.maxLength;
11044         }
11045         
11046         if (this.disabled) {
11047             input.disabled=true;
11048         }
11049         
11050         if (this.readOnly) {
11051             input.readonly=true;
11052         }
11053         
11054         if (this.name) {
11055             input.name = this.name;
11056         }
11057         
11058         if (this.size) {
11059             input.cls += ' input-' + this.size;
11060         }
11061         
11062         var settings=this;
11063         ['xs','sm','md','lg'].map(function(size){
11064             if (settings[size]) {
11065                 cfg.cls += ' col-' + size + '-' + settings[size];
11066             }
11067         });
11068         
11069         var inputblock = input;
11070         
11071         var feedback = {
11072             tag: 'span',
11073             cls: 'glyphicon form-control-feedback'
11074         };
11075             
11076         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11077             
11078             inputblock = {
11079                 cls : 'has-feedback',
11080                 cn :  [
11081                     input,
11082                     feedback
11083                 ] 
11084             };  
11085         }
11086         
11087         if (this.before || this.after) {
11088             
11089             inputblock = {
11090                 cls : 'input-group',
11091                 cn :  [] 
11092             };
11093             
11094             if (this.before && typeof(this.before) == 'string') {
11095                 
11096                 inputblock.cn.push({
11097                     tag :'span',
11098                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11099                     html : this.before
11100                 });
11101             }
11102             if (this.before && typeof(this.before) == 'object') {
11103                 this.before = Roo.factory(this.before);
11104                 
11105                 inputblock.cn.push({
11106                     tag :'span',
11107                     cls : 'roo-input-before input-group-prepend   input-group-' +
11108                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11109                 });
11110             }
11111             
11112             inputblock.cn.push(input);
11113             
11114             if (this.after && typeof(this.after) == 'string') {
11115                 inputblock.cn.push({
11116                     tag :'span',
11117                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11118                     html : this.after
11119                 });
11120             }
11121             if (this.after && typeof(this.after) == 'object') {
11122                 this.after = Roo.factory(this.after);
11123                 
11124                 inputblock.cn.push({
11125                     tag :'span',
11126                     cls : 'roo-input-after input-group-append  input-group-' +
11127                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11128                 });
11129             }
11130             
11131             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11132                 inputblock.cls += ' has-feedback';
11133                 inputblock.cn.push(feedback);
11134             }
11135         };
11136         var indicator = {
11137             tag : 'i',
11138             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11139             tooltip : 'This field is required'
11140         };
11141         if (this.allowBlank ) {
11142             indicator.style = this.allowBlank ? ' display:none' : '';
11143         }
11144         if (align ==='left' && this.fieldLabel.length) {
11145             
11146             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11147             
11148             cfg.cn = [
11149                 indicator,
11150                 {
11151                     tag: 'label',
11152                     'for' :  id,
11153                     cls : 'control-label col-form-label',
11154                     html : this.fieldLabel
11155
11156                 },
11157                 {
11158                     cls : "", 
11159                     cn: [
11160                         inputblock
11161                     ]
11162                 }
11163             ];
11164             
11165             var labelCfg = cfg.cn[1];
11166             var contentCfg = cfg.cn[2];
11167             
11168             if(this.indicatorpos == 'right'){
11169                 cfg.cn = [
11170                     {
11171                         tag: 'label',
11172                         'for' :  id,
11173                         cls : 'control-label col-form-label',
11174                         cn : [
11175                             {
11176                                 tag : 'span',
11177                                 html : this.fieldLabel
11178                             },
11179                             indicator
11180                         ]
11181                     },
11182                     {
11183                         cls : "",
11184                         cn: [
11185                             inputblock
11186                         ]
11187                     }
11188
11189                 ];
11190                 
11191                 labelCfg = cfg.cn[0];
11192                 contentCfg = cfg.cn[1];
11193             
11194             }
11195             
11196             if(this.labelWidth > 12){
11197                 labelCfg.style = "width: " + this.labelWidth + 'px';
11198             }
11199             
11200             if(this.labelWidth < 13 && this.labelmd == 0){
11201                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11202             }
11203             
11204             if(this.labellg > 0){
11205                 labelCfg.cls += ' col-lg-' + this.labellg;
11206                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11207             }
11208             
11209             if(this.labelmd > 0){
11210                 labelCfg.cls += ' col-md-' + this.labelmd;
11211                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11212             }
11213             
11214             if(this.labelsm > 0){
11215                 labelCfg.cls += ' col-sm-' + this.labelsm;
11216                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11217             }
11218             
11219             if(this.labelxs > 0){
11220                 labelCfg.cls += ' col-xs-' + this.labelxs;
11221                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11222             }
11223             
11224             
11225         } else if ( this.fieldLabel.length) {
11226                 
11227             
11228             
11229             cfg.cn = [
11230                 {
11231                     tag : 'i',
11232                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11233                     tooltip : 'This field is required',
11234                     style : this.allowBlank ? ' display:none' : '' 
11235                 },
11236                 {
11237                     tag: 'label',
11238                    //cls : 'input-group-addon',
11239                     html : this.fieldLabel
11240
11241                 },
11242
11243                inputblock
11244
11245            ];
11246            
11247            if(this.indicatorpos == 'right'){
11248        
11249                 cfg.cn = [
11250                     {
11251                         tag: 'label',
11252                        //cls : 'input-group-addon',
11253                         html : this.fieldLabel
11254
11255                     },
11256                     {
11257                         tag : 'i',
11258                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11259                         tooltip : 'This field is required',
11260                         style : this.allowBlank ? ' display:none' : '' 
11261                     },
11262
11263                    inputblock
11264
11265                ];
11266
11267             }
11268
11269         } else {
11270             
11271             cfg.cn = [
11272
11273                     inputblock
11274
11275             ];
11276                 
11277                 
11278         };
11279         
11280         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11281            cfg.cls += ' navbar-form';
11282         }
11283         
11284         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11285             // on BS4 we do this only if not form 
11286             cfg.cls += ' navbar-form';
11287             cfg.tag = 'li';
11288         }
11289         
11290         return cfg;
11291         
11292     },
11293     /**
11294      * return the real input element.
11295      */
11296     inputEl: function ()
11297     {
11298         return this.el.select('input.form-control',true).first();
11299     },
11300     
11301     tooltipEl : function()
11302     {
11303         return this.inputEl();
11304     },
11305     
11306     indicatorEl : function()
11307     {
11308         if (Roo.bootstrap.version == 4) {
11309             return false; // not enabled in v4 yet.
11310         }
11311         
11312         var indicator = this.el.select('i.roo-required-indicator',true).first();
11313         
11314         if(!indicator){
11315             return false;
11316         }
11317         
11318         return indicator;
11319         
11320     },
11321     
11322     setDisabled : function(v)
11323     {
11324         var i  = this.inputEl().dom;
11325         if (!v) {
11326             i.removeAttribute('disabled');
11327             return;
11328             
11329         }
11330         i.setAttribute('disabled','true');
11331     },
11332     initEvents : function()
11333     {
11334           
11335         this.inputEl().on("keydown" , this.fireKey,  this);
11336         this.inputEl().on("focus", this.onFocus,  this);
11337         this.inputEl().on("blur", this.onBlur,  this);
11338         
11339         this.inputEl().relayEvent('keyup', this);
11340         this.inputEl().relayEvent('paste', this);
11341         
11342         this.indicator = this.indicatorEl();
11343         
11344         if(this.indicator){
11345             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11346         }
11347  
11348         // reference to original value for reset
11349         this.originalValue = this.getValue();
11350         //Roo.form.TextField.superclass.initEvents.call(this);
11351         if(this.validationEvent == 'keyup'){
11352             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11353             this.inputEl().on('keyup', this.filterValidation, this);
11354         }
11355         else if(this.validationEvent !== false){
11356             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11357         }
11358         
11359         if(this.selectOnFocus){
11360             this.on("focus", this.preFocus, this);
11361             
11362         }
11363         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11364             this.inputEl().on("keypress", this.filterKeys, this);
11365         } else {
11366             this.inputEl().relayEvent('keypress', this);
11367         }
11368        /* if(this.grow){
11369             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11370             this.el.on("click", this.autoSize,  this);
11371         }
11372         */
11373         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11374             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11375         }
11376         
11377         if (typeof(this.before) == 'object') {
11378             this.before.render(this.el.select('.roo-input-before',true).first());
11379         }
11380         if (typeof(this.after) == 'object') {
11381             this.after.render(this.el.select('.roo-input-after',true).first());
11382         }
11383         
11384         this.inputEl().on('change', this.onChange, this);
11385         
11386     },
11387     filterValidation : function(e){
11388         if(!e.isNavKeyPress()){
11389             this.validationTask.delay(this.validationDelay);
11390         }
11391     },
11392      /**
11393      * Validates the field value
11394      * @return {Boolean} True if the value is valid, else false
11395      */
11396     validate : function(){
11397         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11398         if(this.disabled || this.validateValue(this.getRawValue())){
11399             this.markValid();
11400             return true;
11401         }
11402         
11403         this.markInvalid();
11404         return false;
11405     },
11406     
11407     
11408     /**
11409      * Validates a value according to the field's validation rules and marks the field as invalid
11410      * if the validation fails
11411      * @param {Mixed} value The value to validate
11412      * @return {Boolean} True if the value is valid, else false
11413      */
11414     validateValue : function(value)
11415     {
11416         if(this.getVisibilityEl().hasClass('hidden')){
11417             return true;
11418         }
11419         
11420         if(value.length < 1)  { // if it's blank
11421             if(this.allowBlank){
11422                 return true;
11423             }
11424             return false;
11425         }
11426         
11427         if(value.length < this.minLength){
11428             return false;
11429         }
11430         if(value.length > this.maxLength){
11431             return false;
11432         }
11433         if(this.vtype){
11434             var vt = Roo.form.VTypes;
11435             if(!vt[this.vtype](value, this)){
11436                 return false;
11437             }
11438         }
11439         if(typeof this.validator == "function"){
11440             var msg = this.validator(value);
11441             if(msg !== true){
11442                 return false;
11443             }
11444             if (typeof(msg) == 'string') {
11445                 this.invalidText = msg;
11446             }
11447         }
11448         
11449         if(this.regex && !this.regex.test(value)){
11450             return false;
11451         }
11452         
11453         return true;
11454     },
11455     
11456      // private
11457     fireKey : function(e){
11458         //Roo.log('field ' + e.getKey());
11459         if(e.isNavKeyPress()){
11460             this.fireEvent("specialkey", this, e);
11461         }
11462     },
11463     focus : function (selectText){
11464         if(this.rendered){
11465             this.inputEl().focus();
11466             if(selectText === true){
11467                 this.inputEl().dom.select();
11468             }
11469         }
11470         return this;
11471     } ,
11472     
11473     onFocus : function(){
11474         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11475            // this.el.addClass(this.focusClass);
11476         }
11477         if(!this.hasFocus){
11478             this.hasFocus = true;
11479             this.startValue = this.getValue();
11480             this.fireEvent("focus", this);
11481         }
11482     },
11483     
11484     beforeBlur : Roo.emptyFn,
11485
11486     
11487     // private
11488     onBlur : function(){
11489         this.beforeBlur();
11490         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11491             //this.el.removeClass(this.focusClass);
11492         }
11493         this.hasFocus = false;
11494         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11495             this.validate();
11496         }
11497         var v = this.getValue();
11498         if(String(v) !== String(this.startValue)){
11499             this.fireEvent('change', this, v, this.startValue);
11500         }
11501         this.fireEvent("blur", this);
11502     },
11503     
11504     onChange : function(e)
11505     {
11506         var v = this.getValue();
11507         if(String(v) !== String(this.startValue)){
11508             this.fireEvent('change', this, v, this.startValue);
11509         }
11510         
11511     },
11512     
11513     /**
11514      * Resets the current field value to the originally loaded value and clears any validation messages
11515      */
11516     reset : function(){
11517         this.setValue(this.originalValue);
11518         this.validate();
11519     },
11520      /**
11521      * Returns the name of the field
11522      * @return {Mixed} name The name field
11523      */
11524     getName: function(){
11525         return this.name;
11526     },
11527      /**
11528      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11529      * @return {Mixed} value The field value
11530      */
11531     getValue : function(){
11532         
11533         var v = this.inputEl().getValue();
11534         
11535         return v;
11536     },
11537     /**
11538      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11539      * @return {Mixed} value The field value
11540      */
11541     getRawValue : function(){
11542         var v = this.inputEl().getValue();
11543         
11544         return v;
11545     },
11546     
11547     /**
11548      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11549      * @param {Mixed} value The value to set
11550      */
11551     setRawValue : function(v){
11552         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11553     },
11554     
11555     selectText : function(start, end){
11556         var v = this.getRawValue();
11557         if(v.length > 0){
11558             start = start === undefined ? 0 : start;
11559             end = end === undefined ? v.length : end;
11560             var d = this.inputEl().dom;
11561             if(d.setSelectionRange){
11562                 d.setSelectionRange(start, end);
11563             }else if(d.createTextRange){
11564                 var range = d.createTextRange();
11565                 range.moveStart("character", start);
11566                 range.moveEnd("character", v.length-end);
11567                 range.select();
11568             }
11569         }
11570     },
11571     
11572     /**
11573      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11574      * @param {Mixed} value The value to set
11575      */
11576     setValue : function(v){
11577         this.value = v;
11578         if(this.rendered){
11579             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11580             this.validate();
11581         }
11582     },
11583     
11584     /*
11585     processValue : function(value){
11586         if(this.stripCharsRe){
11587             var newValue = value.replace(this.stripCharsRe, '');
11588             if(newValue !== value){
11589                 this.setRawValue(newValue);
11590                 return newValue;
11591             }
11592         }
11593         return value;
11594     },
11595   */
11596     preFocus : function(){
11597         
11598         if(this.selectOnFocus){
11599             this.inputEl().dom.select();
11600         }
11601     },
11602     filterKeys : function(e){
11603         var k = e.getKey();
11604         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11605             return;
11606         }
11607         var c = e.getCharCode(), cc = String.fromCharCode(c);
11608         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11609             return;
11610         }
11611         if(!this.maskRe.test(cc)){
11612             e.stopEvent();
11613         }
11614     },
11615      /**
11616      * Clear any invalid styles/messages for this field
11617      */
11618     clearInvalid : function(){
11619         
11620         if(!this.el || this.preventMark){ // not rendered
11621             return;
11622         }
11623         
11624         
11625         this.el.removeClass([this.invalidClass, 'is-invalid']);
11626         
11627         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11628             
11629             var feedback = this.el.select('.form-control-feedback', true).first();
11630             
11631             if(feedback){
11632                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11633             }
11634             
11635         }
11636         
11637         if(this.indicator){
11638             this.indicator.removeClass('visible');
11639             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11640         }
11641         
11642         this.fireEvent('valid', this);
11643     },
11644     
11645      /**
11646      * Mark this field as valid
11647      */
11648     markValid : function()
11649     {
11650         if(!this.el  || this.preventMark){ // not rendered...
11651             return;
11652         }
11653         
11654         this.el.removeClass([this.invalidClass, this.validClass]);
11655         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11656
11657         var feedback = this.el.select('.form-control-feedback', true).first();
11658             
11659         if(feedback){
11660             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11661         }
11662         
11663         if(this.indicator){
11664             this.indicator.removeClass('visible');
11665             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11666         }
11667         
11668         if(this.disabled){
11669             return;
11670         }
11671         
11672            
11673         if(this.allowBlank && !this.getRawValue().length){
11674             return;
11675         }
11676         if (Roo.bootstrap.version == 3) {
11677             this.el.addClass(this.validClass);
11678         } else {
11679             this.inputEl().addClass('is-valid');
11680         }
11681
11682         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11683             
11684             var feedback = this.el.select('.form-control-feedback', true).first();
11685             
11686             if(feedback){
11687                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11688                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11689             }
11690             
11691         }
11692         
11693         this.fireEvent('valid', this);
11694     },
11695     
11696      /**
11697      * Mark this field as invalid
11698      * @param {String} msg The validation message
11699      */
11700     markInvalid : function(msg)
11701     {
11702         if(!this.el  || this.preventMark){ // not rendered
11703             return;
11704         }
11705         
11706         this.el.removeClass([this.invalidClass, this.validClass]);
11707         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11708         
11709         var feedback = this.el.select('.form-control-feedback', true).first();
11710             
11711         if(feedback){
11712             this.el.select('.form-control-feedback', true).first().removeClass(
11713                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11714         }
11715
11716         if(this.disabled){
11717             return;
11718         }
11719         
11720         if(this.allowBlank && !this.getRawValue().length){
11721             return;
11722         }
11723         
11724         if(this.indicator){
11725             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11726             this.indicator.addClass('visible');
11727         }
11728         if (Roo.bootstrap.version == 3) {
11729             this.el.addClass(this.invalidClass);
11730         } else {
11731             this.inputEl().addClass('is-invalid');
11732         }
11733         
11734         
11735         
11736         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11737             
11738             var feedback = this.el.select('.form-control-feedback', true).first();
11739             
11740             if(feedback){
11741                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11742                 
11743                 if(this.getValue().length || this.forceFeedback){
11744                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11745                 }
11746                 
11747             }
11748             
11749         }
11750         
11751         this.fireEvent('invalid', this, msg);
11752     },
11753     // private
11754     SafariOnKeyDown : function(event)
11755     {
11756         // this is a workaround for a password hang bug on chrome/ webkit.
11757         if (this.inputEl().dom.type != 'password') {
11758             return;
11759         }
11760         
11761         var isSelectAll = false;
11762         
11763         if(this.inputEl().dom.selectionEnd > 0){
11764             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11765         }
11766         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11767             event.preventDefault();
11768             this.setValue('');
11769             return;
11770         }
11771         
11772         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11773             
11774             event.preventDefault();
11775             // this is very hacky as keydown always get's upper case.
11776             //
11777             var cc = String.fromCharCode(event.getCharCode());
11778             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11779             
11780         }
11781     },
11782     adjustWidth : function(tag, w){
11783         tag = tag.toLowerCase();
11784         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11785             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11786                 if(tag == 'input'){
11787                     return w + 2;
11788                 }
11789                 if(tag == 'textarea'){
11790                     return w-2;
11791                 }
11792             }else if(Roo.isOpera){
11793                 if(tag == 'input'){
11794                     return w + 2;
11795                 }
11796                 if(tag == 'textarea'){
11797                     return w-2;
11798                 }
11799             }
11800         }
11801         return w;
11802     },
11803     
11804     setFieldLabel : function(v)
11805     {
11806         if(!this.rendered){
11807             return;
11808         }
11809         
11810         if(this.indicatorEl()){
11811             var ar = this.el.select('label > span',true);
11812             
11813             if (ar.elements.length) {
11814                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11815                 this.fieldLabel = v;
11816                 return;
11817             }
11818             
11819             var br = this.el.select('label',true);
11820             
11821             if(br.elements.length) {
11822                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11823                 this.fieldLabel = v;
11824                 return;
11825             }
11826             
11827             Roo.log('Cannot Found any of label > span || label in input');
11828             return;
11829         }
11830         
11831         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11832         this.fieldLabel = v;
11833         
11834         
11835     }
11836 });
11837
11838  
11839 /*
11840  * - LGPL
11841  *
11842  * Input
11843  * 
11844  */
11845
11846 /**
11847  * @class Roo.bootstrap.TextArea
11848  * @extends Roo.bootstrap.Input
11849  * Bootstrap TextArea class
11850  * @cfg {Number} cols Specifies the visible width of a text area
11851  * @cfg {Number} rows Specifies the visible number of lines in a text area
11852  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11853  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11854  * @cfg {string} html text
11855  * 
11856  * @constructor
11857  * Create a new TextArea
11858  * @param {Object} config The config object
11859  */
11860
11861 Roo.bootstrap.TextArea = function(config){
11862     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11863    
11864 };
11865
11866 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11867      
11868     cols : false,
11869     rows : 5,
11870     readOnly : false,
11871     warp : 'soft',
11872     resize : false,
11873     value: false,
11874     html: false,
11875     
11876     getAutoCreate : function(){
11877         
11878         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11879         
11880         var id = Roo.id();
11881         
11882         var cfg = {};
11883         
11884         if(this.inputType != 'hidden'){
11885             cfg.cls = 'form-group' //input-group
11886         }
11887         
11888         var input =  {
11889             tag: 'textarea',
11890             id : id,
11891             warp : this.warp,
11892             rows : this.rows,
11893             value : this.value || '',
11894             html: this.html || '',
11895             cls : 'form-control',
11896             placeholder : this.placeholder || '' 
11897             
11898         };
11899         
11900         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11901             input.maxLength = this.maxLength;
11902         }
11903         
11904         if(this.resize){
11905             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11906         }
11907         
11908         if(this.cols){
11909             input.cols = this.cols;
11910         }
11911         
11912         if (this.readOnly) {
11913             input.readonly = true;
11914         }
11915         
11916         if (this.name) {
11917             input.name = this.name;
11918         }
11919         
11920         if (this.size) {
11921             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11922         }
11923         
11924         var settings=this;
11925         ['xs','sm','md','lg'].map(function(size){
11926             if (settings[size]) {
11927                 cfg.cls += ' col-' + size + '-' + settings[size];
11928             }
11929         });
11930         
11931         var inputblock = input;
11932         
11933         if(this.hasFeedback && !this.allowBlank){
11934             
11935             var feedback = {
11936                 tag: 'span',
11937                 cls: 'glyphicon form-control-feedback'
11938             };
11939
11940             inputblock = {
11941                 cls : 'has-feedback',
11942                 cn :  [
11943                     input,
11944                     feedback
11945                 ] 
11946             };  
11947         }
11948         
11949         
11950         if (this.before || this.after) {
11951             
11952             inputblock = {
11953                 cls : 'input-group',
11954                 cn :  [] 
11955             };
11956             if (this.before) {
11957                 inputblock.cn.push({
11958                     tag :'span',
11959                     cls : 'input-group-addon',
11960                     html : this.before
11961                 });
11962             }
11963             
11964             inputblock.cn.push(input);
11965             
11966             if(this.hasFeedback && !this.allowBlank){
11967                 inputblock.cls += ' has-feedback';
11968                 inputblock.cn.push(feedback);
11969             }
11970             
11971             if (this.after) {
11972                 inputblock.cn.push({
11973                     tag :'span',
11974                     cls : 'input-group-addon',
11975                     html : this.after
11976                 });
11977             }
11978             
11979         }
11980         
11981         if (align ==='left' && this.fieldLabel.length) {
11982             cfg.cn = [
11983                 {
11984                     tag: 'label',
11985                     'for' :  id,
11986                     cls : 'control-label',
11987                     html : this.fieldLabel
11988                 },
11989                 {
11990                     cls : "",
11991                     cn: [
11992                         inputblock
11993                     ]
11994                 }
11995
11996             ];
11997             
11998             if(this.labelWidth > 12){
11999                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12000             }
12001
12002             if(this.labelWidth < 13 && this.labelmd == 0){
12003                 this.labelmd = this.labelWidth;
12004             }
12005
12006             if(this.labellg > 0){
12007                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12008                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12009             }
12010
12011             if(this.labelmd > 0){
12012                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12013                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12014             }
12015
12016             if(this.labelsm > 0){
12017                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12018                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12019             }
12020
12021             if(this.labelxs > 0){
12022                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12023                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12024             }
12025             
12026         } else if ( this.fieldLabel.length) {
12027             cfg.cn = [
12028
12029                {
12030                    tag: 'label',
12031                    //cls : 'input-group-addon',
12032                    html : this.fieldLabel
12033
12034                },
12035
12036                inputblock
12037
12038            ];
12039
12040         } else {
12041
12042             cfg.cn = [
12043
12044                 inputblock
12045
12046             ];
12047                 
12048         }
12049         
12050         if (this.disabled) {
12051             input.disabled=true;
12052         }
12053         
12054         return cfg;
12055         
12056     },
12057     /**
12058      * return the real textarea element.
12059      */
12060     inputEl: function ()
12061     {
12062         return this.el.select('textarea.form-control',true).first();
12063     },
12064     
12065     /**
12066      * Clear any invalid styles/messages for this field
12067      */
12068     clearInvalid : function()
12069     {
12070         
12071         if(!this.el || this.preventMark){ // not rendered
12072             return;
12073         }
12074         
12075         var label = this.el.select('label', true).first();
12076         var icon = this.el.select('i.fa-star', true).first();
12077         
12078         if(label && icon){
12079             icon.remove();
12080         }
12081         this.el.removeClass( this.validClass);
12082         this.inputEl().removeClass('is-invalid');
12083          
12084         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12085             
12086             var feedback = this.el.select('.form-control-feedback', true).first();
12087             
12088             if(feedback){
12089                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12090             }
12091             
12092         }
12093         
12094         this.fireEvent('valid', this);
12095     },
12096     
12097      /**
12098      * Mark this field as valid
12099      */
12100     markValid : function()
12101     {
12102         if(!this.el  || this.preventMark){ // not rendered
12103             return;
12104         }
12105         
12106         this.el.removeClass([this.invalidClass, this.validClass]);
12107         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12108         
12109         var feedback = this.el.select('.form-control-feedback', true).first();
12110             
12111         if(feedback){
12112             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12113         }
12114
12115         if(this.disabled || this.allowBlank){
12116             return;
12117         }
12118         
12119         var label = this.el.select('label', true).first();
12120         var icon = this.el.select('i.fa-star', true).first();
12121         
12122         if(label && icon){
12123             icon.remove();
12124         }
12125         if (Roo.bootstrap.version == 3) {
12126             this.el.addClass(this.validClass);
12127         } else {
12128             this.inputEl().addClass('is-valid');
12129         }
12130         
12131         
12132         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12133             
12134             var feedback = this.el.select('.form-control-feedback', true).first();
12135             
12136             if(feedback){
12137                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12138                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12139             }
12140             
12141         }
12142         
12143         this.fireEvent('valid', this);
12144     },
12145     
12146      /**
12147      * Mark this field as invalid
12148      * @param {String} msg The validation message
12149      */
12150     markInvalid : function(msg)
12151     {
12152         if(!this.el  || this.preventMark){ // not rendered
12153             return;
12154         }
12155         
12156         this.el.removeClass([this.invalidClass, this.validClass]);
12157         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12158         
12159         var feedback = this.el.select('.form-control-feedback', true).first();
12160             
12161         if(feedback){
12162             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12163         }
12164
12165         if(this.disabled || this.allowBlank){
12166             return;
12167         }
12168         
12169         var label = this.el.select('label', true).first();
12170         var icon = this.el.select('i.fa-star', true).first();
12171         
12172         if(!this.getValue().length && label && !icon){
12173             this.el.createChild({
12174                 tag : 'i',
12175                 cls : 'text-danger fa fa-lg fa-star',
12176                 tooltip : 'This field is required',
12177                 style : 'margin-right:5px;'
12178             }, label, true);
12179         }
12180         
12181         if (Roo.bootstrap.version == 3) {
12182             this.el.addClass(this.invalidClass);
12183         } else {
12184             this.inputEl().addClass('is-invalid');
12185         }
12186         
12187         // fixme ... this may be depricated need to test..
12188         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12189             
12190             var feedback = this.el.select('.form-control-feedback', true).first();
12191             
12192             if(feedback){
12193                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12194                 
12195                 if(this.getValue().length || this.forceFeedback){
12196                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12197                 }
12198                 
12199             }
12200             
12201         }
12202         
12203         this.fireEvent('invalid', this, msg);
12204     }
12205 });
12206
12207  
12208 /*
12209  * - LGPL
12210  *
12211  * trigger field - base class for combo..
12212  * 
12213  */
12214  
12215 /**
12216  * @class Roo.bootstrap.TriggerField
12217  * @extends Roo.bootstrap.Input
12218  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12219  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12220  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12221  * for which you can provide a custom implementation.  For example:
12222  * <pre><code>
12223 var trigger = new Roo.bootstrap.TriggerField();
12224 trigger.onTriggerClick = myTriggerFn;
12225 trigger.applyTo('my-field');
12226 </code></pre>
12227  *
12228  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12229  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12230  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12231  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12232  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12233
12234  * @constructor
12235  * Create a new TriggerField.
12236  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12237  * to the base TextField)
12238  */
12239 Roo.bootstrap.TriggerField = function(config){
12240     this.mimicing = false;
12241     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12242 };
12243
12244 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12245     /**
12246      * @cfg {String} triggerClass A CSS class to apply to the trigger
12247      */
12248      /**
12249      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12250      */
12251     hideTrigger:false,
12252
12253     /**
12254      * @cfg {Boolean} removable (true|false) special filter default false
12255      */
12256     removable : false,
12257     
12258     /** @cfg {Boolean} grow @hide */
12259     /** @cfg {Number} growMin @hide */
12260     /** @cfg {Number} growMax @hide */
12261
12262     /**
12263      * @hide 
12264      * @method
12265      */
12266     autoSize: Roo.emptyFn,
12267     // private
12268     monitorTab : true,
12269     // private
12270     deferHeight : true,
12271
12272     
12273     actionMode : 'wrap',
12274     
12275     caret : false,
12276     
12277     
12278     getAutoCreate : function(){
12279        
12280         var align = this.labelAlign || this.parentLabelAlign();
12281         
12282         var id = Roo.id();
12283         
12284         var cfg = {
12285             cls: 'form-group' //input-group
12286         };
12287         
12288         
12289         var input =  {
12290             tag: 'input',
12291             id : id,
12292             type : this.inputType,
12293             cls : 'form-control',
12294             autocomplete: 'new-password',
12295             placeholder : this.placeholder || '' 
12296             
12297         };
12298         if (this.name) {
12299             input.name = this.name;
12300         }
12301         if (this.size) {
12302             input.cls += ' input-' + this.size;
12303         }
12304         
12305         if (this.disabled) {
12306             input.disabled=true;
12307         }
12308         
12309         var inputblock = input;
12310         
12311         if(this.hasFeedback && !this.allowBlank){
12312             
12313             var feedback = {
12314                 tag: 'span',
12315                 cls: 'glyphicon form-control-feedback'
12316             };
12317             
12318             if(this.removable && !this.editable  ){
12319                 inputblock = {
12320                     cls : 'has-feedback',
12321                     cn :  [
12322                         inputblock,
12323                         {
12324                             tag: 'button',
12325                             html : 'x',
12326                             cls : 'roo-combo-removable-btn close'
12327                         },
12328                         feedback
12329                     ] 
12330                 };
12331             } else {
12332                 inputblock = {
12333                     cls : 'has-feedback',
12334                     cn :  [
12335                         inputblock,
12336                         feedback
12337                     ] 
12338                 };
12339             }
12340
12341         } else {
12342             if(this.removable && !this.editable ){
12343                 inputblock = {
12344                     cls : 'roo-removable',
12345                     cn :  [
12346                         inputblock,
12347                         {
12348                             tag: 'button',
12349                             html : 'x',
12350                             cls : 'roo-combo-removable-btn close'
12351                         }
12352                     ] 
12353                 };
12354             }
12355         }
12356         
12357         if (this.before || this.after) {
12358             
12359             inputblock = {
12360                 cls : 'input-group',
12361                 cn :  [] 
12362             };
12363             if (this.before) {
12364                 inputblock.cn.push({
12365                     tag :'span',
12366                     cls : 'input-group-addon input-group-prepend input-group-text',
12367                     html : this.before
12368                 });
12369             }
12370             
12371             inputblock.cn.push(input);
12372             
12373             if(this.hasFeedback && !this.allowBlank){
12374                 inputblock.cls += ' has-feedback';
12375                 inputblock.cn.push(feedback);
12376             }
12377             
12378             if (this.after) {
12379                 inputblock.cn.push({
12380                     tag :'span',
12381                     cls : 'input-group-addon input-group-append input-group-text',
12382                     html : this.after
12383                 });
12384             }
12385             
12386         };
12387         
12388       
12389         
12390         var ibwrap = inputblock;
12391         
12392         if(this.multiple){
12393             ibwrap = {
12394                 tag: 'ul',
12395                 cls: 'roo-select2-choices',
12396                 cn:[
12397                     {
12398                         tag: 'li',
12399                         cls: 'roo-select2-search-field',
12400                         cn: [
12401
12402                             inputblock
12403                         ]
12404                     }
12405                 ]
12406             };
12407                 
12408         }
12409         
12410         var combobox = {
12411             cls: 'roo-select2-container input-group',
12412             cn: [
12413                  {
12414                     tag: 'input',
12415                     type : 'hidden',
12416                     cls: 'form-hidden-field'
12417                 },
12418                 ibwrap
12419             ]
12420         };
12421         
12422         if(!this.multiple && this.showToggleBtn){
12423             
12424             var caret = {
12425                         tag: 'span',
12426                         cls: 'caret'
12427              };
12428             if (this.caret != false) {
12429                 caret = {
12430                      tag: 'i',
12431                      cls: 'fa fa-' + this.caret
12432                 };
12433                 
12434             }
12435             
12436             combobox.cn.push({
12437                 tag :'span',
12438                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12439                 cn : [
12440                     Roo.bootstrap.version == 3 ? caret : '',
12441                     {
12442                         tag: 'span',
12443                         cls: 'combobox-clear',
12444                         cn  : [
12445                             {
12446                                 tag : 'i',
12447                                 cls: 'icon-remove'
12448                             }
12449                         ]
12450                     }
12451                 ]
12452
12453             })
12454         }
12455         
12456         if(this.multiple){
12457             combobox.cls += ' roo-select2-container-multi';
12458         }
12459          var indicator = {
12460             tag : 'i',
12461             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12462             tooltip : 'This field is required'
12463         };
12464         if (Roo.bootstrap.version == 4) {
12465             indicator = {
12466                 tag : 'i',
12467                 style : 'display:none'
12468             };
12469         }
12470         
12471         
12472         if (align ==='left' && this.fieldLabel.length) {
12473             
12474             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12475
12476             cfg.cn = [
12477                 indicator,
12478                 {
12479                     tag: 'label',
12480                     'for' :  id,
12481                     cls : 'control-label',
12482                     html : this.fieldLabel
12483
12484                 },
12485                 {
12486                     cls : "", 
12487                     cn: [
12488                         combobox
12489                     ]
12490                 }
12491
12492             ];
12493             
12494             var labelCfg = cfg.cn[1];
12495             var contentCfg = cfg.cn[2];
12496             
12497             if(this.indicatorpos == 'right'){
12498                 cfg.cn = [
12499                     {
12500                         tag: 'label',
12501                         'for' :  id,
12502                         cls : 'control-label',
12503                         cn : [
12504                             {
12505                                 tag : 'span',
12506                                 html : this.fieldLabel
12507                             },
12508                             indicator
12509                         ]
12510                     },
12511                     {
12512                         cls : "", 
12513                         cn: [
12514                             combobox
12515                         ]
12516                     }
12517
12518                 ];
12519                 
12520                 labelCfg = cfg.cn[0];
12521                 contentCfg = cfg.cn[1];
12522             }
12523             
12524             if(this.labelWidth > 12){
12525                 labelCfg.style = "width: " + this.labelWidth + 'px';
12526             }
12527             
12528             if(this.labelWidth < 13 && this.labelmd == 0){
12529                 this.labelmd = this.labelWidth;
12530             }
12531             
12532             if(this.labellg > 0){
12533                 labelCfg.cls += ' col-lg-' + this.labellg;
12534                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12535             }
12536             
12537             if(this.labelmd > 0){
12538                 labelCfg.cls += ' col-md-' + this.labelmd;
12539                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12540             }
12541             
12542             if(this.labelsm > 0){
12543                 labelCfg.cls += ' col-sm-' + this.labelsm;
12544                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12545             }
12546             
12547             if(this.labelxs > 0){
12548                 labelCfg.cls += ' col-xs-' + this.labelxs;
12549                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12550             }
12551             
12552         } else if ( this.fieldLabel.length) {
12553 //                Roo.log(" label");
12554             cfg.cn = [
12555                 indicator,
12556                {
12557                    tag: 'label',
12558                    //cls : 'input-group-addon',
12559                    html : this.fieldLabel
12560
12561                },
12562
12563                combobox
12564
12565             ];
12566             
12567             if(this.indicatorpos == 'right'){
12568                 
12569                 cfg.cn = [
12570                     {
12571                        tag: 'label',
12572                        cn : [
12573                            {
12574                                tag : 'span',
12575                                html : this.fieldLabel
12576                            },
12577                            indicator
12578                        ]
12579
12580                     },
12581                     combobox
12582
12583                 ];
12584
12585             }
12586
12587         } else {
12588             
12589 //                Roo.log(" no label && no align");
12590                 cfg = combobox
12591                      
12592                 
12593         }
12594         
12595         var settings=this;
12596         ['xs','sm','md','lg'].map(function(size){
12597             if (settings[size]) {
12598                 cfg.cls += ' col-' + size + '-' + settings[size];
12599             }
12600         });
12601         
12602         return cfg;
12603         
12604     },
12605     
12606     
12607     
12608     // private
12609     onResize : function(w, h){
12610 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12611 //        if(typeof w == 'number'){
12612 //            var x = w - this.trigger.getWidth();
12613 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12614 //            this.trigger.setStyle('left', x+'px');
12615 //        }
12616     },
12617
12618     // private
12619     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12620
12621     // private
12622     getResizeEl : function(){
12623         return this.inputEl();
12624     },
12625
12626     // private
12627     getPositionEl : function(){
12628         return this.inputEl();
12629     },
12630
12631     // private
12632     alignErrorIcon : function(){
12633         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12634     },
12635
12636     // private
12637     initEvents : function(){
12638         
12639         this.createList();
12640         
12641         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12642         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12643         if(!this.multiple && this.showToggleBtn){
12644             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12645             if(this.hideTrigger){
12646                 this.trigger.setDisplayed(false);
12647             }
12648             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12649         }
12650         
12651         if(this.multiple){
12652             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12653         }
12654         
12655         if(this.removable && !this.editable && !this.tickable){
12656             var close = this.closeTriggerEl();
12657             
12658             if(close){
12659                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12660                 close.on('click', this.removeBtnClick, this, close);
12661             }
12662         }
12663         
12664         //this.trigger.addClassOnOver('x-form-trigger-over');
12665         //this.trigger.addClassOnClick('x-form-trigger-click');
12666         
12667         //if(!this.width){
12668         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12669         //}
12670     },
12671     
12672     closeTriggerEl : function()
12673     {
12674         var close = this.el.select('.roo-combo-removable-btn', true).first();
12675         return close ? close : false;
12676     },
12677     
12678     removeBtnClick : function(e, h, el)
12679     {
12680         e.preventDefault();
12681         
12682         if(this.fireEvent("remove", this) !== false){
12683             this.reset();
12684             this.fireEvent("afterremove", this)
12685         }
12686     },
12687     
12688     createList : function()
12689     {
12690         this.list = Roo.get(document.body).createChild({
12691             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12692             cls: 'typeahead typeahead-long dropdown-menu shadow',
12693             style: 'display:none'
12694         });
12695         
12696         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12697         
12698     },
12699
12700     // private
12701     initTrigger : function(){
12702        
12703     },
12704
12705     // private
12706     onDestroy : function(){
12707         if(this.trigger){
12708             this.trigger.removeAllListeners();
12709           //  this.trigger.remove();
12710         }
12711         //if(this.wrap){
12712         //    this.wrap.remove();
12713         //}
12714         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12715     },
12716
12717     // private
12718     onFocus : function(){
12719         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12720         /*
12721         if(!this.mimicing){
12722             this.wrap.addClass('x-trigger-wrap-focus');
12723             this.mimicing = true;
12724             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12725             if(this.monitorTab){
12726                 this.el.on("keydown", this.checkTab, this);
12727             }
12728         }
12729         */
12730     },
12731
12732     // private
12733     checkTab : function(e){
12734         if(e.getKey() == e.TAB){
12735             this.triggerBlur();
12736         }
12737     },
12738
12739     // private
12740     onBlur : function(){
12741         // do nothing
12742     },
12743
12744     // private
12745     mimicBlur : function(e, t){
12746         /*
12747         if(!this.wrap.contains(t) && this.validateBlur()){
12748             this.triggerBlur();
12749         }
12750         */
12751     },
12752
12753     // private
12754     triggerBlur : function(){
12755         this.mimicing = false;
12756         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12757         if(this.monitorTab){
12758             this.el.un("keydown", this.checkTab, this);
12759         }
12760         //this.wrap.removeClass('x-trigger-wrap-focus');
12761         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12762     },
12763
12764     // private
12765     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12766     validateBlur : function(e, t){
12767         return true;
12768     },
12769
12770     // private
12771     onDisable : function(){
12772         this.inputEl().dom.disabled = true;
12773         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12774         //if(this.wrap){
12775         //    this.wrap.addClass('x-item-disabled');
12776         //}
12777     },
12778
12779     // private
12780     onEnable : function(){
12781         this.inputEl().dom.disabled = false;
12782         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12783         //if(this.wrap){
12784         //    this.el.removeClass('x-item-disabled');
12785         //}
12786     },
12787
12788     // private
12789     onShow : function(){
12790         var ae = this.getActionEl();
12791         
12792         if(ae){
12793             ae.dom.style.display = '';
12794             ae.dom.style.visibility = 'visible';
12795         }
12796     },
12797
12798     // private
12799     
12800     onHide : function(){
12801         var ae = this.getActionEl();
12802         ae.dom.style.display = 'none';
12803     },
12804
12805     /**
12806      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12807      * by an implementing function.
12808      * @method
12809      * @param {EventObject} e
12810      */
12811     onTriggerClick : Roo.emptyFn
12812 });
12813  
12814 /*
12815 * Licence: LGPL
12816 */
12817
12818 /**
12819  * @class Roo.bootstrap.CardUploader
12820  * @extends Roo.bootstrap.Button
12821  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12822  * @cfg {Number} errorTimeout default 3000
12823  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12824  * @cfg {Array}  html The button text.
12825
12826  *
12827  * @constructor
12828  * Create a new CardUploader
12829  * @param {Object} config The config object
12830  */
12831
12832 Roo.bootstrap.CardUploader = function(config){
12833     
12834  
12835     
12836     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12837     
12838     
12839     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12840         return r.data.id
12841      });
12842     
12843      this.addEvents({
12844          // raw events
12845         /**
12846          * @event preview
12847          * When a image is clicked on - and needs to display a slideshow or similar..
12848          * @param {Roo.bootstrap.Card} this
12849          * @param {Object} The image information data 
12850          *
12851          */
12852         'preview' : true,
12853          /**
12854          * @event download
12855          * When a the download link is clicked
12856          * @param {Roo.bootstrap.Card} this
12857          * @param {Object} The image information data  contains 
12858          */
12859         'download' : true
12860         
12861     });
12862 };
12863  
12864 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12865     
12866      
12867     errorTimeout : 3000,
12868      
12869     images : false,
12870    
12871     fileCollection : false,
12872     allowBlank : true,
12873     
12874     getAutoCreate : function()
12875     {
12876         
12877         var cfg =  {
12878             cls :'form-group' ,
12879             cn : [
12880                
12881                 {
12882                     tag: 'label',
12883                    //cls : 'input-group-addon',
12884                     html : this.fieldLabel
12885
12886                 },
12887
12888                 {
12889                     tag: 'input',
12890                     type : 'hidden',
12891                     name : this.name,
12892                     value : this.value,
12893                     cls : 'd-none  form-control'
12894                 },
12895                 
12896                 {
12897                     tag: 'input',
12898                     multiple : 'multiple',
12899                     type : 'file',
12900                     cls : 'd-none  roo-card-upload-selector'
12901                 },
12902                 
12903                 {
12904                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12905                 },
12906                 {
12907                     cls : 'card-columns roo-card-uploader-container'
12908                 }
12909
12910             ]
12911         };
12912            
12913          
12914         return cfg;
12915     },
12916     
12917     getChildContainer : function() /// what children are added to.
12918     {
12919         return this.containerEl;
12920     },
12921    
12922     getButtonContainer : function() /// what children are added to.
12923     {
12924         return this.el.select(".roo-card-uploader-button-container").first();
12925     },
12926    
12927     initEvents : function()
12928     {
12929         
12930         Roo.bootstrap.Input.prototype.initEvents.call(this);
12931         
12932         var t = this;
12933         this.addxtype({
12934             xns: Roo.bootstrap,
12935
12936             xtype : 'Button',
12937             container_method : 'getButtonContainer' ,            
12938             html :  this.html, // fix changable?
12939             cls : 'w-100 ',
12940             listeners : {
12941                 'click' : function(btn, e) {
12942                     t.onClick(e);
12943                 }
12944             }
12945         });
12946         
12947         
12948         
12949         
12950         this.urlAPI = (window.createObjectURL && window) || 
12951                                 (window.URL && URL.revokeObjectURL && URL) || 
12952                                 (window.webkitURL && webkitURL);
12953                         
12954          
12955          
12956          
12957         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12958         
12959         this.selectorEl.on('change', this.onFileSelected, this);
12960         if (this.images) {
12961             var t = this;
12962             this.images.forEach(function(img) {
12963                 t.addCard(img)
12964             });
12965             this.images = false;
12966         }
12967         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12968          
12969        
12970     },
12971     
12972    
12973     onClick : function(e)
12974     {
12975         e.preventDefault();
12976          
12977         this.selectorEl.dom.click();
12978          
12979     },
12980     
12981     onFileSelected : function(e)
12982     {
12983         e.preventDefault();
12984         
12985         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12986             return;
12987         }
12988         
12989         Roo.each(this.selectorEl.dom.files, function(file){    
12990             this.addFile(file);
12991         }, this);
12992          
12993     },
12994     
12995       
12996     
12997       
12998     
12999     addFile : function(file)
13000     {
13001            
13002         if(typeof(file) === 'string'){
13003             throw "Add file by name?"; // should not happen
13004             return;
13005         }
13006         
13007         if(!file || !this.urlAPI){
13008             return;
13009         }
13010         
13011         // file;
13012         // file.type;
13013         
13014         var _this = this;
13015         
13016         
13017         var url = _this.urlAPI.createObjectURL( file);
13018            
13019         this.addCard({
13020             id : Roo.bootstrap.CardUploader.ID--,
13021             is_uploaded : false,
13022             src : url,
13023             srcfile : file,
13024             title : file.name,
13025             mimetype : file.type,
13026             preview : false,
13027             is_deleted : 0
13028         });
13029         
13030     },
13031     
13032     /**
13033      * addCard - add an Attachment to the uploader
13034      * @param data - the data about the image to upload
13035      *
13036      * {
13037           id : 123
13038           title : "Title of file",
13039           is_uploaded : false,
13040           src : "http://.....",
13041           srcfile : { the File upload object },
13042           mimetype : file.type,
13043           preview : false,
13044           is_deleted : 0
13045           .. any other data...
13046         }
13047      *
13048      * 
13049     */
13050     
13051     addCard : function (data)
13052     {
13053         // hidden input element?
13054         // if the file is not an image...
13055         //then we need to use something other that and header_image
13056         var t = this;
13057         //   remove.....
13058         var footer = [
13059             {
13060                 xns : Roo.bootstrap,
13061                 xtype : 'CardFooter',
13062                  items: [
13063                     {
13064                         xns : Roo.bootstrap,
13065                         xtype : 'Element',
13066                         cls : 'd-flex',
13067                         items : [
13068                             
13069                             {
13070                                 xns : Roo.bootstrap,
13071                                 xtype : 'Button',
13072                                 html : String.format("<small>{0}</small>", data.title),
13073                                 cls : 'col-10 text-left',
13074                                 size: 'sm',
13075                                 weight: 'link',
13076                                 fa : 'download',
13077                                 listeners : {
13078                                     click : function() {
13079                                      
13080                                         t.fireEvent( "download", t, data );
13081                                     }
13082                                 }
13083                             },
13084                           
13085                             {
13086                                 xns : Roo.bootstrap,
13087                                 xtype : 'Button',
13088                                 style: 'max-height: 28px; ',
13089                                 size : 'sm',
13090                                 weight: 'danger',
13091                                 cls : 'col-2',
13092                                 fa : 'times',
13093                                 listeners : {
13094                                     click : function() {
13095                                         t.removeCard(data.id)
13096                                     }
13097                                 }
13098                             }
13099                         ]
13100                     }
13101                     
13102                 ] 
13103             }
13104             
13105         ];
13106         
13107         var cn = this.addxtype(
13108             {
13109                  
13110                 xns : Roo.bootstrap,
13111                 xtype : 'Card',
13112                 closeable : true,
13113                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13114                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13115                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13116                 data : data,
13117                 html : false,
13118                  
13119                 items : footer,
13120                 initEvents : function() {
13121                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13122                     var card = this;
13123                     this.imgEl = this.el.select('.card-img-top').first();
13124                     if (this.imgEl) {
13125                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13126                         this.imgEl.set({ 'pointer' : 'cursor' });
13127                                   
13128                     }
13129                     this.getCardFooter().addClass('p-1');
13130                     
13131                   
13132                 }
13133                 
13134             }
13135         );
13136         // dont' really need ot update items.
13137         // this.items.push(cn);
13138         this.fileCollection.add(cn);
13139         
13140         if (!data.srcfile) {
13141             this.updateInput();
13142             return;
13143         }
13144             
13145         var _t = this;
13146         var reader = new FileReader();
13147         reader.addEventListener("load", function() {  
13148             data.srcdata =  reader.result;
13149             _t.updateInput();
13150         });
13151         reader.readAsDataURL(data.srcfile);
13152         
13153         
13154         
13155     },
13156     removeCard : function(id)
13157     {
13158         
13159         var card  = this.fileCollection.get(id);
13160         card.data.is_deleted = 1;
13161         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13162         //this.fileCollection.remove(card);
13163         //this.items = this.items.filter(function(e) { return e != card });
13164         // dont' really need ot update items.
13165         card.el.dom.parentNode.removeChild(card.el.dom);
13166         this.updateInput();
13167
13168         
13169     },
13170     reset: function()
13171     {
13172         this.fileCollection.each(function(card) {
13173             if (card.el.dom && card.el.dom.parentNode) {
13174                 card.el.dom.parentNode.removeChild(card.el.dom);
13175             }
13176         });
13177         this.fileCollection.clear();
13178         this.updateInput();
13179     },
13180     
13181     updateInput : function()
13182     {
13183          var data = [];
13184         this.fileCollection.each(function(e) {
13185             data.push(e.data);
13186             
13187         });
13188         this.inputEl().dom.value = JSON.stringify(data);
13189         
13190         
13191         
13192     }
13193     
13194     
13195 });
13196
13197
13198 Roo.bootstrap.CardUploader.ID = -1;/*
13199  * Based on:
13200  * Ext JS Library 1.1.1
13201  * Copyright(c) 2006-2007, Ext JS, LLC.
13202  *
13203  * Originally Released Under LGPL - original licence link has changed is not relivant.
13204  *
13205  * Fork - LGPL
13206  * <script type="text/javascript">
13207  */
13208
13209
13210 /**
13211  * @class Roo.data.SortTypes
13212  * @singleton
13213  * Defines the default sorting (casting?) comparison functions used when sorting data.
13214  */
13215 Roo.data.SortTypes = {
13216     /**
13217      * Default sort that does nothing
13218      * @param {Mixed} s The value being converted
13219      * @return {Mixed} The comparison value
13220      */
13221     none : function(s){
13222         return s;
13223     },
13224     
13225     /**
13226      * The regular expression used to strip tags
13227      * @type {RegExp}
13228      * @property
13229      */
13230     stripTagsRE : /<\/?[^>]+>/gi,
13231     
13232     /**
13233      * Strips all HTML tags to sort on text only
13234      * @param {Mixed} s The value being converted
13235      * @return {String} The comparison value
13236      */
13237     asText : function(s){
13238         return String(s).replace(this.stripTagsRE, "");
13239     },
13240     
13241     /**
13242      * Strips all HTML tags to sort on text only - Case insensitive
13243      * @param {Mixed} s The value being converted
13244      * @return {String} The comparison value
13245      */
13246     asUCText : function(s){
13247         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13248     },
13249     
13250     /**
13251      * Case insensitive string
13252      * @param {Mixed} s The value being converted
13253      * @return {String} The comparison value
13254      */
13255     asUCString : function(s) {
13256         return String(s).toUpperCase();
13257     },
13258     
13259     /**
13260      * Date sorting
13261      * @param {Mixed} s The value being converted
13262      * @return {Number} The comparison value
13263      */
13264     asDate : function(s) {
13265         if(!s){
13266             return 0;
13267         }
13268         if(s instanceof Date){
13269             return s.getTime();
13270         }
13271         return Date.parse(String(s));
13272     },
13273     
13274     /**
13275      * Float sorting
13276      * @param {Mixed} s The value being converted
13277      * @return {Float} The comparison value
13278      */
13279     asFloat : function(s) {
13280         var val = parseFloat(String(s).replace(/,/g, ""));
13281         if(isNaN(val)) {
13282             val = 0;
13283         }
13284         return val;
13285     },
13286     
13287     /**
13288      * Integer sorting
13289      * @param {Mixed} s The value being converted
13290      * @return {Number} The comparison value
13291      */
13292     asInt : function(s) {
13293         var val = parseInt(String(s).replace(/,/g, ""));
13294         if(isNaN(val)) {
13295             val = 0;
13296         }
13297         return val;
13298     }
13299 };/*
13300  * Based on:
13301  * Ext JS Library 1.1.1
13302  * Copyright(c) 2006-2007, Ext JS, LLC.
13303  *
13304  * Originally Released Under LGPL - original licence link has changed is not relivant.
13305  *
13306  * Fork - LGPL
13307  * <script type="text/javascript">
13308  */
13309
13310 /**
13311 * @class Roo.data.Record
13312  * Instances of this class encapsulate both record <em>definition</em> information, and record
13313  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13314  * to access Records cached in an {@link Roo.data.Store} object.<br>
13315  * <p>
13316  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13317  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13318  * objects.<br>
13319  * <p>
13320  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13321  * @constructor
13322  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13323  * {@link #create}. The parameters are the same.
13324  * @param {Array} data An associative Array of data values keyed by the field name.
13325  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13326  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13327  * not specified an integer id is generated.
13328  */
13329 Roo.data.Record = function(data, id){
13330     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13331     this.data = data;
13332 };
13333
13334 /**
13335  * Generate a constructor for a specific record layout.
13336  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13337  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13338  * Each field definition object may contain the following properties: <ul>
13339  * <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,
13340  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13341  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13342  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13343  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13344  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13345  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13346  * this may be omitted.</p></li>
13347  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13348  * <ul><li>auto (Default, implies no conversion)</li>
13349  * <li>string</li>
13350  * <li>int</li>
13351  * <li>float</li>
13352  * <li>boolean</li>
13353  * <li>date</li></ul></p></li>
13354  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13355  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13356  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13357  * by the Reader into an object that will be stored in the Record. It is passed the
13358  * following parameters:<ul>
13359  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13360  * </ul></p></li>
13361  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13362  * </ul>
13363  * <br>usage:<br><pre><code>
13364 var TopicRecord = Roo.data.Record.create(
13365     {name: 'title', mapping: 'topic_title'},
13366     {name: 'author', mapping: 'username'},
13367     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13368     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13369     {name: 'lastPoster', mapping: 'user2'},
13370     {name: 'excerpt', mapping: 'post_text'}
13371 );
13372
13373 var myNewRecord = new TopicRecord({
13374     title: 'Do my job please',
13375     author: 'noobie',
13376     totalPosts: 1,
13377     lastPost: new Date(),
13378     lastPoster: 'Animal',
13379     excerpt: 'No way dude!'
13380 });
13381 myStore.add(myNewRecord);
13382 </code></pre>
13383  * @method create
13384  * @static
13385  */
13386 Roo.data.Record.create = function(o){
13387     var f = function(){
13388         f.superclass.constructor.apply(this, arguments);
13389     };
13390     Roo.extend(f, Roo.data.Record);
13391     var p = f.prototype;
13392     p.fields = new Roo.util.MixedCollection(false, function(field){
13393         return field.name;
13394     });
13395     for(var i = 0, len = o.length; i < len; i++){
13396         p.fields.add(new Roo.data.Field(o[i]));
13397     }
13398     f.getField = function(name){
13399         return p.fields.get(name);  
13400     };
13401     return f;
13402 };
13403
13404 Roo.data.Record.AUTO_ID = 1000;
13405 Roo.data.Record.EDIT = 'edit';
13406 Roo.data.Record.REJECT = 'reject';
13407 Roo.data.Record.COMMIT = 'commit';
13408
13409 Roo.data.Record.prototype = {
13410     /**
13411      * Readonly flag - true if this record has been modified.
13412      * @type Boolean
13413      */
13414     dirty : false,
13415     editing : false,
13416     error: null,
13417     modified: null,
13418
13419     // private
13420     join : function(store){
13421         this.store = store;
13422     },
13423
13424     /**
13425      * Set the named field to the specified value.
13426      * @param {String} name The name of the field to set.
13427      * @param {Object} value The value to set the field to.
13428      */
13429     set : function(name, value){
13430         if(this.data[name] == value){
13431             return;
13432         }
13433         this.dirty = true;
13434         if(!this.modified){
13435             this.modified = {};
13436         }
13437         if(typeof this.modified[name] == 'undefined'){
13438             this.modified[name] = this.data[name];
13439         }
13440         this.data[name] = value;
13441         if(!this.editing && this.store){
13442             this.store.afterEdit(this);
13443         }       
13444     },
13445
13446     /**
13447      * Get the value of the named field.
13448      * @param {String} name The name of the field to get the value of.
13449      * @return {Object} The value of the field.
13450      */
13451     get : function(name){
13452         return this.data[name]; 
13453     },
13454
13455     // private
13456     beginEdit : function(){
13457         this.editing = true;
13458         this.modified = {}; 
13459     },
13460
13461     // private
13462     cancelEdit : function(){
13463         this.editing = false;
13464         delete this.modified;
13465     },
13466
13467     // private
13468     endEdit : function(){
13469         this.editing = false;
13470         if(this.dirty && this.store){
13471             this.store.afterEdit(this);
13472         }
13473     },
13474
13475     /**
13476      * Usually called by the {@link Roo.data.Store} which owns the Record.
13477      * Rejects all changes made to the Record since either creation, or the last commit operation.
13478      * Modified fields are reverted to their original values.
13479      * <p>
13480      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13481      * of reject operations.
13482      */
13483     reject : function(){
13484         var m = this.modified;
13485         for(var n in m){
13486             if(typeof m[n] != "function"){
13487                 this.data[n] = m[n];
13488             }
13489         }
13490         this.dirty = false;
13491         delete this.modified;
13492         this.editing = false;
13493         if(this.store){
13494             this.store.afterReject(this);
13495         }
13496     },
13497
13498     /**
13499      * Usually called by the {@link Roo.data.Store} which owns the Record.
13500      * Commits all changes made to the Record since either creation, or the last commit operation.
13501      * <p>
13502      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13503      * of commit operations.
13504      */
13505     commit : function(){
13506         this.dirty = false;
13507         delete this.modified;
13508         this.editing = false;
13509         if(this.store){
13510             this.store.afterCommit(this);
13511         }
13512     },
13513
13514     // private
13515     hasError : function(){
13516         return this.error != null;
13517     },
13518
13519     // private
13520     clearError : function(){
13521         this.error = null;
13522     },
13523
13524     /**
13525      * Creates a copy of this record.
13526      * @param {String} id (optional) A new record id if you don't want to use this record's id
13527      * @return {Record}
13528      */
13529     copy : function(newId) {
13530         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13531     }
13532 };/*
13533  * Based on:
13534  * Ext JS Library 1.1.1
13535  * Copyright(c) 2006-2007, Ext JS, LLC.
13536  *
13537  * Originally Released Under LGPL - original licence link has changed is not relivant.
13538  *
13539  * Fork - LGPL
13540  * <script type="text/javascript">
13541  */
13542
13543
13544
13545 /**
13546  * @class Roo.data.Store
13547  * @extends Roo.util.Observable
13548  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13549  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13550  * <p>
13551  * 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
13552  * has no knowledge of the format of the data returned by the Proxy.<br>
13553  * <p>
13554  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13555  * instances from the data object. These records are cached and made available through accessor functions.
13556  * @constructor
13557  * Creates a new Store.
13558  * @param {Object} config A config object containing the objects needed for the Store to access data,
13559  * and read the data into Records.
13560  */
13561 Roo.data.Store = function(config){
13562     this.data = new Roo.util.MixedCollection(false);
13563     this.data.getKey = function(o){
13564         return o.id;
13565     };
13566     this.baseParams = {};
13567     // private
13568     this.paramNames = {
13569         "start" : "start",
13570         "limit" : "limit",
13571         "sort" : "sort",
13572         "dir" : "dir",
13573         "multisort" : "_multisort"
13574     };
13575
13576     if(config && config.data){
13577         this.inlineData = config.data;
13578         delete config.data;
13579     }
13580
13581     Roo.apply(this, config);
13582     
13583     if(this.reader){ // reader passed
13584         this.reader = Roo.factory(this.reader, Roo.data);
13585         this.reader.xmodule = this.xmodule || false;
13586         if(!this.recordType){
13587             this.recordType = this.reader.recordType;
13588         }
13589         if(this.reader.onMetaChange){
13590             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13591         }
13592     }
13593
13594     if(this.recordType){
13595         this.fields = this.recordType.prototype.fields;
13596     }
13597     this.modified = [];
13598
13599     this.addEvents({
13600         /**
13601          * @event datachanged
13602          * Fires when the data cache has changed, and a widget which is using this Store
13603          * as a Record cache should refresh its view.
13604          * @param {Store} this
13605          */
13606         datachanged : true,
13607         /**
13608          * @event metachange
13609          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13610          * @param {Store} this
13611          * @param {Object} meta The JSON metadata
13612          */
13613         metachange : true,
13614         /**
13615          * @event add
13616          * Fires when Records have been added to the Store
13617          * @param {Store} this
13618          * @param {Roo.data.Record[]} records The array of Records added
13619          * @param {Number} index The index at which the record(s) were added
13620          */
13621         add : true,
13622         /**
13623          * @event remove
13624          * Fires when a Record has been removed from the Store
13625          * @param {Store} this
13626          * @param {Roo.data.Record} record The Record that was removed
13627          * @param {Number} index The index at which the record was removed
13628          */
13629         remove : true,
13630         /**
13631          * @event update
13632          * Fires when a Record has been updated
13633          * @param {Store} this
13634          * @param {Roo.data.Record} record The Record that was updated
13635          * @param {String} operation The update operation being performed.  Value may be one of:
13636          * <pre><code>
13637  Roo.data.Record.EDIT
13638  Roo.data.Record.REJECT
13639  Roo.data.Record.COMMIT
13640          * </code></pre>
13641          */
13642         update : true,
13643         /**
13644          * @event clear
13645          * Fires when the data cache has been cleared.
13646          * @param {Store} this
13647          */
13648         clear : true,
13649         /**
13650          * @event beforeload
13651          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13652          * the load action will be canceled.
13653          * @param {Store} this
13654          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13655          */
13656         beforeload : true,
13657         /**
13658          * @event beforeloadadd
13659          * Fires after a new set of Records has been loaded.
13660          * @param {Store} this
13661          * @param {Roo.data.Record[]} records The Records that were loaded
13662          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13663          */
13664         beforeloadadd : true,
13665         /**
13666          * @event load
13667          * Fires after a new set of Records has been loaded, before they are added to the store.
13668          * @param {Store} this
13669          * @param {Roo.data.Record[]} records The Records that were loaded
13670          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13671          * @params {Object} return from reader
13672          */
13673         load : true,
13674         /**
13675          * @event loadexception
13676          * Fires if an exception occurs in the Proxy during loading.
13677          * Called with the signature of the Proxy's "loadexception" event.
13678          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13679          * 
13680          * @param {Proxy} 
13681          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13682          * @param {Object} load options 
13683          * @param {Object} jsonData from your request (normally this contains the Exception)
13684          */
13685         loadexception : true
13686     });
13687     
13688     if(this.proxy){
13689         this.proxy = Roo.factory(this.proxy, Roo.data);
13690         this.proxy.xmodule = this.xmodule || false;
13691         this.relayEvents(this.proxy,  ["loadexception"]);
13692     }
13693     this.sortToggle = {};
13694     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13695
13696     Roo.data.Store.superclass.constructor.call(this);
13697
13698     if(this.inlineData){
13699         this.loadData(this.inlineData);
13700         delete this.inlineData;
13701     }
13702 };
13703
13704 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13705      /**
13706     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13707     * without a remote query - used by combo/forms at present.
13708     */
13709     
13710     /**
13711     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13712     */
13713     /**
13714     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13715     */
13716     /**
13717     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13718     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13719     */
13720     /**
13721     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13722     * on any HTTP request
13723     */
13724     /**
13725     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13726     */
13727     /**
13728     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13729     */
13730     multiSort: false,
13731     /**
13732     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13733     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13734     */
13735     remoteSort : false,
13736
13737     /**
13738     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13739      * loaded or when a record is removed. (defaults to false).
13740     */
13741     pruneModifiedRecords : false,
13742
13743     // private
13744     lastOptions : null,
13745
13746     /**
13747      * Add Records to the Store and fires the add event.
13748      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13749      */
13750     add : function(records){
13751         records = [].concat(records);
13752         for(var i = 0, len = records.length; i < len; i++){
13753             records[i].join(this);
13754         }
13755         var index = this.data.length;
13756         this.data.addAll(records);
13757         this.fireEvent("add", this, records, index);
13758     },
13759
13760     /**
13761      * Remove a Record from the Store and fires the remove event.
13762      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13763      */
13764     remove : function(record){
13765         var index = this.data.indexOf(record);
13766         this.data.removeAt(index);
13767  
13768         if(this.pruneModifiedRecords){
13769             this.modified.remove(record);
13770         }
13771         this.fireEvent("remove", this, record, index);
13772     },
13773
13774     /**
13775      * Remove all Records from the Store and fires the clear event.
13776      */
13777     removeAll : function(){
13778         this.data.clear();
13779         if(this.pruneModifiedRecords){
13780             this.modified = [];
13781         }
13782         this.fireEvent("clear", this);
13783     },
13784
13785     /**
13786      * Inserts Records to the Store at the given index and fires the add event.
13787      * @param {Number} index The start index at which to insert the passed Records.
13788      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13789      */
13790     insert : function(index, records){
13791         records = [].concat(records);
13792         for(var i = 0, len = records.length; i < len; i++){
13793             this.data.insert(index, records[i]);
13794             records[i].join(this);
13795         }
13796         this.fireEvent("add", this, records, index);
13797     },
13798
13799     /**
13800      * Get the index within the cache of the passed Record.
13801      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13802      * @return {Number} The index of the passed Record. Returns -1 if not found.
13803      */
13804     indexOf : function(record){
13805         return this.data.indexOf(record);
13806     },
13807
13808     /**
13809      * Get the index within the cache of the Record with the passed id.
13810      * @param {String} id The id of the Record to find.
13811      * @return {Number} The index of the Record. Returns -1 if not found.
13812      */
13813     indexOfId : function(id){
13814         return this.data.indexOfKey(id);
13815     },
13816
13817     /**
13818      * Get the Record with the specified id.
13819      * @param {String} id The id of the Record to find.
13820      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13821      */
13822     getById : function(id){
13823         return this.data.key(id);
13824     },
13825
13826     /**
13827      * Get the Record at the specified index.
13828      * @param {Number} index The index of the Record to find.
13829      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13830      */
13831     getAt : function(index){
13832         return this.data.itemAt(index);
13833     },
13834
13835     /**
13836      * Returns a range of Records between specified indices.
13837      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13838      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13839      * @return {Roo.data.Record[]} An array of Records
13840      */
13841     getRange : function(start, end){
13842         return this.data.getRange(start, end);
13843     },
13844
13845     // private
13846     storeOptions : function(o){
13847         o = Roo.apply({}, o);
13848         delete o.callback;
13849         delete o.scope;
13850         this.lastOptions = o;
13851     },
13852
13853     /**
13854      * Loads the Record cache from the configured Proxy using the configured Reader.
13855      * <p>
13856      * If using remote paging, then the first load call must specify the <em>start</em>
13857      * and <em>limit</em> properties in the options.params property to establish the initial
13858      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13859      * <p>
13860      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13861      * and this call will return before the new data has been loaded. Perform any post-processing
13862      * in a callback function, or in a "load" event handler.</strong>
13863      * <p>
13864      * @param {Object} options An object containing properties which control loading options:<ul>
13865      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13866      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13867      * passed the following arguments:<ul>
13868      * <li>r : Roo.data.Record[]</li>
13869      * <li>options: Options object from the load call</li>
13870      * <li>success: Boolean success indicator</li></ul></li>
13871      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13872      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13873      * </ul>
13874      */
13875     load : function(options){
13876         options = options || {};
13877         if(this.fireEvent("beforeload", this, options) !== false){
13878             this.storeOptions(options);
13879             var p = Roo.apply(options.params || {}, this.baseParams);
13880             // if meta was not loaded from remote source.. try requesting it.
13881             if (!this.reader.metaFromRemote) {
13882                 p._requestMeta = 1;
13883             }
13884             if(this.sortInfo && this.remoteSort){
13885                 var pn = this.paramNames;
13886                 p[pn["sort"]] = this.sortInfo.field;
13887                 p[pn["dir"]] = this.sortInfo.direction;
13888             }
13889             if (this.multiSort) {
13890                 var pn = this.paramNames;
13891                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13892             }
13893             
13894             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13895         }
13896     },
13897
13898     /**
13899      * Reloads the Record cache from the configured Proxy using the configured Reader and
13900      * the options from the last load operation performed.
13901      * @param {Object} options (optional) An object containing properties which may override the options
13902      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13903      * the most recently used options are reused).
13904      */
13905     reload : function(options){
13906         this.load(Roo.applyIf(options||{}, this.lastOptions));
13907     },
13908
13909     // private
13910     // Called as a callback by the Reader during a load operation.
13911     loadRecords : function(o, options, success){
13912         if(!o || success === false){
13913             if(success !== false){
13914                 this.fireEvent("load", this, [], options, o);
13915             }
13916             if(options.callback){
13917                 options.callback.call(options.scope || this, [], options, false);
13918             }
13919             return;
13920         }
13921         // if data returned failure - throw an exception.
13922         if (o.success === false) {
13923             // show a message if no listener is registered.
13924             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13925                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13926             }
13927             // loadmask wil be hooked into this..
13928             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13929             return;
13930         }
13931         var r = o.records, t = o.totalRecords || r.length;
13932         
13933         this.fireEvent("beforeloadadd", this, r, options, o);
13934         
13935         if(!options || options.add !== true){
13936             if(this.pruneModifiedRecords){
13937                 this.modified = [];
13938             }
13939             for(var i = 0, len = r.length; i < len; i++){
13940                 r[i].join(this);
13941             }
13942             if(this.snapshot){
13943                 this.data = this.snapshot;
13944                 delete this.snapshot;
13945             }
13946             this.data.clear();
13947             this.data.addAll(r);
13948             this.totalLength = t;
13949             this.applySort();
13950             this.fireEvent("datachanged", this);
13951         }else{
13952             this.totalLength = Math.max(t, this.data.length+r.length);
13953             this.add(r);
13954         }
13955         
13956         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13957                 
13958             var e = new Roo.data.Record({});
13959
13960             e.set(this.parent.displayField, this.parent.emptyTitle);
13961             e.set(this.parent.valueField, '');
13962
13963             this.insert(0, e);
13964         }
13965             
13966         this.fireEvent("load", this, r, options, o);
13967         if(options.callback){
13968             options.callback.call(options.scope || this, r, options, true);
13969         }
13970     },
13971
13972
13973     /**
13974      * Loads data from a passed data block. A Reader which understands the format of the data
13975      * must have been configured in the constructor.
13976      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13977      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13978      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13979      */
13980     loadData : function(o, append){
13981         var r = this.reader.readRecords(o);
13982         this.loadRecords(r, {add: append}, true);
13983     },
13984     
13985      /**
13986      * using 'cn' the nested child reader read the child array into it's child stores.
13987      * @param {Object} rec The record with a 'children array
13988      */
13989     loadDataFromChildren : function(rec)
13990     {
13991         this.loadData(this.reader.toLoadData(rec));
13992     },
13993     
13994
13995     /**
13996      * Gets the number of cached records.
13997      * <p>
13998      * <em>If using paging, this may not be the total size of the dataset. If the data object
13999      * used by the Reader contains the dataset size, then the getTotalCount() function returns
14000      * the data set size</em>
14001      */
14002     getCount : function(){
14003         return this.data.length || 0;
14004     },
14005
14006     /**
14007      * Gets the total number of records in the dataset as returned by the server.
14008      * <p>
14009      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14010      * the dataset size</em>
14011      */
14012     getTotalCount : function(){
14013         return this.totalLength || 0;
14014     },
14015
14016     /**
14017      * Returns the sort state of the Store as an object with two properties:
14018      * <pre><code>
14019  field {String} The name of the field by which the Records are sorted
14020  direction {String} The sort order, "ASC" or "DESC"
14021      * </code></pre>
14022      */
14023     getSortState : function(){
14024         return this.sortInfo;
14025     },
14026
14027     // private
14028     applySort : function(){
14029         if(this.sortInfo && !this.remoteSort){
14030             var s = this.sortInfo, f = s.field;
14031             var st = this.fields.get(f).sortType;
14032             var fn = function(r1, r2){
14033                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14034                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14035             };
14036             this.data.sort(s.direction, fn);
14037             if(this.snapshot && this.snapshot != this.data){
14038                 this.snapshot.sort(s.direction, fn);
14039             }
14040         }
14041     },
14042
14043     /**
14044      * Sets the default sort column and order to be used by the next load operation.
14045      * @param {String} fieldName The name of the field to sort by.
14046      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14047      */
14048     setDefaultSort : function(field, dir){
14049         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14050     },
14051
14052     /**
14053      * Sort the Records.
14054      * If remote sorting is used, the sort is performed on the server, and the cache is
14055      * reloaded. If local sorting is used, the cache is sorted internally.
14056      * @param {String} fieldName The name of the field to sort by.
14057      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14058      */
14059     sort : function(fieldName, dir){
14060         var f = this.fields.get(fieldName);
14061         if(!dir){
14062             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14063             
14064             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14065                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14066             }else{
14067                 dir = f.sortDir;
14068             }
14069         }
14070         this.sortToggle[f.name] = dir;
14071         this.sortInfo = {field: f.name, direction: dir};
14072         if(!this.remoteSort){
14073             this.applySort();
14074             this.fireEvent("datachanged", this);
14075         }else{
14076             this.load(this.lastOptions);
14077         }
14078     },
14079
14080     /**
14081      * Calls the specified function for each of the Records in the cache.
14082      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14083      * Returning <em>false</em> aborts and exits the iteration.
14084      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14085      */
14086     each : function(fn, scope){
14087         this.data.each(fn, scope);
14088     },
14089
14090     /**
14091      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14092      * (e.g., during paging).
14093      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14094      */
14095     getModifiedRecords : function(){
14096         return this.modified;
14097     },
14098
14099     // private
14100     createFilterFn : function(property, value, anyMatch){
14101         if(!value.exec){ // not a regex
14102             value = String(value);
14103             if(value.length == 0){
14104                 return false;
14105             }
14106             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14107         }
14108         return function(r){
14109             return value.test(r.data[property]);
14110         };
14111     },
14112
14113     /**
14114      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14115      * @param {String} property A field on your records
14116      * @param {Number} start The record index to start at (defaults to 0)
14117      * @param {Number} end The last record index to include (defaults to length - 1)
14118      * @return {Number} The sum
14119      */
14120     sum : function(property, start, end){
14121         var rs = this.data.items, v = 0;
14122         start = start || 0;
14123         end = (end || end === 0) ? end : rs.length-1;
14124
14125         for(var i = start; i <= end; i++){
14126             v += (rs[i].data[property] || 0);
14127         }
14128         return v;
14129     },
14130
14131     /**
14132      * Filter the records by a specified property.
14133      * @param {String} field A field on your records
14134      * @param {String/RegExp} value Either a string that the field
14135      * should start with or a RegExp to test against the field
14136      * @param {Boolean} anyMatch True to match any part not just the beginning
14137      */
14138     filter : function(property, value, anyMatch){
14139         var fn = this.createFilterFn(property, value, anyMatch);
14140         return fn ? this.filterBy(fn) : this.clearFilter();
14141     },
14142
14143     /**
14144      * Filter by a function. The specified function will be called with each
14145      * record in this data source. If the function returns true the record is included,
14146      * otherwise it is filtered.
14147      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14148      * @param {Object} scope (optional) The scope of the function (defaults to this)
14149      */
14150     filterBy : function(fn, scope){
14151         this.snapshot = this.snapshot || this.data;
14152         this.data = this.queryBy(fn, scope||this);
14153         this.fireEvent("datachanged", this);
14154     },
14155
14156     /**
14157      * Query the records by a specified property.
14158      * @param {String} field A field on your records
14159      * @param {String/RegExp} value Either a string that the field
14160      * should start with or a RegExp to test against the field
14161      * @param {Boolean} anyMatch True to match any part not just the beginning
14162      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14163      */
14164     query : function(property, value, anyMatch){
14165         var fn = this.createFilterFn(property, value, anyMatch);
14166         return fn ? this.queryBy(fn) : this.data.clone();
14167     },
14168
14169     /**
14170      * Query by a function. The specified function will be called with each
14171      * record in this data source. If the function returns true the record is included
14172      * in the results.
14173      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14174      * @param {Object} scope (optional) The scope of the function (defaults to this)
14175       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14176      **/
14177     queryBy : function(fn, scope){
14178         var data = this.snapshot || this.data;
14179         return data.filterBy(fn, scope||this);
14180     },
14181
14182     /**
14183      * Collects unique values for a particular dataIndex from this store.
14184      * @param {String} dataIndex The property to collect
14185      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14186      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14187      * @return {Array} An array of the unique values
14188      **/
14189     collect : function(dataIndex, allowNull, bypassFilter){
14190         var d = (bypassFilter === true && this.snapshot) ?
14191                 this.snapshot.items : this.data.items;
14192         var v, sv, r = [], l = {};
14193         for(var i = 0, len = d.length; i < len; i++){
14194             v = d[i].data[dataIndex];
14195             sv = String(v);
14196             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14197                 l[sv] = true;
14198                 r[r.length] = v;
14199             }
14200         }
14201         return r;
14202     },
14203
14204     /**
14205      * Revert to a view of the Record cache with no filtering applied.
14206      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14207      */
14208     clearFilter : function(suppressEvent){
14209         if(this.snapshot && this.snapshot != this.data){
14210             this.data = this.snapshot;
14211             delete this.snapshot;
14212             if(suppressEvent !== true){
14213                 this.fireEvent("datachanged", this);
14214             }
14215         }
14216     },
14217
14218     // private
14219     afterEdit : function(record){
14220         if(this.modified.indexOf(record) == -1){
14221             this.modified.push(record);
14222         }
14223         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14224     },
14225     
14226     // private
14227     afterReject : function(record){
14228         this.modified.remove(record);
14229         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14230     },
14231
14232     // private
14233     afterCommit : function(record){
14234         this.modified.remove(record);
14235         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14236     },
14237
14238     /**
14239      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14240      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14241      */
14242     commitChanges : function(){
14243         var m = this.modified.slice(0);
14244         this.modified = [];
14245         for(var i = 0, len = m.length; i < len; i++){
14246             m[i].commit();
14247         }
14248     },
14249
14250     /**
14251      * Cancel outstanding changes on all changed records.
14252      */
14253     rejectChanges : function(){
14254         var m = this.modified.slice(0);
14255         this.modified = [];
14256         for(var i = 0, len = m.length; i < len; i++){
14257             m[i].reject();
14258         }
14259     },
14260
14261     onMetaChange : function(meta, rtype, o){
14262         this.recordType = rtype;
14263         this.fields = rtype.prototype.fields;
14264         delete this.snapshot;
14265         this.sortInfo = meta.sortInfo || this.sortInfo;
14266         this.modified = [];
14267         this.fireEvent('metachange', this, this.reader.meta);
14268     },
14269     
14270     moveIndex : function(data, type)
14271     {
14272         var index = this.indexOf(data);
14273         
14274         var newIndex = index + type;
14275         
14276         this.remove(data);
14277         
14278         this.insert(newIndex, data);
14279         
14280     }
14281 });/*
14282  * Based on:
14283  * Ext JS Library 1.1.1
14284  * Copyright(c) 2006-2007, Ext JS, LLC.
14285  *
14286  * Originally Released Under LGPL - original licence link has changed is not relivant.
14287  *
14288  * Fork - LGPL
14289  * <script type="text/javascript">
14290  */
14291
14292 /**
14293  * @class Roo.data.SimpleStore
14294  * @extends Roo.data.Store
14295  * Small helper class to make creating Stores from Array data easier.
14296  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14297  * @cfg {Array} fields An array of field definition objects, or field name strings.
14298  * @cfg {Object} an existing reader (eg. copied from another store)
14299  * @cfg {Array} data The multi-dimensional array of data
14300  * @constructor
14301  * @param {Object} config
14302  */
14303 Roo.data.SimpleStore = function(config)
14304 {
14305     Roo.data.SimpleStore.superclass.constructor.call(this, {
14306         isLocal : true,
14307         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14308                 id: config.id
14309             },
14310             Roo.data.Record.create(config.fields)
14311         ),
14312         proxy : new Roo.data.MemoryProxy(config.data)
14313     });
14314     this.load();
14315 };
14316 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14317  * Based on:
14318  * Ext JS Library 1.1.1
14319  * Copyright(c) 2006-2007, Ext JS, LLC.
14320  *
14321  * Originally Released Under LGPL - original licence link has changed is not relivant.
14322  *
14323  * Fork - LGPL
14324  * <script type="text/javascript">
14325  */
14326
14327 /**
14328 /**
14329  * @extends Roo.data.Store
14330  * @class Roo.data.JsonStore
14331  * Small helper class to make creating Stores for JSON data easier. <br/>
14332 <pre><code>
14333 var store = new Roo.data.JsonStore({
14334     url: 'get-images.php',
14335     root: 'images',
14336     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14337 });
14338 </code></pre>
14339  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14340  * JsonReader and HttpProxy (unless inline data is provided).</b>
14341  * @cfg {Array} fields An array of field definition objects, or field name strings.
14342  * @constructor
14343  * @param {Object} config
14344  */
14345 Roo.data.JsonStore = function(c){
14346     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14347         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14348         reader: new Roo.data.JsonReader(c, c.fields)
14349     }));
14350 };
14351 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14352  * Based on:
14353  * Ext JS Library 1.1.1
14354  * Copyright(c) 2006-2007, Ext JS, LLC.
14355  *
14356  * Originally Released Under LGPL - original licence link has changed is not relivant.
14357  *
14358  * Fork - LGPL
14359  * <script type="text/javascript">
14360  */
14361
14362  
14363 Roo.data.Field = function(config){
14364     if(typeof config == "string"){
14365         config = {name: config};
14366     }
14367     Roo.apply(this, config);
14368     
14369     if(!this.type){
14370         this.type = "auto";
14371     }
14372     
14373     var st = Roo.data.SortTypes;
14374     // named sortTypes are supported, here we look them up
14375     if(typeof this.sortType == "string"){
14376         this.sortType = st[this.sortType];
14377     }
14378     
14379     // set default sortType for strings and dates
14380     if(!this.sortType){
14381         switch(this.type){
14382             case "string":
14383                 this.sortType = st.asUCString;
14384                 break;
14385             case "date":
14386                 this.sortType = st.asDate;
14387                 break;
14388             default:
14389                 this.sortType = st.none;
14390         }
14391     }
14392
14393     // define once
14394     var stripRe = /[\$,%]/g;
14395
14396     // prebuilt conversion function for this field, instead of
14397     // switching every time we're reading a value
14398     if(!this.convert){
14399         var cv, dateFormat = this.dateFormat;
14400         switch(this.type){
14401             case "":
14402             case "auto":
14403             case undefined:
14404                 cv = function(v){ return v; };
14405                 break;
14406             case "string":
14407                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14408                 break;
14409             case "int":
14410                 cv = function(v){
14411                     return v !== undefined && v !== null && v !== '' ?
14412                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14413                     };
14414                 break;
14415             case "float":
14416                 cv = function(v){
14417                     return v !== undefined && v !== null && v !== '' ?
14418                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14419                     };
14420                 break;
14421             case "bool":
14422             case "boolean":
14423                 cv = function(v){ return v === true || v === "true" || v == 1; };
14424                 break;
14425             case "date":
14426                 cv = function(v){
14427                     if(!v){
14428                         return '';
14429                     }
14430                     if(v instanceof Date){
14431                         return v;
14432                     }
14433                     if(dateFormat){
14434                         if(dateFormat == "timestamp"){
14435                             return new Date(v*1000);
14436                         }
14437                         return Date.parseDate(v, dateFormat);
14438                     }
14439                     var parsed = Date.parse(v);
14440                     return parsed ? new Date(parsed) : null;
14441                 };
14442              break;
14443             
14444         }
14445         this.convert = cv;
14446     }
14447 };
14448
14449 Roo.data.Field.prototype = {
14450     dateFormat: null,
14451     defaultValue: "",
14452     mapping: null,
14453     sortType : null,
14454     sortDir : "ASC"
14455 };/*
14456  * Based on:
14457  * Ext JS Library 1.1.1
14458  * Copyright(c) 2006-2007, Ext JS, LLC.
14459  *
14460  * Originally Released Under LGPL - original licence link has changed is not relivant.
14461  *
14462  * Fork - LGPL
14463  * <script type="text/javascript">
14464  */
14465  
14466 // Base class for reading structured data from a data source.  This class is intended to be
14467 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14468
14469 /**
14470  * @class Roo.data.DataReader
14471  * Base class for reading structured data from a data source.  This class is intended to be
14472  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14473  */
14474
14475 Roo.data.DataReader = function(meta, recordType){
14476     
14477     this.meta = meta;
14478     
14479     this.recordType = recordType instanceof Array ? 
14480         Roo.data.Record.create(recordType) : recordType;
14481 };
14482
14483 Roo.data.DataReader.prototype = {
14484     
14485     
14486     readerType : 'Data',
14487      /**
14488      * Create an empty record
14489      * @param {Object} data (optional) - overlay some values
14490      * @return {Roo.data.Record} record created.
14491      */
14492     newRow :  function(d) {
14493         var da =  {};
14494         this.recordType.prototype.fields.each(function(c) {
14495             switch( c.type) {
14496                 case 'int' : da[c.name] = 0; break;
14497                 case 'date' : da[c.name] = new Date(); break;
14498                 case 'float' : da[c.name] = 0.0; break;
14499                 case 'boolean' : da[c.name] = false; break;
14500                 default : da[c.name] = ""; break;
14501             }
14502             
14503         });
14504         return new this.recordType(Roo.apply(da, d));
14505     }
14506     
14507     
14508 };/*
14509  * Based on:
14510  * Ext JS Library 1.1.1
14511  * Copyright(c) 2006-2007, Ext JS, LLC.
14512  *
14513  * Originally Released Under LGPL - original licence link has changed is not relivant.
14514  *
14515  * Fork - LGPL
14516  * <script type="text/javascript">
14517  */
14518
14519 /**
14520  * @class Roo.data.DataProxy
14521  * @extends Roo.data.Observable
14522  * This class is an abstract base class for implementations which provide retrieval of
14523  * unformatted data objects.<br>
14524  * <p>
14525  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14526  * (of the appropriate type which knows how to parse the data object) to provide a block of
14527  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14528  * <p>
14529  * Custom implementations must implement the load method as described in
14530  * {@link Roo.data.HttpProxy#load}.
14531  */
14532 Roo.data.DataProxy = function(){
14533     this.addEvents({
14534         /**
14535          * @event beforeload
14536          * Fires before a network request is made to retrieve a data object.
14537          * @param {Object} This DataProxy object.
14538          * @param {Object} params The params parameter to the load function.
14539          */
14540         beforeload : true,
14541         /**
14542          * @event load
14543          * Fires before the load method's callback is called.
14544          * @param {Object} This DataProxy object.
14545          * @param {Object} o The data object.
14546          * @param {Object} arg The callback argument object passed to the load function.
14547          */
14548         load : true,
14549         /**
14550          * @event loadexception
14551          * Fires if an Exception occurs during data retrieval.
14552          * @param {Object} This DataProxy object.
14553          * @param {Object} o The data object.
14554          * @param {Object} arg The callback argument object passed to the load function.
14555          * @param {Object} e The Exception.
14556          */
14557         loadexception : true
14558     });
14559     Roo.data.DataProxy.superclass.constructor.call(this);
14560 };
14561
14562 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14563
14564     /**
14565      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14566      */
14567 /*
14568  * Based on:
14569  * Ext JS Library 1.1.1
14570  * Copyright(c) 2006-2007, Ext JS, LLC.
14571  *
14572  * Originally Released Under LGPL - original licence link has changed is not relivant.
14573  *
14574  * Fork - LGPL
14575  * <script type="text/javascript">
14576  */
14577 /**
14578  * @class Roo.data.MemoryProxy
14579  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14580  * to the Reader when its load method is called.
14581  * @constructor
14582  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14583  */
14584 Roo.data.MemoryProxy = function(data){
14585     if (data.data) {
14586         data = data.data;
14587     }
14588     Roo.data.MemoryProxy.superclass.constructor.call(this);
14589     this.data = data;
14590 };
14591
14592 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14593     
14594     /**
14595      * Load data from the requested source (in this case an in-memory
14596      * data object passed to the constructor), read the data object into
14597      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14598      * process that block using the passed callback.
14599      * @param {Object} params This parameter is not used by the MemoryProxy class.
14600      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14601      * object into a block of Roo.data.Records.
14602      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14603      * The function must be passed <ul>
14604      * <li>The Record block object</li>
14605      * <li>The "arg" argument from the load function</li>
14606      * <li>A boolean success indicator</li>
14607      * </ul>
14608      * @param {Object} scope The scope in which to call the callback
14609      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14610      */
14611     load : function(params, reader, callback, scope, arg){
14612         params = params || {};
14613         var result;
14614         try {
14615             result = reader.readRecords(params.data ? params.data :this.data);
14616         }catch(e){
14617             this.fireEvent("loadexception", this, arg, null, e);
14618             callback.call(scope, null, arg, false);
14619             return;
14620         }
14621         callback.call(scope, result, arg, true);
14622     },
14623     
14624     // private
14625     update : function(params, records){
14626         
14627     }
14628 });/*
14629  * Based on:
14630  * Ext JS Library 1.1.1
14631  * Copyright(c) 2006-2007, Ext JS, LLC.
14632  *
14633  * Originally Released Under LGPL - original licence link has changed is not relivant.
14634  *
14635  * Fork - LGPL
14636  * <script type="text/javascript">
14637  */
14638 /**
14639  * @class Roo.data.HttpProxy
14640  * @extends Roo.data.DataProxy
14641  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14642  * configured to reference a certain URL.<br><br>
14643  * <p>
14644  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14645  * from which the running page was served.<br><br>
14646  * <p>
14647  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14648  * <p>
14649  * Be aware that to enable the browser to parse an XML document, the server must set
14650  * the Content-Type header in the HTTP response to "text/xml".
14651  * @constructor
14652  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14653  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14654  * will be used to make the request.
14655  */
14656 Roo.data.HttpProxy = function(conn){
14657     Roo.data.HttpProxy.superclass.constructor.call(this);
14658     // is conn a conn config or a real conn?
14659     this.conn = conn;
14660     this.useAjax = !conn || !conn.events;
14661   
14662 };
14663
14664 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14665     // thse are take from connection...
14666     
14667     /**
14668      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14669      */
14670     /**
14671      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14672      * extra parameters to each request made by this object. (defaults to undefined)
14673      */
14674     /**
14675      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14676      *  to each request made by this object. (defaults to undefined)
14677      */
14678     /**
14679      * @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)
14680      */
14681     /**
14682      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14683      */
14684      /**
14685      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14686      * @type Boolean
14687      */
14688   
14689
14690     /**
14691      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14692      * @type Boolean
14693      */
14694     /**
14695      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14696      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14697      * a finer-grained basis than the DataProxy events.
14698      */
14699     getConnection : function(){
14700         return this.useAjax ? Roo.Ajax : this.conn;
14701     },
14702
14703     /**
14704      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14705      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14706      * process that block using the passed callback.
14707      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14708      * for the request to the remote server.
14709      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14710      * object into a block of Roo.data.Records.
14711      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14712      * The function must be passed <ul>
14713      * <li>The Record block object</li>
14714      * <li>The "arg" argument from the load function</li>
14715      * <li>A boolean success indicator</li>
14716      * </ul>
14717      * @param {Object} scope The scope in which to call the callback
14718      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14719      */
14720     load : function(params, reader, callback, scope, arg){
14721         if(this.fireEvent("beforeload", this, params) !== false){
14722             var  o = {
14723                 params : params || {},
14724                 request: {
14725                     callback : callback,
14726                     scope : scope,
14727                     arg : arg
14728                 },
14729                 reader: reader,
14730                 callback : this.loadResponse,
14731                 scope: this
14732             };
14733             if(this.useAjax){
14734                 Roo.applyIf(o, this.conn);
14735                 if(this.activeRequest){
14736                     Roo.Ajax.abort(this.activeRequest);
14737                 }
14738                 this.activeRequest = Roo.Ajax.request(o);
14739             }else{
14740                 this.conn.request(o);
14741             }
14742         }else{
14743             callback.call(scope||this, null, arg, false);
14744         }
14745     },
14746
14747     // private
14748     loadResponse : function(o, success, response){
14749         delete this.activeRequest;
14750         if(!success){
14751             this.fireEvent("loadexception", this, o, response);
14752             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14753             return;
14754         }
14755         var result;
14756         try {
14757             result = o.reader.read(response);
14758         }catch(e){
14759             this.fireEvent("loadexception", this, o, response, e);
14760             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14761             return;
14762         }
14763         
14764         this.fireEvent("load", this, o, o.request.arg);
14765         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14766     },
14767
14768     // private
14769     update : function(dataSet){
14770
14771     },
14772
14773     // private
14774     updateResponse : function(dataSet){
14775
14776     }
14777 });/*
14778  * Based on:
14779  * Ext JS Library 1.1.1
14780  * Copyright(c) 2006-2007, Ext JS, LLC.
14781  *
14782  * Originally Released Under LGPL - original licence link has changed is not relivant.
14783  *
14784  * Fork - LGPL
14785  * <script type="text/javascript">
14786  */
14787
14788 /**
14789  * @class Roo.data.ScriptTagProxy
14790  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14791  * other than the originating domain of the running page.<br><br>
14792  * <p>
14793  * <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
14794  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14795  * <p>
14796  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14797  * source code that is used as the source inside a &lt;script> tag.<br><br>
14798  * <p>
14799  * In order for the browser to process the returned data, the server must wrap the data object
14800  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14801  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14802  * depending on whether the callback name was passed:
14803  * <p>
14804  * <pre><code>
14805 boolean scriptTag = false;
14806 String cb = request.getParameter("callback");
14807 if (cb != null) {
14808     scriptTag = true;
14809     response.setContentType("text/javascript");
14810 } else {
14811     response.setContentType("application/x-json");
14812 }
14813 Writer out = response.getWriter();
14814 if (scriptTag) {
14815     out.write(cb + "(");
14816 }
14817 out.print(dataBlock.toJsonString());
14818 if (scriptTag) {
14819     out.write(");");
14820 }
14821 </pre></code>
14822  *
14823  * @constructor
14824  * @param {Object} config A configuration object.
14825  */
14826 Roo.data.ScriptTagProxy = function(config){
14827     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14828     Roo.apply(this, config);
14829     this.head = document.getElementsByTagName("head")[0];
14830 };
14831
14832 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14833
14834 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14835     /**
14836      * @cfg {String} url The URL from which to request the data object.
14837      */
14838     /**
14839      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14840      */
14841     timeout : 30000,
14842     /**
14843      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14844      * the server the name of the callback function set up by the load call to process the returned data object.
14845      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14846      * javascript output which calls this named function passing the data object as its only parameter.
14847      */
14848     callbackParam : "callback",
14849     /**
14850      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14851      * name to the request.
14852      */
14853     nocache : true,
14854
14855     /**
14856      * Load data from the configured URL, read the data object into
14857      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14858      * process that block using the passed callback.
14859      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14860      * for the request to the remote server.
14861      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14862      * object into a block of Roo.data.Records.
14863      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14864      * The function must be passed <ul>
14865      * <li>The Record block object</li>
14866      * <li>The "arg" argument from the load function</li>
14867      * <li>A boolean success indicator</li>
14868      * </ul>
14869      * @param {Object} scope The scope in which to call the callback
14870      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14871      */
14872     load : function(params, reader, callback, scope, arg){
14873         if(this.fireEvent("beforeload", this, params) !== false){
14874
14875             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14876
14877             var url = this.url;
14878             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14879             if(this.nocache){
14880                 url += "&_dc=" + (new Date().getTime());
14881             }
14882             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14883             var trans = {
14884                 id : transId,
14885                 cb : "stcCallback"+transId,
14886                 scriptId : "stcScript"+transId,
14887                 params : params,
14888                 arg : arg,
14889                 url : url,
14890                 callback : callback,
14891                 scope : scope,
14892                 reader : reader
14893             };
14894             var conn = this;
14895
14896             window[trans.cb] = function(o){
14897                 conn.handleResponse(o, trans);
14898             };
14899
14900             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14901
14902             if(this.autoAbort !== false){
14903                 this.abort();
14904             }
14905
14906             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14907
14908             var script = document.createElement("script");
14909             script.setAttribute("src", url);
14910             script.setAttribute("type", "text/javascript");
14911             script.setAttribute("id", trans.scriptId);
14912             this.head.appendChild(script);
14913
14914             this.trans = trans;
14915         }else{
14916             callback.call(scope||this, null, arg, false);
14917         }
14918     },
14919
14920     // private
14921     isLoading : function(){
14922         return this.trans ? true : false;
14923     },
14924
14925     /**
14926      * Abort the current server request.
14927      */
14928     abort : function(){
14929         if(this.isLoading()){
14930             this.destroyTrans(this.trans);
14931         }
14932     },
14933
14934     // private
14935     destroyTrans : function(trans, isLoaded){
14936         this.head.removeChild(document.getElementById(trans.scriptId));
14937         clearTimeout(trans.timeoutId);
14938         if(isLoaded){
14939             window[trans.cb] = undefined;
14940             try{
14941                 delete window[trans.cb];
14942             }catch(e){}
14943         }else{
14944             // if hasn't been loaded, wait for load to remove it to prevent script error
14945             window[trans.cb] = function(){
14946                 window[trans.cb] = undefined;
14947                 try{
14948                     delete window[trans.cb];
14949                 }catch(e){}
14950             };
14951         }
14952     },
14953
14954     // private
14955     handleResponse : function(o, trans){
14956         this.trans = false;
14957         this.destroyTrans(trans, true);
14958         var result;
14959         try {
14960             result = trans.reader.readRecords(o);
14961         }catch(e){
14962             this.fireEvent("loadexception", this, o, trans.arg, e);
14963             trans.callback.call(trans.scope||window, null, trans.arg, false);
14964             return;
14965         }
14966         this.fireEvent("load", this, o, trans.arg);
14967         trans.callback.call(trans.scope||window, result, trans.arg, true);
14968     },
14969
14970     // private
14971     handleFailure : function(trans){
14972         this.trans = false;
14973         this.destroyTrans(trans, false);
14974         this.fireEvent("loadexception", this, null, trans.arg);
14975         trans.callback.call(trans.scope||window, null, trans.arg, false);
14976     }
14977 });/*
14978  * Based on:
14979  * Ext JS Library 1.1.1
14980  * Copyright(c) 2006-2007, Ext JS, LLC.
14981  *
14982  * Originally Released Under LGPL - original licence link has changed is not relivant.
14983  *
14984  * Fork - LGPL
14985  * <script type="text/javascript">
14986  */
14987
14988 /**
14989  * @class Roo.data.JsonReader
14990  * @extends Roo.data.DataReader
14991  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14992  * based on mappings in a provided Roo.data.Record constructor.
14993  * 
14994  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14995  * in the reply previously. 
14996  * 
14997  * <p>
14998  * Example code:
14999  * <pre><code>
15000 var RecordDef = Roo.data.Record.create([
15001     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
15002     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
15003 ]);
15004 var myReader = new Roo.data.JsonReader({
15005     totalProperty: "results",    // The property which contains the total dataset size (optional)
15006     root: "rows",                // The property which contains an Array of row objects
15007     id: "id"                     // The property within each row object that provides an ID for the record (optional)
15008 }, RecordDef);
15009 </code></pre>
15010  * <p>
15011  * This would consume a JSON file like this:
15012  * <pre><code>
15013 { 'results': 2, 'rows': [
15014     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15015     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15016 }
15017 </code></pre>
15018  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15019  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15020  * paged from the remote server.
15021  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15022  * @cfg {String} root name of the property which contains the Array of row objects.
15023  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15024  * @cfg {Array} fields Array of field definition objects
15025  * @constructor
15026  * Create a new JsonReader
15027  * @param {Object} meta Metadata configuration options
15028  * @param {Object} recordType Either an Array of field definition objects,
15029  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15030  */
15031 Roo.data.JsonReader = function(meta, recordType){
15032     
15033     meta = meta || {};
15034     // set some defaults:
15035     Roo.applyIf(meta, {
15036         totalProperty: 'total',
15037         successProperty : 'success',
15038         root : 'data',
15039         id : 'id'
15040     });
15041     
15042     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15043 };
15044 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15045     
15046     readerType : 'Json',
15047     
15048     /**
15049      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
15050      * Used by Store query builder to append _requestMeta to params.
15051      * 
15052      */
15053     metaFromRemote : false,
15054     /**
15055      * This method is only used by a DataProxy which has retrieved data from a remote server.
15056      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15057      * @return {Object} data A data block which is used by an Roo.data.Store object as
15058      * a cache of Roo.data.Records.
15059      */
15060     read : function(response){
15061         var json = response.responseText;
15062        
15063         var o = /* eval:var:o */ eval("("+json+")");
15064         if(!o) {
15065             throw {message: "JsonReader.read: Json object not found"};
15066         }
15067         
15068         if(o.metaData){
15069             
15070             delete this.ef;
15071             this.metaFromRemote = true;
15072             this.meta = o.metaData;
15073             this.recordType = Roo.data.Record.create(o.metaData.fields);
15074             this.onMetaChange(this.meta, this.recordType, o);
15075         }
15076         return this.readRecords(o);
15077     },
15078
15079     // private function a store will implement
15080     onMetaChange : function(meta, recordType, o){
15081
15082     },
15083
15084     /**
15085          * @ignore
15086          */
15087     simpleAccess: function(obj, subsc) {
15088         return obj[subsc];
15089     },
15090
15091         /**
15092          * @ignore
15093          */
15094     getJsonAccessor: function(){
15095         var re = /[\[\.]/;
15096         return function(expr) {
15097             try {
15098                 return(re.test(expr))
15099                     ? new Function("obj", "return obj." + expr)
15100                     : function(obj){
15101                         return obj[expr];
15102                     };
15103             } catch(e){}
15104             return Roo.emptyFn;
15105         };
15106     }(),
15107
15108     /**
15109      * Create a data block containing Roo.data.Records from an XML document.
15110      * @param {Object} o An object which contains an Array of row objects in the property specified
15111      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15112      * which contains the total size of the dataset.
15113      * @return {Object} data A data block which is used by an Roo.data.Store object as
15114      * a cache of Roo.data.Records.
15115      */
15116     readRecords : function(o){
15117         /**
15118          * After any data loads, the raw JSON data is available for further custom processing.
15119          * @type Object
15120          */
15121         this.o = o;
15122         var s = this.meta, Record = this.recordType,
15123             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15124
15125 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15126         if (!this.ef) {
15127             if(s.totalProperty) {
15128                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15129                 }
15130                 if(s.successProperty) {
15131                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15132                 }
15133                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15134                 if (s.id) {
15135                         var g = this.getJsonAccessor(s.id);
15136                         this.getId = function(rec) {
15137                                 var r = g(rec);  
15138                                 return (r === undefined || r === "") ? null : r;
15139                         };
15140                 } else {
15141                         this.getId = function(){return null;};
15142                 }
15143             this.ef = [];
15144             for(var jj = 0; jj < fl; jj++){
15145                 f = fi[jj];
15146                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15147                 this.ef[jj] = this.getJsonAccessor(map);
15148             }
15149         }
15150
15151         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15152         if(s.totalProperty){
15153             var vt = parseInt(this.getTotal(o), 10);
15154             if(!isNaN(vt)){
15155                 totalRecords = vt;
15156             }
15157         }
15158         if(s.successProperty){
15159             var vs = this.getSuccess(o);
15160             if(vs === false || vs === 'false'){
15161                 success = false;
15162             }
15163         }
15164         var records = [];
15165         for(var i = 0; i < c; i++){
15166                 var n = root[i];
15167             var values = {};
15168             var id = this.getId(n);
15169             for(var j = 0; j < fl; j++){
15170                 f = fi[j];
15171             var v = this.ef[j](n);
15172             if (!f.convert) {
15173                 Roo.log('missing convert for ' + f.name);
15174                 Roo.log(f);
15175                 continue;
15176             }
15177             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15178             }
15179             var record = new Record(values, id);
15180             record.json = n;
15181             records[i] = record;
15182         }
15183         return {
15184             raw : o,
15185             success : success,
15186             records : records,
15187             totalRecords : totalRecords
15188         };
15189     },
15190     // used when loading children.. @see loadDataFromChildren
15191     toLoadData: function(rec)
15192     {
15193         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15194         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15195         return { data : data, total : data.length };
15196         
15197     }
15198 });/*
15199  * Based on:
15200  * Ext JS Library 1.1.1
15201  * Copyright(c) 2006-2007, Ext JS, LLC.
15202  *
15203  * Originally Released Under LGPL - original licence link has changed is not relivant.
15204  *
15205  * Fork - LGPL
15206  * <script type="text/javascript">
15207  */
15208
15209 /**
15210  * @class Roo.data.ArrayReader
15211  * @extends Roo.data.DataReader
15212  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15213  * Each element of that Array represents a row of data fields. The
15214  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15215  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15216  * <p>
15217  * Example code:.
15218  * <pre><code>
15219 var RecordDef = Roo.data.Record.create([
15220     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15221     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15222 ]);
15223 var myReader = new Roo.data.ArrayReader({
15224     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15225 }, RecordDef);
15226 </code></pre>
15227  * <p>
15228  * This would consume an Array like this:
15229  * <pre><code>
15230 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15231   </code></pre>
15232  
15233  * @constructor
15234  * Create a new JsonReader
15235  * @param {Object} meta Metadata configuration options.
15236  * @param {Object|Array} recordType Either an Array of field definition objects
15237  * 
15238  * @cfg {Array} fields Array of field definition objects
15239  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15240  * as specified to {@link Roo.data.Record#create},
15241  * or an {@link Roo.data.Record} object
15242  *
15243  * 
15244  * created using {@link Roo.data.Record#create}.
15245  */
15246 Roo.data.ArrayReader = function(meta, recordType)
15247 {    
15248     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15249 };
15250
15251 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15252     
15253       /**
15254      * Create a data block containing Roo.data.Records from an XML document.
15255      * @param {Object} o An Array of row objects which represents the dataset.
15256      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15257      * a cache of Roo.data.Records.
15258      */
15259     readRecords : function(o)
15260     {
15261         var sid = this.meta ? this.meta.id : null;
15262         var recordType = this.recordType, fields = recordType.prototype.fields;
15263         var records = [];
15264         var root = o;
15265         for(var i = 0; i < root.length; i++){
15266             var n = root[i];
15267             var values = {};
15268             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15269             for(var j = 0, jlen = fields.length; j < jlen; j++){
15270                 var f = fields.items[j];
15271                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15272                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15273                 v = f.convert(v);
15274                 values[f.name] = v;
15275             }
15276             var record = new recordType(values, id);
15277             record.json = n;
15278             records[records.length] = record;
15279         }
15280         return {
15281             records : records,
15282             totalRecords : records.length
15283         };
15284     },
15285     // used when loading children.. @see loadDataFromChildren
15286     toLoadData: function(rec)
15287     {
15288         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15289         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15290         
15291     }
15292     
15293     
15294 });/*
15295  * - LGPL
15296  * * 
15297  */
15298
15299 /**
15300  * @class Roo.bootstrap.ComboBox
15301  * @extends Roo.bootstrap.TriggerField
15302  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15303  * @cfg {Boolean} append (true|false) default false
15304  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15305  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15306  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15307  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15308  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15309  * @cfg {Boolean} animate default true
15310  * @cfg {Boolean} emptyResultText only for touch device
15311  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15312  * @cfg {String} emptyTitle default ''
15313  * @cfg {Number} width fixed with? experimental
15314  * @constructor
15315  * Create a new ComboBox.
15316  * @param {Object} config Configuration options
15317  */
15318 Roo.bootstrap.ComboBox = function(config){
15319     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15320     this.addEvents({
15321         /**
15322          * @event expand
15323          * Fires when the dropdown list is expanded
15324         * @param {Roo.bootstrap.ComboBox} combo This combo box
15325         */
15326         'expand' : true,
15327         /**
15328          * @event collapse
15329          * Fires when the dropdown list is collapsed
15330         * @param {Roo.bootstrap.ComboBox} combo This combo box
15331         */
15332         'collapse' : true,
15333         /**
15334          * @event beforeselect
15335          * Fires before a list item is selected. Return false to cancel the selection.
15336         * @param {Roo.bootstrap.ComboBox} combo This combo box
15337         * @param {Roo.data.Record} record The data record returned from the underlying store
15338         * @param {Number} index The index of the selected item in the dropdown list
15339         */
15340         'beforeselect' : true,
15341         /**
15342          * @event select
15343          * Fires when a list item is selected
15344         * @param {Roo.bootstrap.ComboBox} combo This combo box
15345         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15346         * @param {Number} index The index of the selected item in the dropdown list
15347         */
15348         'select' : true,
15349         /**
15350          * @event beforequery
15351          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15352          * The event object passed has these properties:
15353         * @param {Roo.bootstrap.ComboBox} combo This combo box
15354         * @param {String} query The query
15355         * @param {Boolean} forceAll true to force "all" query
15356         * @param {Boolean} cancel true to cancel the query
15357         * @param {Object} e The query event object
15358         */
15359         'beforequery': true,
15360          /**
15361          * @event add
15362          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15363         * @param {Roo.bootstrap.ComboBox} combo This combo box
15364         */
15365         'add' : true,
15366         /**
15367          * @event edit
15368          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15369         * @param {Roo.bootstrap.ComboBox} combo This combo box
15370         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15371         */
15372         'edit' : true,
15373         /**
15374          * @event remove
15375          * Fires when the remove value from the combobox array
15376         * @param {Roo.bootstrap.ComboBox} combo This combo box
15377         */
15378         'remove' : true,
15379         /**
15380          * @event afterremove
15381          * Fires when the remove value from the combobox array
15382         * @param {Roo.bootstrap.ComboBox} combo This combo box
15383         */
15384         'afterremove' : true,
15385         /**
15386          * @event specialfilter
15387          * Fires when specialfilter
15388             * @param {Roo.bootstrap.ComboBox} combo This combo box
15389             */
15390         'specialfilter' : true,
15391         /**
15392          * @event tick
15393          * Fires when tick the element
15394             * @param {Roo.bootstrap.ComboBox} combo This combo box
15395             */
15396         'tick' : true,
15397         /**
15398          * @event touchviewdisplay
15399          * Fires when touch view require special display (default is using displayField)
15400             * @param {Roo.bootstrap.ComboBox} combo This combo box
15401             * @param {Object} cfg set html .
15402             */
15403         'touchviewdisplay' : true
15404         
15405     });
15406     
15407     this.item = [];
15408     this.tickItems = [];
15409     
15410     this.selectedIndex = -1;
15411     if(this.mode == 'local'){
15412         if(config.queryDelay === undefined){
15413             this.queryDelay = 10;
15414         }
15415         if(config.minChars === undefined){
15416             this.minChars = 0;
15417         }
15418     }
15419 };
15420
15421 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15422      
15423     /**
15424      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15425      * rendering into an Roo.Editor, defaults to false)
15426      */
15427     /**
15428      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15429      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15430      */
15431     /**
15432      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15433      */
15434     /**
15435      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15436      * the dropdown list (defaults to undefined, with no header element)
15437      */
15438
15439      /**
15440      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15441      */
15442      
15443      /**
15444      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15445      */
15446     listWidth: undefined,
15447     /**
15448      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15449      * mode = 'remote' or 'text' if mode = 'local')
15450      */
15451     displayField: undefined,
15452     
15453     /**
15454      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15455      * mode = 'remote' or 'value' if mode = 'local'). 
15456      * Note: use of a valueField requires the user make a selection
15457      * in order for a value to be mapped.
15458      */
15459     valueField: undefined,
15460     /**
15461      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15462      */
15463     modalTitle : '',
15464     
15465     /**
15466      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15467      * field's data value (defaults to the underlying DOM element's name)
15468      */
15469     hiddenName: undefined,
15470     /**
15471      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15472      */
15473     listClass: '',
15474     /**
15475      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15476      */
15477     selectedClass: 'active',
15478     
15479     /**
15480      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15481      */
15482     shadow:'sides',
15483     /**
15484      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15485      * anchor positions (defaults to 'tl-bl')
15486      */
15487     listAlign: 'tl-bl?',
15488     /**
15489      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15490      */
15491     maxHeight: 300,
15492     /**
15493      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15494      * query specified by the allQuery config option (defaults to 'query')
15495      */
15496     triggerAction: 'query',
15497     /**
15498      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15499      * (defaults to 4, does not apply if editable = false)
15500      */
15501     minChars : 4,
15502     /**
15503      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15504      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15505      */
15506     typeAhead: false,
15507     /**
15508      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15509      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15510      */
15511     queryDelay: 500,
15512     /**
15513      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15514      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15515      */
15516     pageSize: 0,
15517     /**
15518      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15519      * when editable = true (defaults to false)
15520      */
15521     selectOnFocus:false,
15522     /**
15523      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15524      */
15525     queryParam: 'query',
15526     /**
15527      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15528      * when mode = 'remote' (defaults to 'Loading...')
15529      */
15530     loadingText: 'Loading...',
15531     /**
15532      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15533      */
15534     resizable: false,
15535     /**
15536      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15537      */
15538     handleHeight : 8,
15539     /**
15540      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15541      * traditional select (defaults to true)
15542      */
15543     editable: true,
15544     /**
15545      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15546      */
15547     allQuery: '',
15548     /**
15549      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15550      */
15551     mode: 'remote',
15552     /**
15553      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15554      * listWidth has a higher value)
15555      */
15556     minListWidth : 70,
15557     /**
15558      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15559      * allow the user to set arbitrary text into the field (defaults to false)
15560      */
15561     forceSelection:false,
15562     /**
15563      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15564      * if typeAhead = true (defaults to 250)
15565      */
15566     typeAheadDelay : 250,
15567     /**
15568      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15569      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15570      */
15571     valueNotFoundText : undefined,
15572     /**
15573      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15574      */
15575     blockFocus : false,
15576     
15577     /**
15578      * @cfg {Boolean} disableClear Disable showing of clear button.
15579      */
15580     disableClear : false,
15581     /**
15582      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15583      */
15584     alwaysQuery : false,
15585     
15586     /**
15587      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15588      */
15589     multiple : false,
15590     
15591     /**
15592      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15593      */
15594     invalidClass : "has-warning",
15595     
15596     /**
15597      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15598      */
15599     validClass : "has-success",
15600     
15601     /**
15602      * @cfg {Boolean} specialFilter (true|false) special filter default false
15603      */
15604     specialFilter : false,
15605     
15606     /**
15607      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15608      */
15609     mobileTouchView : true,
15610     
15611     /**
15612      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15613      */
15614     useNativeIOS : false,
15615     
15616     /**
15617      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15618      */
15619     mobile_restrict_height : false,
15620     
15621     ios_options : false,
15622     
15623     //private
15624     addicon : false,
15625     editicon: false,
15626     
15627     page: 0,
15628     hasQuery: false,
15629     append: false,
15630     loadNext: false,
15631     autoFocus : true,
15632     tickable : false,
15633     btnPosition : 'right',
15634     triggerList : true,
15635     showToggleBtn : true,
15636     animate : true,
15637     emptyResultText: 'Empty',
15638     triggerText : 'Select',
15639     emptyTitle : '',
15640     width : false,
15641     
15642     // element that contains real text value.. (when hidden is used..)
15643     
15644     getAutoCreate : function()
15645     {   
15646         var cfg = false;
15647         //render
15648         /*
15649          * Render classic select for iso
15650          */
15651         
15652         if(Roo.isIOS && this.useNativeIOS){
15653             cfg = this.getAutoCreateNativeIOS();
15654             return cfg;
15655         }
15656         
15657         /*
15658          * Touch Devices
15659          */
15660         
15661         if(Roo.isTouch && this.mobileTouchView){
15662             cfg = this.getAutoCreateTouchView();
15663             return cfg;;
15664         }
15665         
15666         /*
15667          *  Normal ComboBox
15668          */
15669         if(!this.tickable){
15670             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15671             return cfg;
15672         }
15673         
15674         /*
15675          *  ComboBox with tickable selections
15676          */
15677              
15678         var align = this.labelAlign || this.parentLabelAlign();
15679         
15680         cfg = {
15681             cls : 'form-group roo-combobox-tickable' //input-group
15682         };
15683         
15684         var btn_text_select = '';
15685         var btn_text_done = '';
15686         var btn_text_cancel = '';
15687         
15688         if (this.btn_text_show) {
15689             btn_text_select = 'Select';
15690             btn_text_done = 'Done';
15691             btn_text_cancel = 'Cancel'; 
15692         }
15693         
15694         var buttons = {
15695             tag : 'div',
15696             cls : 'tickable-buttons',
15697             cn : [
15698                 {
15699                     tag : 'button',
15700                     type : 'button',
15701                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15702                     //html : this.triggerText
15703                     html: btn_text_select
15704                 },
15705                 {
15706                     tag : 'button',
15707                     type : 'button',
15708                     name : 'ok',
15709                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15710                     //html : 'Done'
15711                     html: btn_text_done
15712                 },
15713                 {
15714                     tag : 'button',
15715                     type : 'button',
15716                     name : 'cancel',
15717                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15718                     //html : 'Cancel'
15719                     html: btn_text_cancel
15720                 }
15721             ]
15722         };
15723         
15724         if(this.editable){
15725             buttons.cn.unshift({
15726                 tag: 'input',
15727                 cls: 'roo-select2-search-field-input'
15728             });
15729         }
15730         
15731         var _this = this;
15732         
15733         Roo.each(buttons.cn, function(c){
15734             if (_this.size) {
15735                 c.cls += ' btn-' + _this.size;
15736             }
15737
15738             if (_this.disabled) {
15739                 c.disabled = true;
15740             }
15741         });
15742         
15743         var box = {
15744             tag: 'div',
15745             style : 'display: contents',
15746             cn: [
15747                 {
15748                     tag: 'input',
15749                     type : 'hidden',
15750                     cls: 'form-hidden-field'
15751                 },
15752                 {
15753                     tag: 'ul',
15754                     cls: 'roo-select2-choices',
15755                     cn:[
15756                         {
15757                             tag: 'li',
15758                             cls: 'roo-select2-search-field',
15759                             cn: [
15760                                 buttons
15761                             ]
15762                         }
15763                     ]
15764                 }
15765             ]
15766         };
15767         
15768         var combobox = {
15769             cls: 'roo-select2-container input-group roo-select2-container-multi',
15770             cn: [
15771                 
15772                 box
15773 //                {
15774 //                    tag: 'ul',
15775 //                    cls: 'typeahead typeahead-long dropdown-menu',
15776 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15777 //                }
15778             ]
15779         };
15780         
15781         if(this.hasFeedback && !this.allowBlank){
15782             
15783             var feedback = {
15784                 tag: 'span',
15785                 cls: 'glyphicon form-control-feedback'
15786             };
15787
15788             combobox.cn.push(feedback);
15789         }
15790         
15791         
15792         
15793         var indicator = {
15794             tag : 'i',
15795             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15796             tooltip : 'This field is required'
15797         };
15798         if (Roo.bootstrap.version == 4) {
15799             indicator = {
15800                 tag : 'i',
15801                 style : 'display:none'
15802             };
15803         }
15804         if (align ==='left' && this.fieldLabel.length) {
15805             
15806             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15807             
15808             cfg.cn = [
15809                 indicator,
15810                 {
15811                     tag: 'label',
15812                     'for' :  id,
15813                     cls : 'control-label col-form-label',
15814                     html : this.fieldLabel
15815
15816                 },
15817                 {
15818                     cls : "", 
15819                     cn: [
15820                         combobox
15821                     ]
15822                 }
15823
15824             ];
15825             
15826             var labelCfg = cfg.cn[1];
15827             var contentCfg = cfg.cn[2];
15828             
15829
15830             if(this.indicatorpos == 'right'){
15831                 
15832                 cfg.cn = [
15833                     {
15834                         tag: 'label',
15835                         'for' :  id,
15836                         cls : 'control-label col-form-label',
15837                         cn : [
15838                             {
15839                                 tag : 'span',
15840                                 html : this.fieldLabel
15841                             },
15842                             indicator
15843                         ]
15844                     },
15845                     {
15846                         cls : "",
15847                         cn: [
15848                             combobox
15849                         ]
15850                     }
15851
15852                 ];
15853                 
15854                 
15855                 
15856                 labelCfg = cfg.cn[0];
15857                 contentCfg = cfg.cn[1];
15858             
15859             }
15860             
15861             if(this.labelWidth > 12){
15862                 labelCfg.style = "width: " + this.labelWidth + 'px';
15863             }
15864             if(this.width * 1 > 0){
15865                 contentCfg.style = "width: " + this.width + 'px';
15866             }
15867             if(this.labelWidth < 13 && this.labelmd == 0){
15868                 this.labelmd = this.labelWidth;
15869             }
15870             
15871             if(this.labellg > 0){
15872                 labelCfg.cls += ' col-lg-' + this.labellg;
15873                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15874             }
15875             
15876             if(this.labelmd > 0){
15877                 labelCfg.cls += ' col-md-' + this.labelmd;
15878                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15879             }
15880             
15881             if(this.labelsm > 0){
15882                 labelCfg.cls += ' col-sm-' + this.labelsm;
15883                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15884             }
15885             
15886             if(this.labelxs > 0){
15887                 labelCfg.cls += ' col-xs-' + this.labelxs;
15888                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15889             }
15890                 
15891                 
15892         } else if ( this.fieldLabel.length) {
15893 //                Roo.log(" label");
15894                  cfg.cn = [
15895                    indicator,
15896                     {
15897                         tag: 'label',
15898                         //cls : 'input-group-addon',
15899                         html : this.fieldLabel
15900                     },
15901                     combobox
15902                 ];
15903                 
15904                 if(this.indicatorpos == 'right'){
15905                     cfg.cn = [
15906                         {
15907                             tag: 'label',
15908                             //cls : 'input-group-addon',
15909                             html : this.fieldLabel
15910                         },
15911                         indicator,
15912                         combobox
15913                     ];
15914                     
15915                 }
15916
15917         } else {
15918             
15919 //                Roo.log(" no label && no align");
15920                 cfg = combobox
15921                      
15922                 
15923         }
15924          
15925         var settings=this;
15926         ['xs','sm','md','lg'].map(function(size){
15927             if (settings[size]) {
15928                 cfg.cls += ' col-' + size + '-' + settings[size];
15929             }
15930         });
15931         
15932         return cfg;
15933         
15934     },
15935     
15936     _initEventsCalled : false,
15937     
15938     // private
15939     initEvents: function()
15940     {   
15941         if (this._initEventsCalled) { // as we call render... prevent looping...
15942             return;
15943         }
15944         this._initEventsCalled = true;
15945         
15946         if (!this.store) {
15947             throw "can not find store for combo";
15948         }
15949         
15950         this.indicator = this.indicatorEl();
15951         
15952         this.store = Roo.factory(this.store, Roo.data);
15953         this.store.parent = this;
15954         
15955         // if we are building from html. then this element is so complex, that we can not really
15956         // use the rendered HTML.
15957         // so we have to trash and replace the previous code.
15958         if (Roo.XComponent.build_from_html) {
15959             // remove this element....
15960             var e = this.el.dom, k=0;
15961             while (e ) { e = e.previousSibling;  ++k;}
15962
15963             this.el.remove();
15964             
15965             this.el=false;
15966             this.rendered = false;
15967             
15968             this.render(this.parent().getChildContainer(true), k);
15969         }
15970         
15971         if(Roo.isIOS && this.useNativeIOS){
15972             this.initIOSView();
15973             return;
15974         }
15975         
15976         /*
15977          * Touch Devices
15978          */
15979         
15980         if(Roo.isTouch && this.mobileTouchView){
15981             this.initTouchView();
15982             return;
15983         }
15984         
15985         if(this.tickable){
15986             this.initTickableEvents();
15987             return;
15988         }
15989         
15990         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15991         
15992         if(this.hiddenName){
15993             
15994             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15995             
15996             this.hiddenField.dom.value =
15997                 this.hiddenValue !== undefined ? this.hiddenValue :
15998                 this.value !== undefined ? this.value : '';
15999
16000             // prevent input submission
16001             this.el.dom.removeAttribute('name');
16002             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16003              
16004              
16005         }
16006         //if(Roo.isGecko){
16007         //    this.el.dom.setAttribute('autocomplete', 'off');
16008         //}
16009         
16010         var cls = 'x-combo-list';
16011         
16012         //this.list = new Roo.Layer({
16013         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16014         //});
16015         
16016         var _this = this;
16017         
16018         (function(){
16019             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16020             _this.list.setWidth(lw);
16021         }).defer(100);
16022         
16023         this.list.on('mouseover', this.onViewOver, this);
16024         this.list.on('mousemove', this.onViewMove, this);
16025         this.list.on('scroll', this.onViewScroll, this);
16026         
16027         /*
16028         this.list.swallowEvent('mousewheel');
16029         this.assetHeight = 0;
16030
16031         if(this.title){
16032             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16033             this.assetHeight += this.header.getHeight();
16034         }
16035
16036         this.innerList = this.list.createChild({cls:cls+'-inner'});
16037         this.innerList.on('mouseover', this.onViewOver, this);
16038         this.innerList.on('mousemove', this.onViewMove, this);
16039         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16040         
16041         if(this.allowBlank && !this.pageSize && !this.disableClear){
16042             this.footer = this.list.createChild({cls:cls+'-ft'});
16043             this.pageTb = new Roo.Toolbar(this.footer);
16044            
16045         }
16046         if(this.pageSize){
16047             this.footer = this.list.createChild({cls:cls+'-ft'});
16048             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16049                     {pageSize: this.pageSize});
16050             
16051         }
16052         
16053         if (this.pageTb && this.allowBlank && !this.disableClear) {
16054             var _this = this;
16055             this.pageTb.add(new Roo.Toolbar.Fill(), {
16056                 cls: 'x-btn-icon x-btn-clear',
16057                 text: '&#160;',
16058                 handler: function()
16059                 {
16060                     _this.collapse();
16061                     _this.clearValue();
16062                     _this.onSelect(false, -1);
16063                 }
16064             });
16065         }
16066         if (this.footer) {
16067             this.assetHeight += this.footer.getHeight();
16068         }
16069         */
16070             
16071         if(!this.tpl){
16072             this.tpl = Roo.bootstrap.version == 4 ?
16073                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16074                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16075         }
16076
16077         this.view = new Roo.View(this.list, this.tpl, {
16078             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16079         });
16080         //this.view.wrapEl.setDisplayed(false);
16081         this.view.on('click', this.onViewClick, this);
16082         
16083         
16084         this.store.on('beforeload', this.onBeforeLoad, this);
16085         this.store.on('load', this.onLoad, this);
16086         this.store.on('loadexception', this.onLoadException, this);
16087         /*
16088         if(this.resizable){
16089             this.resizer = new Roo.Resizable(this.list,  {
16090                pinned:true, handles:'se'
16091             });
16092             this.resizer.on('resize', function(r, w, h){
16093                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16094                 this.listWidth = w;
16095                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16096                 this.restrictHeight();
16097             }, this);
16098             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16099         }
16100         */
16101         if(!this.editable){
16102             this.editable = true;
16103             this.setEditable(false);
16104         }
16105         
16106         /*
16107         
16108         if (typeof(this.events.add.listeners) != 'undefined') {
16109             
16110             this.addicon = this.wrap.createChild(
16111                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16112        
16113             this.addicon.on('click', function(e) {
16114                 this.fireEvent('add', this);
16115             }, this);
16116         }
16117         if (typeof(this.events.edit.listeners) != 'undefined') {
16118             
16119             this.editicon = this.wrap.createChild(
16120                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16121             if (this.addicon) {
16122                 this.editicon.setStyle('margin-left', '40px');
16123             }
16124             this.editicon.on('click', function(e) {
16125                 
16126                 // we fire even  if inothing is selected..
16127                 this.fireEvent('edit', this, this.lastData );
16128                 
16129             }, this);
16130         }
16131         */
16132         
16133         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16134             "up" : function(e){
16135                 this.inKeyMode = true;
16136                 this.selectPrev();
16137             },
16138
16139             "down" : function(e){
16140                 if(!this.isExpanded()){
16141                     this.onTriggerClick();
16142                 }else{
16143                     this.inKeyMode = true;
16144                     this.selectNext();
16145                 }
16146             },
16147
16148             "enter" : function(e){
16149 //                this.onViewClick();
16150                 //return true;
16151                 this.collapse();
16152                 
16153                 if(this.fireEvent("specialkey", this, e)){
16154                     this.onViewClick(false);
16155                 }
16156                 
16157                 return true;
16158             },
16159
16160             "esc" : function(e){
16161                 this.collapse();
16162             },
16163
16164             "tab" : function(e){
16165                 this.collapse();
16166                 
16167                 if(this.fireEvent("specialkey", this, e)){
16168                     this.onViewClick(false);
16169                 }
16170                 
16171                 return true;
16172             },
16173
16174             scope : this,
16175
16176             doRelay : function(foo, bar, hname){
16177                 if(hname == 'down' || this.scope.isExpanded()){
16178                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16179                 }
16180                 return true;
16181             },
16182
16183             forceKeyDown: true
16184         });
16185         
16186         
16187         this.queryDelay = Math.max(this.queryDelay || 10,
16188                 this.mode == 'local' ? 10 : 250);
16189         
16190         
16191         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16192         
16193         if(this.typeAhead){
16194             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16195         }
16196         if(this.editable !== false){
16197             this.inputEl().on("keyup", this.onKeyUp, this);
16198         }
16199         if(this.forceSelection){
16200             this.inputEl().on('blur', this.doForce, this);
16201         }
16202         
16203         if(this.multiple){
16204             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16205             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16206         }
16207     },
16208     
16209     initTickableEvents: function()
16210     {   
16211         this.createList();
16212         
16213         if(this.hiddenName){
16214             
16215             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16216             
16217             this.hiddenField.dom.value =
16218                 this.hiddenValue !== undefined ? this.hiddenValue :
16219                 this.value !== undefined ? this.value : '';
16220
16221             // prevent input submission
16222             this.el.dom.removeAttribute('name');
16223             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16224              
16225              
16226         }
16227         
16228 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16229         
16230         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16231         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16232         if(this.triggerList){
16233             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16234         }
16235          
16236         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16237         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16238         
16239         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16240         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16241         
16242         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16243         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16244         
16245         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16246         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16247         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16248         
16249         this.okBtn.hide();
16250         this.cancelBtn.hide();
16251         
16252         var _this = this;
16253         
16254         (function(){
16255             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16256             _this.list.setWidth(lw);
16257         }).defer(100);
16258         
16259         this.list.on('mouseover', this.onViewOver, this);
16260         this.list.on('mousemove', this.onViewMove, this);
16261         
16262         this.list.on('scroll', this.onViewScroll, this);
16263         
16264         if(!this.tpl){
16265             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16266                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16267         }
16268
16269         this.view = new Roo.View(this.list, this.tpl, {
16270             singleSelect:true,
16271             tickable:true,
16272             parent:this,
16273             store: this.store,
16274             selectedClass: this.selectedClass
16275         });
16276         
16277         //this.view.wrapEl.setDisplayed(false);
16278         this.view.on('click', this.onViewClick, this);
16279         
16280         
16281         
16282         this.store.on('beforeload', this.onBeforeLoad, this);
16283         this.store.on('load', this.onLoad, this);
16284         this.store.on('loadexception', this.onLoadException, this);
16285         
16286         if(this.editable){
16287             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16288                 "up" : function(e){
16289                     this.inKeyMode = true;
16290                     this.selectPrev();
16291                 },
16292
16293                 "down" : function(e){
16294                     this.inKeyMode = true;
16295                     this.selectNext();
16296                 },
16297
16298                 "enter" : function(e){
16299                     if(this.fireEvent("specialkey", this, e)){
16300                         this.onViewClick(false);
16301                     }
16302                     
16303                     return true;
16304                 },
16305
16306                 "esc" : function(e){
16307                     this.onTickableFooterButtonClick(e, false, false);
16308                 },
16309
16310                 "tab" : function(e){
16311                     this.fireEvent("specialkey", this, e);
16312                     
16313                     this.onTickableFooterButtonClick(e, false, false);
16314                     
16315                     return true;
16316                 },
16317
16318                 scope : this,
16319
16320                 doRelay : function(e, fn, key){
16321                     if(this.scope.isExpanded()){
16322                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16323                     }
16324                     return true;
16325                 },
16326
16327                 forceKeyDown: true
16328             });
16329         }
16330         
16331         this.queryDelay = Math.max(this.queryDelay || 10,
16332                 this.mode == 'local' ? 10 : 250);
16333         
16334         
16335         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16336         
16337         if(this.typeAhead){
16338             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16339         }
16340         
16341         if(this.editable !== false){
16342             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16343         }
16344         
16345         this.indicator = this.indicatorEl();
16346         
16347         if(this.indicator){
16348             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16349             this.indicator.hide();
16350         }
16351         
16352     },
16353
16354     onDestroy : function(){
16355         if(this.view){
16356             this.view.setStore(null);
16357             this.view.el.removeAllListeners();
16358             this.view.el.remove();
16359             this.view.purgeListeners();
16360         }
16361         if(this.list){
16362             this.list.dom.innerHTML  = '';
16363         }
16364         
16365         if(this.store){
16366             this.store.un('beforeload', this.onBeforeLoad, this);
16367             this.store.un('load', this.onLoad, this);
16368             this.store.un('loadexception', this.onLoadException, this);
16369         }
16370         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16371     },
16372
16373     // private
16374     fireKey : function(e){
16375         if(e.isNavKeyPress() && !this.list.isVisible()){
16376             this.fireEvent("specialkey", this, e);
16377         }
16378     },
16379
16380     // private
16381     onResize: function(w, h)
16382     {
16383         
16384         
16385 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16386 //        
16387 //        if(typeof w != 'number'){
16388 //            // we do not handle it!?!?
16389 //            return;
16390 //        }
16391 //        var tw = this.trigger.getWidth();
16392 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16393 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16394 //        var x = w - tw;
16395 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16396 //            
16397 //        //this.trigger.setStyle('left', x+'px');
16398 //        
16399 //        if(this.list && this.listWidth === undefined){
16400 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16401 //            this.list.setWidth(lw);
16402 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16403 //        }
16404         
16405     
16406         
16407     },
16408
16409     /**
16410      * Allow or prevent the user from directly editing the field text.  If false is passed,
16411      * the user will only be able to select from the items defined in the dropdown list.  This method
16412      * is the runtime equivalent of setting the 'editable' config option at config time.
16413      * @param {Boolean} value True to allow the user to directly edit the field text
16414      */
16415     setEditable : function(value){
16416         if(value == this.editable){
16417             return;
16418         }
16419         this.editable = value;
16420         if(!value){
16421             this.inputEl().dom.setAttribute('readOnly', true);
16422             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16423             this.inputEl().addClass('x-combo-noedit');
16424         }else{
16425             this.inputEl().dom.removeAttribute('readOnly');
16426             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16427             this.inputEl().removeClass('x-combo-noedit');
16428         }
16429     },
16430
16431     // private
16432     
16433     onBeforeLoad : function(combo,opts){
16434         if(!this.hasFocus){
16435             return;
16436         }
16437          if (!opts.add) {
16438             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16439          }
16440         this.restrictHeight();
16441         this.selectedIndex = -1;
16442     },
16443
16444     // private
16445     onLoad : function(){
16446         
16447         this.hasQuery = false;
16448         
16449         if(!this.hasFocus){
16450             return;
16451         }
16452         
16453         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16454             this.loading.hide();
16455         }
16456         
16457         if(this.store.getCount() > 0){
16458             
16459             this.expand();
16460             this.restrictHeight();
16461             if(this.lastQuery == this.allQuery){
16462                 if(this.editable && !this.tickable){
16463                     this.inputEl().dom.select();
16464                 }
16465                 
16466                 if(
16467                     !this.selectByValue(this.value, true) &&
16468                     this.autoFocus && 
16469                     (
16470                         !this.store.lastOptions ||
16471                         typeof(this.store.lastOptions.add) == 'undefined' || 
16472                         this.store.lastOptions.add != true
16473                     )
16474                 ){
16475                     this.select(0, true);
16476                 }
16477             }else{
16478                 if(this.autoFocus){
16479                     this.selectNext();
16480                 }
16481                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16482                     this.taTask.delay(this.typeAheadDelay);
16483                 }
16484             }
16485         }else{
16486             this.onEmptyResults();
16487         }
16488         
16489         //this.el.focus();
16490     },
16491     // private
16492     onLoadException : function()
16493     {
16494         this.hasQuery = false;
16495         
16496         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16497             this.loading.hide();
16498         }
16499         
16500         if(this.tickable && this.editable){
16501             return;
16502         }
16503         
16504         this.collapse();
16505         // only causes errors at present
16506         //Roo.log(this.store.reader.jsonData);
16507         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16508             // fixme
16509             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16510         //}
16511         
16512         
16513     },
16514     // private
16515     onTypeAhead : function(){
16516         if(this.store.getCount() > 0){
16517             var r = this.store.getAt(0);
16518             var newValue = r.data[this.displayField];
16519             var len = newValue.length;
16520             var selStart = this.getRawValue().length;
16521             
16522             if(selStart != len){
16523                 this.setRawValue(newValue);
16524                 this.selectText(selStart, newValue.length);
16525             }
16526         }
16527     },
16528
16529     // private
16530     onSelect : function(record, index){
16531         
16532         if(this.fireEvent('beforeselect', this, record, index) !== false){
16533         
16534             this.setFromData(index > -1 ? record.data : false);
16535             
16536             this.collapse();
16537             this.fireEvent('select', this, record, index);
16538         }
16539     },
16540
16541     /**
16542      * Returns the currently selected field value or empty string if no value is set.
16543      * @return {String} value The selected value
16544      */
16545     getValue : function()
16546     {
16547         if(Roo.isIOS && this.useNativeIOS){
16548             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16549         }
16550         
16551         if(this.multiple){
16552             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16553         }
16554         
16555         if(this.valueField){
16556             return typeof this.value != 'undefined' ? this.value : '';
16557         }else{
16558             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16559         }
16560     },
16561     
16562     getRawValue : function()
16563     {
16564         if(Roo.isIOS && this.useNativeIOS){
16565             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16566         }
16567         
16568         var v = this.inputEl().getValue();
16569         
16570         return v;
16571     },
16572
16573     /**
16574      * Clears any text/value currently set in the field
16575      */
16576     clearValue : function(){
16577         
16578         if(this.hiddenField){
16579             this.hiddenField.dom.value = '';
16580         }
16581         this.value = '';
16582         this.setRawValue('');
16583         this.lastSelectionText = '';
16584         this.lastData = false;
16585         
16586         var close = this.closeTriggerEl();
16587         
16588         if(close){
16589             close.hide();
16590         }
16591         
16592         this.validate();
16593         
16594     },
16595
16596     /**
16597      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16598      * will be displayed in the field.  If the value does not match the data value of an existing item,
16599      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16600      * Otherwise the field will be blank (although the value will still be set).
16601      * @param {String} value The value to match
16602      */
16603     setValue : function(v)
16604     {
16605         if(Roo.isIOS && this.useNativeIOS){
16606             this.setIOSValue(v);
16607             return;
16608         }
16609         
16610         if(this.multiple){
16611             this.syncValue();
16612             return;
16613         }
16614         
16615         var text = v;
16616         if(this.valueField){
16617             var r = this.findRecord(this.valueField, v);
16618             if(r){
16619                 text = r.data[this.displayField];
16620             }else if(this.valueNotFoundText !== undefined){
16621                 text = this.valueNotFoundText;
16622             }
16623         }
16624         this.lastSelectionText = text;
16625         if(this.hiddenField){
16626             this.hiddenField.dom.value = v;
16627         }
16628         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16629         this.value = v;
16630         
16631         var close = this.closeTriggerEl();
16632         
16633         if(close){
16634             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16635         }
16636         
16637         this.validate();
16638     },
16639     /**
16640      * @property {Object} the last set data for the element
16641      */
16642     
16643     lastData : false,
16644     /**
16645      * Sets the value of the field based on a object which is related to the record format for the store.
16646      * @param {Object} value the value to set as. or false on reset?
16647      */
16648     setFromData : function(o){
16649         
16650         if(this.multiple){
16651             this.addItem(o);
16652             return;
16653         }
16654             
16655         var dv = ''; // display value
16656         var vv = ''; // value value..
16657         this.lastData = o;
16658         if (this.displayField) {
16659             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16660         } else {
16661             // this is an error condition!!!
16662             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16663         }
16664         
16665         if(this.valueField){
16666             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16667         }
16668         
16669         var close = this.closeTriggerEl();
16670         
16671         if(close){
16672             if(dv.length || vv * 1 > 0){
16673                 close.show() ;
16674                 this.blockFocus=true;
16675             } else {
16676                 close.hide();
16677             }             
16678         }
16679         
16680         if(this.hiddenField){
16681             this.hiddenField.dom.value = vv;
16682             
16683             this.lastSelectionText = dv;
16684             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16685             this.value = vv;
16686             return;
16687         }
16688         // no hidden field.. - we store the value in 'value', but still display
16689         // display field!!!!
16690         this.lastSelectionText = dv;
16691         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16692         this.value = vv;
16693         
16694         
16695         
16696     },
16697     // private
16698     reset : function(){
16699         // overridden so that last data is reset..
16700         
16701         if(this.multiple){
16702             this.clearItem();
16703             return;
16704         }
16705         
16706         this.setValue(this.originalValue);
16707         //this.clearInvalid();
16708         this.lastData = false;
16709         if (this.view) {
16710             this.view.clearSelections();
16711         }
16712         
16713         this.validate();
16714     },
16715     // private
16716     findRecord : function(prop, value){
16717         var record;
16718         if(this.store.getCount() > 0){
16719             this.store.each(function(r){
16720                 if(r.data[prop] == value){
16721                     record = r;
16722                     return false;
16723                 }
16724                 return true;
16725             });
16726         }
16727         return record;
16728     },
16729     
16730     getName: function()
16731     {
16732         // returns hidden if it's set..
16733         if (!this.rendered) {return ''};
16734         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16735         
16736     },
16737     // private
16738     onViewMove : function(e, t){
16739         this.inKeyMode = false;
16740     },
16741
16742     // private
16743     onViewOver : function(e, t){
16744         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16745             return;
16746         }
16747         var item = this.view.findItemFromChild(t);
16748         
16749         if(item){
16750             var index = this.view.indexOf(item);
16751             this.select(index, false);
16752         }
16753     },
16754
16755     // private
16756     onViewClick : function(view, doFocus, el, e)
16757     {
16758         var index = this.view.getSelectedIndexes()[0];
16759         
16760         var r = this.store.getAt(index);
16761         
16762         if(this.tickable){
16763             
16764             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16765                 return;
16766             }
16767             
16768             var rm = false;
16769             var _this = this;
16770             
16771             Roo.each(this.tickItems, function(v,k){
16772                 
16773                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16774                     Roo.log(v);
16775                     _this.tickItems.splice(k, 1);
16776                     
16777                     if(typeof(e) == 'undefined' && view == false){
16778                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16779                     }
16780                     
16781                     rm = true;
16782                     return;
16783                 }
16784             });
16785             
16786             if(rm){
16787                 return;
16788             }
16789             
16790             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16791                 this.tickItems.push(r.data);
16792             }
16793             
16794             if(typeof(e) == 'undefined' && view == false){
16795                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16796             }
16797                     
16798             return;
16799         }
16800         
16801         if(r){
16802             this.onSelect(r, index);
16803         }
16804         if(doFocus !== false && !this.blockFocus){
16805             this.inputEl().focus();
16806         }
16807     },
16808
16809     // private
16810     restrictHeight : function(){
16811         //this.innerList.dom.style.height = '';
16812         //var inner = this.innerList.dom;
16813         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16814         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16815         //this.list.beginUpdate();
16816         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16817         this.list.alignTo(this.inputEl(), this.listAlign);
16818         this.list.alignTo(this.inputEl(), this.listAlign);
16819         //this.list.endUpdate();
16820     },
16821
16822     // private
16823     onEmptyResults : function(){
16824         
16825         if(this.tickable && this.editable){
16826             this.hasFocus = false;
16827             this.restrictHeight();
16828             return;
16829         }
16830         
16831         this.collapse();
16832     },
16833
16834     /**
16835      * Returns true if the dropdown list is expanded, else false.
16836      */
16837     isExpanded : function(){
16838         return this.list.isVisible();
16839     },
16840
16841     /**
16842      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16843      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16844      * @param {String} value The data value of the item to select
16845      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16846      * selected item if it is not currently in view (defaults to true)
16847      * @return {Boolean} True if the value matched an item in the list, else false
16848      */
16849     selectByValue : function(v, scrollIntoView){
16850         if(v !== undefined && v !== null){
16851             var r = this.findRecord(this.valueField || this.displayField, v);
16852             if(r){
16853                 this.select(this.store.indexOf(r), scrollIntoView);
16854                 return true;
16855             }
16856         }
16857         return false;
16858     },
16859
16860     /**
16861      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16862      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16863      * @param {Number} index The zero-based index of the list item to select
16864      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16865      * selected item if it is not currently in view (defaults to true)
16866      */
16867     select : function(index, scrollIntoView){
16868         this.selectedIndex = index;
16869         this.view.select(index);
16870         if(scrollIntoView !== false){
16871             var el = this.view.getNode(index);
16872             /*
16873              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16874              */
16875             if(el){
16876                 this.list.scrollChildIntoView(el, false);
16877             }
16878         }
16879     },
16880
16881     // private
16882     selectNext : function(){
16883         var ct = this.store.getCount();
16884         if(ct > 0){
16885             if(this.selectedIndex == -1){
16886                 this.select(0);
16887             }else if(this.selectedIndex < ct-1){
16888                 this.select(this.selectedIndex+1);
16889             }
16890         }
16891     },
16892
16893     // private
16894     selectPrev : function(){
16895         var ct = this.store.getCount();
16896         if(ct > 0){
16897             if(this.selectedIndex == -1){
16898                 this.select(0);
16899             }else if(this.selectedIndex != 0){
16900                 this.select(this.selectedIndex-1);
16901             }
16902         }
16903     },
16904
16905     // private
16906     onKeyUp : function(e){
16907         if(this.editable !== false && !e.isSpecialKey()){
16908             this.lastKey = e.getKey();
16909             this.dqTask.delay(this.queryDelay);
16910         }
16911     },
16912
16913     // private
16914     validateBlur : function(){
16915         return !this.list || !this.list.isVisible();   
16916     },
16917
16918     // private
16919     initQuery : function(){
16920         
16921         var v = this.getRawValue();
16922         
16923         if(this.tickable && this.editable){
16924             v = this.tickableInputEl().getValue();
16925         }
16926         
16927         this.doQuery(v);
16928     },
16929
16930     // private
16931     doForce : function(){
16932         if(this.inputEl().dom.value.length > 0){
16933             this.inputEl().dom.value =
16934                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16935              
16936         }
16937     },
16938
16939     /**
16940      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16941      * query allowing the query action to be canceled if needed.
16942      * @param {String} query The SQL query to execute
16943      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16944      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16945      * saved in the current store (defaults to false)
16946      */
16947     doQuery : function(q, forceAll){
16948         
16949         if(q === undefined || q === null){
16950             q = '';
16951         }
16952         var qe = {
16953             query: q,
16954             forceAll: forceAll,
16955             combo: this,
16956             cancel:false
16957         };
16958         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16959             return false;
16960         }
16961         q = qe.query;
16962         
16963         forceAll = qe.forceAll;
16964         if(forceAll === true || (q.length >= this.minChars)){
16965             
16966             this.hasQuery = true;
16967             
16968             if(this.lastQuery != q || this.alwaysQuery){
16969                 this.lastQuery = q;
16970                 if(this.mode == 'local'){
16971                     this.selectedIndex = -1;
16972                     if(forceAll){
16973                         this.store.clearFilter();
16974                     }else{
16975                         
16976                         if(this.specialFilter){
16977                             this.fireEvent('specialfilter', this);
16978                             this.onLoad();
16979                             return;
16980                         }
16981                         
16982                         this.store.filter(this.displayField, q);
16983                     }
16984                     
16985                     this.store.fireEvent("datachanged", this.store);
16986                     
16987                     this.onLoad();
16988                     
16989                     
16990                 }else{
16991                     
16992                     this.store.baseParams[this.queryParam] = q;
16993                     
16994                     var options = {params : this.getParams(q)};
16995                     
16996                     if(this.loadNext){
16997                         options.add = true;
16998                         options.params.start = this.page * this.pageSize;
16999                     }
17000                     
17001                     this.store.load(options);
17002                     
17003                     /*
17004                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
17005                      *  we should expand the list on onLoad
17006                      *  so command out it
17007                      */
17008 //                    this.expand();
17009                 }
17010             }else{
17011                 this.selectedIndex = -1;
17012                 this.onLoad();   
17013             }
17014         }
17015         
17016         this.loadNext = false;
17017     },
17018     
17019     // private
17020     getParams : function(q){
17021         var p = {};
17022         //p[this.queryParam] = q;
17023         
17024         if(this.pageSize){
17025             p.start = 0;
17026             p.limit = this.pageSize;
17027         }
17028         return p;
17029     },
17030
17031     /**
17032      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17033      */
17034     collapse : function(){
17035         if(!this.isExpanded()){
17036             return;
17037         }
17038         
17039         this.list.hide();
17040         
17041         this.hasFocus = false;
17042         
17043         if(this.tickable){
17044             this.okBtn.hide();
17045             this.cancelBtn.hide();
17046             this.trigger.show();
17047             
17048             if(this.editable){
17049                 this.tickableInputEl().dom.value = '';
17050                 this.tickableInputEl().blur();
17051             }
17052             
17053         }
17054         
17055         Roo.get(document).un('mousedown', this.collapseIf, this);
17056         Roo.get(document).un('mousewheel', this.collapseIf, this);
17057         if (!this.editable) {
17058             Roo.get(document).un('keydown', this.listKeyPress, this);
17059         }
17060         this.fireEvent('collapse', this);
17061         
17062         this.validate();
17063     },
17064
17065     // private
17066     collapseIf : function(e){
17067         var in_combo  = e.within(this.el);
17068         var in_list =  e.within(this.list);
17069         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17070         
17071         if (in_combo || in_list || is_list) {
17072             //e.stopPropagation();
17073             return;
17074         }
17075         
17076         if(this.tickable){
17077             this.onTickableFooterButtonClick(e, false, false);
17078         }
17079
17080         this.collapse();
17081         
17082     },
17083
17084     /**
17085      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17086      */
17087     expand : function(){
17088        
17089         if(this.isExpanded() || !this.hasFocus){
17090             return;
17091         }
17092         
17093         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17094         this.list.setWidth(lw);
17095         
17096         Roo.log('expand');
17097         
17098         this.list.show();
17099         
17100         this.restrictHeight();
17101         
17102         if(this.tickable){
17103             
17104             this.tickItems = Roo.apply([], this.item);
17105             
17106             this.okBtn.show();
17107             this.cancelBtn.show();
17108             this.trigger.hide();
17109             
17110             if(this.editable){
17111                 this.tickableInputEl().focus();
17112             }
17113             
17114         }
17115         
17116         Roo.get(document).on('mousedown', this.collapseIf, this);
17117         Roo.get(document).on('mousewheel', this.collapseIf, this);
17118         if (!this.editable) {
17119             Roo.get(document).on('keydown', this.listKeyPress, this);
17120         }
17121         
17122         this.fireEvent('expand', this);
17123     },
17124
17125     // private
17126     // Implements the default empty TriggerField.onTriggerClick function
17127     onTriggerClick : function(e)
17128     {
17129         Roo.log('trigger click');
17130         
17131         if(this.disabled || !this.triggerList){
17132             return;
17133         }
17134         
17135         this.page = 0;
17136         this.loadNext = false;
17137         
17138         if(this.isExpanded()){
17139             this.collapse();
17140             if (!this.blockFocus) {
17141                 this.inputEl().focus();
17142             }
17143             
17144         }else {
17145             this.hasFocus = true;
17146             if(this.triggerAction == 'all') {
17147                 this.doQuery(this.allQuery, true);
17148             } else {
17149                 this.doQuery(this.getRawValue());
17150             }
17151             if (!this.blockFocus) {
17152                 this.inputEl().focus();
17153             }
17154         }
17155     },
17156     
17157     onTickableTriggerClick : function(e)
17158     {
17159         if(this.disabled){
17160             return;
17161         }
17162         
17163         this.page = 0;
17164         this.loadNext = false;
17165         this.hasFocus = true;
17166         
17167         if(this.triggerAction == 'all') {
17168             this.doQuery(this.allQuery, true);
17169         } else {
17170             this.doQuery(this.getRawValue());
17171         }
17172     },
17173     
17174     onSearchFieldClick : function(e)
17175     {
17176         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17177             this.onTickableFooterButtonClick(e, false, false);
17178             return;
17179         }
17180         
17181         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17182             return;
17183         }
17184         
17185         this.page = 0;
17186         this.loadNext = false;
17187         this.hasFocus = true;
17188         
17189         if(this.triggerAction == 'all') {
17190             this.doQuery(this.allQuery, true);
17191         } else {
17192             this.doQuery(this.getRawValue());
17193         }
17194     },
17195     
17196     listKeyPress : function(e)
17197     {
17198         //Roo.log('listkeypress');
17199         // scroll to first matching element based on key pres..
17200         if (e.isSpecialKey()) {
17201             return false;
17202         }
17203         var k = String.fromCharCode(e.getKey()).toUpperCase();
17204         //Roo.log(k);
17205         var match  = false;
17206         var csel = this.view.getSelectedNodes();
17207         var cselitem = false;
17208         if (csel.length) {
17209             var ix = this.view.indexOf(csel[0]);
17210             cselitem  = this.store.getAt(ix);
17211             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17212                 cselitem = false;
17213             }
17214             
17215         }
17216         
17217         this.store.each(function(v) { 
17218             if (cselitem) {
17219                 // start at existing selection.
17220                 if (cselitem.id == v.id) {
17221                     cselitem = false;
17222                 }
17223                 return true;
17224             }
17225                 
17226             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17227                 match = this.store.indexOf(v);
17228                 return false;
17229             }
17230             return true;
17231         }, this);
17232         
17233         if (match === false) {
17234             return true; // no more action?
17235         }
17236         // scroll to?
17237         this.view.select(match);
17238         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17239         sn.scrollIntoView(sn.dom.parentNode, false);
17240     },
17241     
17242     onViewScroll : function(e, t){
17243         
17244         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){
17245             return;
17246         }
17247         
17248         this.hasQuery = true;
17249         
17250         this.loading = this.list.select('.loading', true).first();
17251         
17252         if(this.loading === null){
17253             this.list.createChild({
17254                 tag: 'div',
17255                 cls: 'loading roo-select2-more-results roo-select2-active',
17256                 html: 'Loading more results...'
17257             });
17258             
17259             this.loading = this.list.select('.loading', true).first();
17260             
17261             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17262             
17263             this.loading.hide();
17264         }
17265         
17266         this.loading.show();
17267         
17268         var _combo = this;
17269         
17270         this.page++;
17271         this.loadNext = true;
17272         
17273         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17274         
17275         return;
17276     },
17277     
17278     addItem : function(o)
17279     {   
17280         var dv = ''; // display value
17281         
17282         if (this.displayField) {
17283             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17284         } else {
17285             // this is an error condition!!!
17286             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17287         }
17288         
17289         if(!dv.length){
17290             return;
17291         }
17292         
17293         var choice = this.choices.createChild({
17294             tag: 'li',
17295             cls: 'roo-select2-search-choice',
17296             cn: [
17297                 {
17298                     tag: 'div',
17299                     html: dv
17300                 },
17301                 {
17302                     tag: 'a',
17303                     href: '#',
17304                     cls: 'roo-select2-search-choice-close fa fa-times',
17305                     tabindex: '-1'
17306                 }
17307             ]
17308             
17309         }, this.searchField);
17310         
17311         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17312         
17313         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17314         
17315         this.item.push(o);
17316         
17317         this.lastData = o;
17318         
17319         this.syncValue();
17320         
17321         this.inputEl().dom.value = '';
17322         
17323         this.validate();
17324     },
17325     
17326     onRemoveItem : function(e, _self, o)
17327     {
17328         e.preventDefault();
17329         
17330         this.lastItem = Roo.apply([], this.item);
17331         
17332         var index = this.item.indexOf(o.data) * 1;
17333         
17334         if( index < 0){
17335             Roo.log('not this item?!');
17336             return;
17337         }
17338         
17339         this.item.splice(index, 1);
17340         o.item.remove();
17341         
17342         this.syncValue();
17343         
17344         this.fireEvent('remove', this, e);
17345         
17346         this.validate();
17347         
17348     },
17349     
17350     syncValue : function()
17351     {
17352         if(!this.item.length){
17353             this.clearValue();
17354             return;
17355         }
17356             
17357         var value = [];
17358         var _this = this;
17359         Roo.each(this.item, function(i){
17360             if(_this.valueField){
17361                 value.push(i[_this.valueField]);
17362                 return;
17363             }
17364
17365             value.push(i);
17366         });
17367
17368         this.value = value.join(',');
17369
17370         if(this.hiddenField){
17371             this.hiddenField.dom.value = this.value;
17372         }
17373         
17374         this.store.fireEvent("datachanged", this.store);
17375         
17376         this.validate();
17377     },
17378     
17379     clearItem : function()
17380     {
17381         if(!this.multiple){
17382             return;
17383         }
17384         
17385         this.item = [];
17386         
17387         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17388            c.remove();
17389         });
17390         
17391         this.syncValue();
17392         
17393         this.validate();
17394         
17395         if(this.tickable && !Roo.isTouch){
17396             this.view.refresh();
17397         }
17398     },
17399     
17400     inputEl: function ()
17401     {
17402         if(Roo.isIOS && this.useNativeIOS){
17403             return this.el.select('select.roo-ios-select', true).first();
17404         }
17405         
17406         if(Roo.isTouch && this.mobileTouchView){
17407             return this.el.select('input.form-control',true).first();
17408         }
17409         
17410         if(this.tickable){
17411             return this.searchField;
17412         }
17413         
17414         return this.el.select('input.form-control',true).first();
17415     },
17416     
17417     onTickableFooterButtonClick : function(e, btn, el)
17418     {
17419         e.preventDefault();
17420         
17421         this.lastItem = Roo.apply([], this.item);
17422         
17423         if(btn && btn.name == 'cancel'){
17424             this.tickItems = Roo.apply([], this.item);
17425             this.collapse();
17426             return;
17427         }
17428         
17429         this.clearItem();
17430         
17431         var _this = this;
17432         
17433         Roo.each(this.tickItems, function(o){
17434             _this.addItem(o);
17435         });
17436         
17437         this.collapse();
17438         
17439     },
17440     
17441     validate : function()
17442     {
17443         if(this.getVisibilityEl().hasClass('hidden')){
17444             return true;
17445         }
17446         
17447         var v = this.getRawValue();
17448         
17449         if(this.multiple){
17450             v = this.getValue();
17451         }
17452         
17453         if(this.disabled || this.allowBlank || v.length){
17454             this.markValid();
17455             return true;
17456         }
17457         
17458         this.markInvalid();
17459         return false;
17460     },
17461     
17462     tickableInputEl : function()
17463     {
17464         if(!this.tickable || !this.editable){
17465             return this.inputEl();
17466         }
17467         
17468         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17469     },
17470     
17471     
17472     getAutoCreateTouchView : function()
17473     {
17474         var id = Roo.id();
17475         
17476         var cfg = {
17477             cls: 'form-group' //input-group
17478         };
17479         
17480         var input =  {
17481             tag: 'input',
17482             id : id,
17483             type : this.inputType,
17484             cls : 'form-control x-combo-noedit',
17485             autocomplete: 'new-password',
17486             placeholder : this.placeholder || '',
17487             readonly : true
17488         };
17489         
17490         if (this.name) {
17491             input.name = this.name;
17492         }
17493         
17494         if (this.size) {
17495             input.cls += ' input-' + this.size;
17496         }
17497         
17498         if (this.disabled) {
17499             input.disabled = true;
17500         }
17501         
17502         var inputblock = {
17503             cls : 'roo-combobox-wrap',
17504             cn : [
17505                 input
17506             ]
17507         };
17508         
17509         if(this.before){
17510             inputblock.cls += ' input-group';
17511             
17512             inputblock.cn.unshift({
17513                 tag :'span',
17514                 cls : 'input-group-addon input-group-prepend input-group-text',
17515                 html : this.before
17516             });
17517         }
17518         
17519         if(this.removable && !this.multiple){
17520             inputblock.cls += ' roo-removable';
17521             
17522             inputblock.cn.push({
17523                 tag: 'button',
17524                 html : 'x',
17525                 cls : 'roo-combo-removable-btn close'
17526             });
17527         }
17528
17529         if(this.hasFeedback && !this.allowBlank){
17530             
17531             inputblock.cls += ' has-feedback';
17532             
17533             inputblock.cn.push({
17534                 tag: 'span',
17535                 cls: 'glyphicon form-control-feedback'
17536             });
17537             
17538         }
17539         
17540         if (this.after) {
17541             
17542             inputblock.cls += (this.before) ? '' : ' input-group';
17543             
17544             inputblock.cn.push({
17545                 tag :'span',
17546                 cls : 'input-group-addon input-group-append input-group-text',
17547                 html : this.after
17548             });
17549         }
17550
17551         
17552         var ibwrap = inputblock;
17553         
17554         if(this.multiple){
17555             ibwrap = {
17556                 tag: 'ul',
17557                 cls: 'roo-select2-choices',
17558                 cn:[
17559                     {
17560                         tag: 'li',
17561                         cls: 'roo-select2-search-field',
17562                         cn: [
17563
17564                             inputblock
17565                         ]
17566                     }
17567                 ]
17568             };
17569         
17570             
17571         }
17572         
17573         var combobox = {
17574             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17575             cn: [
17576                 {
17577                     tag: 'input',
17578                     type : 'hidden',
17579                     cls: 'form-hidden-field'
17580                 },
17581                 ibwrap
17582             ]
17583         };
17584         
17585         if(!this.multiple && this.showToggleBtn){
17586             
17587             var caret = {
17588                 cls: 'caret'
17589             };
17590             
17591             if (this.caret != false) {
17592                 caret = {
17593                      tag: 'i',
17594                      cls: 'fa fa-' + this.caret
17595                 };
17596                 
17597             }
17598             
17599             combobox.cn.push({
17600                 tag :'span',
17601                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17602                 cn : [
17603                     Roo.bootstrap.version == 3 ? caret : '',
17604                     {
17605                         tag: 'span',
17606                         cls: 'combobox-clear',
17607                         cn  : [
17608                             {
17609                                 tag : 'i',
17610                                 cls: 'icon-remove'
17611                             }
17612                         ]
17613                     }
17614                 ]
17615
17616             })
17617         }
17618         
17619         if(this.multiple){
17620             combobox.cls += ' roo-select2-container-multi';
17621         }
17622         
17623         var required =  this.allowBlank ?  {
17624                     tag : 'i',
17625                     style: 'display: none'
17626                 } : {
17627                    tag : 'i',
17628                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17629                    tooltip : 'This field is required'
17630                 };
17631         
17632         var align = this.labelAlign || this.parentLabelAlign();
17633         
17634         if (align ==='left' && this.fieldLabel.length) {
17635
17636             cfg.cn = [
17637                 required,
17638                 {
17639                     tag: 'label',
17640                     cls : 'control-label col-form-label',
17641                     html : this.fieldLabel
17642
17643                 },
17644                 {
17645                     cls : 'roo-combobox-wrap ', 
17646                     cn: [
17647                         combobox
17648                     ]
17649                 }
17650             ];
17651             
17652             var labelCfg = cfg.cn[1];
17653             var contentCfg = cfg.cn[2];
17654             
17655
17656             if(this.indicatorpos == 'right'){
17657                 cfg.cn = [
17658                     {
17659                         tag: 'label',
17660                         'for' :  id,
17661                         cls : 'control-label col-form-label',
17662                         cn : [
17663                             {
17664                                 tag : 'span',
17665                                 html : this.fieldLabel
17666                             },
17667                             required
17668                         ]
17669                     },
17670                     {
17671                         cls : "roo-combobox-wrap ",
17672                         cn: [
17673                             combobox
17674                         ]
17675                     }
17676
17677                 ];
17678                 
17679                 labelCfg = cfg.cn[0];
17680                 contentCfg = cfg.cn[1];
17681             }
17682             
17683            
17684             
17685             if(this.labelWidth > 12){
17686                 labelCfg.style = "width: " + this.labelWidth + 'px';
17687             }
17688            
17689             if(this.labelWidth < 13 && this.labelmd == 0){
17690                 this.labelmd = this.labelWidth;
17691             }
17692             
17693             if(this.labellg > 0){
17694                 labelCfg.cls += ' col-lg-' + this.labellg;
17695                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17696             }
17697             
17698             if(this.labelmd > 0){
17699                 labelCfg.cls += ' col-md-' + this.labelmd;
17700                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17701             }
17702             
17703             if(this.labelsm > 0){
17704                 labelCfg.cls += ' col-sm-' + this.labelsm;
17705                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17706             }
17707             
17708             if(this.labelxs > 0){
17709                 labelCfg.cls += ' col-xs-' + this.labelxs;
17710                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17711             }
17712                 
17713                 
17714         } else if ( this.fieldLabel.length) {
17715             cfg.cn = [
17716                required,
17717                 {
17718                     tag: 'label',
17719                     cls : 'control-label',
17720                     html : this.fieldLabel
17721
17722                 },
17723                 {
17724                     cls : '', 
17725                     cn: [
17726                         combobox
17727                     ]
17728                 }
17729             ];
17730             
17731             if(this.indicatorpos == 'right'){
17732                 cfg.cn = [
17733                     {
17734                         tag: 'label',
17735                         cls : 'control-label',
17736                         html : this.fieldLabel,
17737                         cn : [
17738                             required
17739                         ]
17740                     },
17741                     {
17742                         cls : '', 
17743                         cn: [
17744                             combobox
17745                         ]
17746                     }
17747                 ];
17748             }
17749         } else {
17750             cfg.cn = combobox;    
17751         }
17752         
17753         
17754         var settings = this;
17755         
17756         ['xs','sm','md','lg'].map(function(size){
17757             if (settings[size]) {
17758                 cfg.cls += ' col-' + size + '-' + settings[size];
17759             }
17760         });
17761         
17762         return cfg;
17763     },
17764     
17765     initTouchView : function()
17766     {
17767         this.renderTouchView();
17768         
17769         this.touchViewEl.on('scroll', function(){
17770             this.el.dom.scrollTop = 0;
17771         }, this);
17772         
17773         this.originalValue = this.getValue();
17774         
17775         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17776         
17777         this.inputEl().on("click", this.showTouchView, this);
17778         if (this.triggerEl) {
17779             this.triggerEl.on("click", this.showTouchView, this);
17780         }
17781         
17782         
17783         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17784         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17785         
17786         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17787         
17788         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17789         this.store.on('load', this.onTouchViewLoad, this);
17790         this.store.on('loadexception', this.onTouchViewLoadException, this);
17791         
17792         if(this.hiddenName){
17793             
17794             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17795             
17796             this.hiddenField.dom.value =
17797                 this.hiddenValue !== undefined ? this.hiddenValue :
17798                 this.value !== undefined ? this.value : '';
17799         
17800             this.el.dom.removeAttribute('name');
17801             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17802         }
17803         
17804         if(this.multiple){
17805             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17806             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17807         }
17808         
17809         if(this.removable && !this.multiple){
17810             var close = this.closeTriggerEl();
17811             if(close){
17812                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17813                 close.on('click', this.removeBtnClick, this, close);
17814             }
17815         }
17816         /*
17817          * fix the bug in Safari iOS8
17818          */
17819         this.inputEl().on("focus", function(e){
17820             document.activeElement.blur();
17821         }, this);
17822         
17823         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17824         
17825         return;
17826         
17827         
17828     },
17829     
17830     renderTouchView : function()
17831     {
17832         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17833         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17834         
17835         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17836         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17837         
17838         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17839         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17840         this.touchViewBodyEl.setStyle('overflow', 'auto');
17841         
17842         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17843         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17844         
17845         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17846         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17847         
17848     },
17849     
17850     showTouchView : function()
17851     {
17852         if(this.disabled){
17853             return;
17854         }
17855         
17856         this.touchViewHeaderEl.hide();
17857
17858         if(this.modalTitle.length){
17859             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17860             this.touchViewHeaderEl.show();
17861         }
17862
17863         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17864         this.touchViewEl.show();
17865
17866         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17867         
17868         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17869         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17870
17871         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17872
17873         if(this.modalTitle.length){
17874             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17875         }
17876         
17877         this.touchViewBodyEl.setHeight(bodyHeight);
17878
17879         if(this.animate){
17880             var _this = this;
17881             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17882         }else{
17883             this.touchViewEl.addClass(['in','show']);
17884         }
17885         
17886         if(this._touchViewMask){
17887             Roo.get(document.body).addClass("x-body-masked");
17888             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17889             this._touchViewMask.setStyle('z-index', 10000);
17890             this._touchViewMask.addClass('show');
17891         }
17892         
17893         this.doTouchViewQuery();
17894         
17895     },
17896     
17897     hideTouchView : function()
17898     {
17899         this.touchViewEl.removeClass(['in','show']);
17900
17901         if(this.animate){
17902             var _this = this;
17903             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17904         }else{
17905             this.touchViewEl.setStyle('display', 'none');
17906         }
17907         
17908         if(this._touchViewMask){
17909             this._touchViewMask.removeClass('show');
17910             Roo.get(document.body).removeClass("x-body-masked");
17911         }
17912     },
17913     
17914     setTouchViewValue : function()
17915     {
17916         if(this.multiple){
17917             this.clearItem();
17918         
17919             var _this = this;
17920
17921             Roo.each(this.tickItems, function(o){
17922                 this.addItem(o);
17923             }, this);
17924         }
17925         
17926         this.hideTouchView();
17927     },
17928     
17929     doTouchViewQuery : function()
17930     {
17931         var qe = {
17932             query: '',
17933             forceAll: true,
17934             combo: this,
17935             cancel:false
17936         };
17937         
17938         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17939             return false;
17940         }
17941         
17942         if(!this.alwaysQuery || this.mode == 'local'){
17943             this.onTouchViewLoad();
17944             return;
17945         }
17946         
17947         this.store.load();
17948     },
17949     
17950     onTouchViewBeforeLoad : function(combo,opts)
17951     {
17952         return;
17953     },
17954
17955     // private
17956     onTouchViewLoad : function()
17957     {
17958         if(this.store.getCount() < 1){
17959             this.onTouchViewEmptyResults();
17960             return;
17961         }
17962         
17963         this.clearTouchView();
17964         
17965         var rawValue = this.getRawValue();
17966         
17967         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17968         
17969         this.tickItems = [];
17970         
17971         this.store.data.each(function(d, rowIndex){
17972             var row = this.touchViewListGroup.createChild(template);
17973             
17974             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17975                 row.addClass(d.data.cls);
17976             }
17977             
17978             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17979                 var cfg = {
17980                     data : d.data,
17981                     html : d.data[this.displayField]
17982                 };
17983                 
17984                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17985                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17986                 }
17987             }
17988             row.removeClass('selected');
17989             if(!this.multiple && this.valueField &&
17990                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17991             {
17992                 // radio buttons..
17993                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17994                 row.addClass('selected');
17995             }
17996             
17997             if(this.multiple && this.valueField &&
17998                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17999             {
18000                 
18001                 // checkboxes...
18002                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18003                 this.tickItems.push(d.data);
18004             }
18005             
18006             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18007             
18008         }, this);
18009         
18010         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18011         
18012         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18013
18014         if(this.modalTitle.length){
18015             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18016         }
18017
18018         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18019         
18020         if(this.mobile_restrict_height && listHeight < bodyHeight){
18021             this.touchViewBodyEl.setHeight(listHeight);
18022         }
18023         
18024         var _this = this;
18025         
18026         if(firstChecked && listHeight > bodyHeight){
18027             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18028         }
18029         
18030     },
18031     
18032     onTouchViewLoadException : function()
18033     {
18034         this.hideTouchView();
18035     },
18036     
18037     onTouchViewEmptyResults : function()
18038     {
18039         this.clearTouchView();
18040         
18041         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18042         
18043         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18044         
18045     },
18046     
18047     clearTouchView : function()
18048     {
18049         this.touchViewListGroup.dom.innerHTML = '';
18050     },
18051     
18052     onTouchViewClick : function(e, el, o)
18053     {
18054         e.preventDefault();
18055         
18056         var row = o.row;
18057         var rowIndex = o.rowIndex;
18058         
18059         var r = this.store.getAt(rowIndex);
18060         
18061         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18062             
18063             if(!this.multiple){
18064                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18065                     c.dom.removeAttribute('checked');
18066                 }, this);
18067
18068                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18069
18070                 this.setFromData(r.data);
18071
18072                 var close = this.closeTriggerEl();
18073
18074                 if(close){
18075                     close.show();
18076                 }
18077
18078                 this.hideTouchView();
18079
18080                 this.fireEvent('select', this, r, rowIndex);
18081
18082                 return;
18083             }
18084
18085             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18086                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18087                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18088                 return;
18089             }
18090
18091             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18092             this.addItem(r.data);
18093             this.tickItems.push(r.data);
18094         }
18095     },
18096     
18097     getAutoCreateNativeIOS : function()
18098     {
18099         var cfg = {
18100             cls: 'form-group' //input-group,
18101         };
18102         
18103         var combobox =  {
18104             tag: 'select',
18105             cls : 'roo-ios-select'
18106         };
18107         
18108         if (this.name) {
18109             combobox.name = this.name;
18110         }
18111         
18112         if (this.disabled) {
18113             combobox.disabled = true;
18114         }
18115         
18116         var settings = this;
18117         
18118         ['xs','sm','md','lg'].map(function(size){
18119             if (settings[size]) {
18120                 cfg.cls += ' col-' + size + '-' + settings[size];
18121             }
18122         });
18123         
18124         cfg.cn = combobox;
18125         
18126         return cfg;
18127         
18128     },
18129     
18130     initIOSView : function()
18131     {
18132         this.store.on('load', this.onIOSViewLoad, this);
18133         
18134         return;
18135     },
18136     
18137     onIOSViewLoad : function()
18138     {
18139         if(this.store.getCount() < 1){
18140             return;
18141         }
18142         
18143         this.clearIOSView();
18144         
18145         if(this.allowBlank) {
18146             
18147             var default_text = '-- SELECT --';
18148             
18149             if(this.placeholder.length){
18150                 default_text = this.placeholder;
18151             }
18152             
18153             if(this.emptyTitle.length){
18154                 default_text += ' - ' + this.emptyTitle + ' -';
18155             }
18156             
18157             var opt = this.inputEl().createChild({
18158                 tag: 'option',
18159                 value : 0,
18160                 html : default_text
18161             });
18162             
18163             var o = {};
18164             o[this.valueField] = 0;
18165             o[this.displayField] = default_text;
18166             
18167             this.ios_options.push({
18168                 data : o,
18169                 el : opt
18170             });
18171             
18172         }
18173         
18174         this.store.data.each(function(d, rowIndex){
18175             
18176             var html = '';
18177             
18178             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18179                 html = d.data[this.displayField];
18180             }
18181             
18182             var value = '';
18183             
18184             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18185                 value = d.data[this.valueField];
18186             }
18187             
18188             var option = {
18189                 tag: 'option',
18190                 value : value,
18191                 html : html
18192             };
18193             
18194             if(this.value == d.data[this.valueField]){
18195                 option['selected'] = true;
18196             }
18197             
18198             var opt = this.inputEl().createChild(option);
18199             
18200             this.ios_options.push({
18201                 data : d.data,
18202                 el : opt
18203             });
18204             
18205         }, this);
18206         
18207         this.inputEl().on('change', function(){
18208            this.fireEvent('select', this);
18209         }, this);
18210         
18211     },
18212     
18213     clearIOSView: function()
18214     {
18215         this.inputEl().dom.innerHTML = '';
18216         
18217         this.ios_options = [];
18218     },
18219     
18220     setIOSValue: function(v)
18221     {
18222         this.value = v;
18223         
18224         if(!this.ios_options){
18225             return;
18226         }
18227         
18228         Roo.each(this.ios_options, function(opts){
18229            
18230            opts.el.dom.removeAttribute('selected');
18231            
18232            if(opts.data[this.valueField] != v){
18233                return;
18234            }
18235            
18236            opts.el.dom.setAttribute('selected', true);
18237            
18238         }, this);
18239     }
18240
18241     /** 
18242     * @cfg {Boolean} grow 
18243     * @hide 
18244     */
18245     /** 
18246     * @cfg {Number} growMin 
18247     * @hide 
18248     */
18249     /** 
18250     * @cfg {Number} growMax 
18251     * @hide 
18252     */
18253     /**
18254      * @hide
18255      * @method autoSize
18256      */
18257 });
18258
18259 Roo.apply(Roo.bootstrap.ComboBox,  {
18260     
18261     header : {
18262         tag: 'div',
18263         cls: 'modal-header',
18264         cn: [
18265             {
18266                 tag: 'h4',
18267                 cls: 'modal-title'
18268             }
18269         ]
18270     },
18271     
18272     body : {
18273         tag: 'div',
18274         cls: 'modal-body',
18275         cn: [
18276             {
18277                 tag: 'ul',
18278                 cls: 'list-group'
18279             }
18280         ]
18281     },
18282     
18283     listItemRadio : {
18284         tag: 'li',
18285         cls: 'list-group-item',
18286         cn: [
18287             {
18288                 tag: 'span',
18289                 cls: 'roo-combobox-list-group-item-value'
18290             },
18291             {
18292                 tag: 'div',
18293                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18294                 cn: [
18295                     {
18296                         tag: 'input',
18297                         type: 'radio'
18298                     },
18299                     {
18300                         tag: 'label'
18301                     }
18302                 ]
18303             }
18304         ]
18305     },
18306     
18307     listItemCheckbox : {
18308         tag: 'li',
18309         cls: 'list-group-item',
18310         cn: [
18311             {
18312                 tag: 'span',
18313                 cls: 'roo-combobox-list-group-item-value'
18314             },
18315             {
18316                 tag: 'div',
18317                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18318                 cn: [
18319                     {
18320                         tag: 'input',
18321                         type: 'checkbox'
18322                     },
18323                     {
18324                         tag: 'label'
18325                     }
18326                 ]
18327             }
18328         ]
18329     },
18330     
18331     emptyResult : {
18332         tag: 'div',
18333         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18334     },
18335     
18336     footer : {
18337         tag: 'div',
18338         cls: 'modal-footer',
18339         cn: [
18340             {
18341                 tag: 'div',
18342                 cls: 'row',
18343                 cn: [
18344                     {
18345                         tag: 'div',
18346                         cls: 'col-xs-6 text-left',
18347                         cn: {
18348                             tag: 'button',
18349                             cls: 'btn btn-danger roo-touch-view-cancel',
18350                             html: 'Cancel'
18351                         }
18352                     },
18353                     {
18354                         tag: 'div',
18355                         cls: 'col-xs-6 text-right',
18356                         cn: {
18357                             tag: 'button',
18358                             cls: 'btn btn-success roo-touch-view-ok',
18359                             html: 'OK'
18360                         }
18361                     }
18362                 ]
18363             }
18364         ]
18365         
18366     }
18367 });
18368
18369 Roo.apply(Roo.bootstrap.ComboBox,  {
18370     
18371     touchViewTemplate : {
18372         tag: 'div',
18373         cls: 'modal fade roo-combobox-touch-view',
18374         cn: [
18375             {
18376                 tag: 'div',
18377                 cls: 'modal-dialog',
18378                 style : 'position:fixed', // we have to fix position....
18379                 cn: [
18380                     {
18381                         tag: 'div',
18382                         cls: 'modal-content',
18383                         cn: [
18384                             Roo.bootstrap.ComboBox.header,
18385                             Roo.bootstrap.ComboBox.body,
18386                             Roo.bootstrap.ComboBox.footer
18387                         ]
18388                     }
18389                 ]
18390             }
18391         ]
18392     }
18393 });/*
18394  * Based on:
18395  * Ext JS Library 1.1.1
18396  * Copyright(c) 2006-2007, Ext JS, LLC.
18397  *
18398  * Originally Released Under LGPL - original licence link has changed is not relivant.
18399  *
18400  * Fork - LGPL
18401  * <script type="text/javascript">
18402  */
18403
18404 /**
18405  * @class Roo.View
18406  * @extends Roo.util.Observable
18407  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18408  * This class also supports single and multi selection modes. <br>
18409  * Create a data model bound view:
18410  <pre><code>
18411  var store = new Roo.data.Store(...);
18412
18413  var view = new Roo.View({
18414     el : "my-element",
18415     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18416  
18417     singleSelect: true,
18418     selectedClass: "ydataview-selected",
18419     store: store
18420  });
18421
18422  // listen for node click?
18423  view.on("click", function(vw, index, node, e){
18424  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18425  });
18426
18427  // load XML data
18428  dataModel.load("foobar.xml");
18429  </code></pre>
18430  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18431  * <br><br>
18432  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18433  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18434  * 
18435  * Note: old style constructor is still suported (container, template, config)
18436  * 
18437  * @constructor
18438  * Create a new View
18439  * @param {Object} config The config object
18440  * 
18441  */
18442 Roo.View = function(config, depreciated_tpl, depreciated_config){
18443     
18444     this.parent = false;
18445     
18446     if (typeof(depreciated_tpl) == 'undefined') {
18447         // new way.. - universal constructor.
18448         Roo.apply(this, config);
18449         this.el  = Roo.get(this.el);
18450     } else {
18451         // old format..
18452         this.el  = Roo.get(config);
18453         this.tpl = depreciated_tpl;
18454         Roo.apply(this, depreciated_config);
18455     }
18456     this.wrapEl  = this.el.wrap().wrap();
18457     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18458     
18459     
18460     if(typeof(this.tpl) == "string"){
18461         this.tpl = new Roo.Template(this.tpl);
18462     } else {
18463         // support xtype ctors..
18464         this.tpl = new Roo.factory(this.tpl, Roo);
18465     }
18466     
18467     
18468     this.tpl.compile();
18469     
18470     /** @private */
18471     this.addEvents({
18472         /**
18473          * @event beforeclick
18474          * Fires before a click is processed. Returns false to cancel the default action.
18475          * @param {Roo.View} this
18476          * @param {Number} index The index of the target node
18477          * @param {HTMLElement} node The target node
18478          * @param {Roo.EventObject} e The raw event object
18479          */
18480             "beforeclick" : true,
18481         /**
18482          * @event click
18483          * Fires when a template node is clicked.
18484          * @param {Roo.View} this
18485          * @param {Number} index The index of the target node
18486          * @param {HTMLElement} node The target node
18487          * @param {Roo.EventObject} e The raw event object
18488          */
18489             "click" : true,
18490         /**
18491          * @event dblclick
18492          * Fires when a template node is double clicked.
18493          * @param {Roo.View} this
18494          * @param {Number} index The index of the target node
18495          * @param {HTMLElement} node The target node
18496          * @param {Roo.EventObject} e The raw event object
18497          */
18498             "dblclick" : true,
18499         /**
18500          * @event contextmenu
18501          * Fires when a template node is right clicked.
18502          * @param {Roo.View} this
18503          * @param {Number} index The index of the target node
18504          * @param {HTMLElement} node The target node
18505          * @param {Roo.EventObject} e The raw event object
18506          */
18507             "contextmenu" : true,
18508         /**
18509          * @event selectionchange
18510          * Fires when the selected nodes change.
18511          * @param {Roo.View} this
18512          * @param {Array} selections Array of the selected nodes
18513          */
18514             "selectionchange" : true,
18515     
18516         /**
18517          * @event beforeselect
18518          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18519          * @param {Roo.View} this
18520          * @param {HTMLElement} node The node to be selected
18521          * @param {Array} selections Array of currently selected nodes
18522          */
18523             "beforeselect" : true,
18524         /**
18525          * @event preparedata
18526          * Fires on every row to render, to allow you to change the data.
18527          * @param {Roo.View} this
18528          * @param {Object} data to be rendered (change this)
18529          */
18530           "preparedata" : true
18531           
18532           
18533         });
18534
18535
18536
18537     this.el.on({
18538         "click": this.onClick,
18539         "dblclick": this.onDblClick,
18540         "contextmenu": this.onContextMenu,
18541         scope:this
18542     });
18543
18544     this.selections = [];
18545     this.nodes = [];
18546     this.cmp = new Roo.CompositeElementLite([]);
18547     if(this.store){
18548         this.store = Roo.factory(this.store, Roo.data);
18549         this.setStore(this.store, true);
18550     }
18551     
18552     if ( this.footer && this.footer.xtype) {
18553            
18554          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18555         
18556         this.footer.dataSource = this.store;
18557         this.footer.container = fctr;
18558         this.footer = Roo.factory(this.footer, Roo);
18559         fctr.insertFirst(this.el);
18560         
18561         // this is a bit insane - as the paging toolbar seems to detach the el..
18562 //        dom.parentNode.parentNode.parentNode
18563          // they get detached?
18564     }
18565     
18566     
18567     Roo.View.superclass.constructor.call(this);
18568     
18569     
18570 };
18571
18572 Roo.extend(Roo.View, Roo.util.Observable, {
18573     
18574      /**
18575      * @cfg {Roo.data.Store} store Data store to load data from.
18576      */
18577     store : false,
18578     
18579     /**
18580      * @cfg {String|Roo.Element} el The container element.
18581      */
18582     el : '',
18583     
18584     /**
18585      * @cfg {String|Roo.Template} tpl The template used by this View 
18586      */
18587     tpl : false,
18588     /**
18589      * @cfg {String} dataName the named area of the template to use as the data area
18590      *                          Works with domtemplates roo-name="name"
18591      */
18592     dataName: false,
18593     /**
18594      * @cfg {String} selectedClass The css class to add to selected nodes
18595      */
18596     selectedClass : "x-view-selected",
18597      /**
18598      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18599      */
18600     emptyText : "",
18601     
18602     /**
18603      * @cfg {String} text to display on mask (default Loading)
18604      */
18605     mask : false,
18606     /**
18607      * @cfg {Boolean} multiSelect Allow multiple selection
18608      */
18609     multiSelect : false,
18610     /**
18611      * @cfg {Boolean} singleSelect Allow single selection
18612      */
18613     singleSelect:  false,
18614     
18615     /**
18616      * @cfg {Boolean} toggleSelect - selecting 
18617      */
18618     toggleSelect : false,
18619     
18620     /**
18621      * @cfg {Boolean} tickable - selecting 
18622      */
18623     tickable : false,
18624     
18625     /**
18626      * Returns the element this view is bound to.
18627      * @return {Roo.Element}
18628      */
18629     getEl : function(){
18630         return this.wrapEl;
18631     },
18632     
18633     
18634
18635     /**
18636      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18637      */
18638     refresh : function(){
18639         //Roo.log('refresh');
18640         var t = this.tpl;
18641         
18642         // if we are using something like 'domtemplate', then
18643         // the what gets used is:
18644         // t.applySubtemplate(NAME, data, wrapping data..)
18645         // the outer template then get' applied with
18646         //     the store 'extra data'
18647         // and the body get's added to the
18648         //      roo-name="data" node?
18649         //      <span class='roo-tpl-{name}'></span> ?????
18650         
18651         
18652         
18653         this.clearSelections();
18654         this.el.update("");
18655         var html = [];
18656         var records = this.store.getRange();
18657         if(records.length < 1) {
18658             
18659             // is this valid??  = should it render a template??
18660             
18661             this.el.update(this.emptyText);
18662             return;
18663         }
18664         var el = this.el;
18665         if (this.dataName) {
18666             this.el.update(t.apply(this.store.meta)); //????
18667             el = this.el.child('.roo-tpl-' + this.dataName);
18668         }
18669         
18670         for(var i = 0, len = records.length; i < len; i++){
18671             var data = this.prepareData(records[i].data, i, records[i]);
18672             this.fireEvent("preparedata", this, data, i, records[i]);
18673             
18674             var d = Roo.apply({}, data);
18675             
18676             if(this.tickable){
18677                 Roo.apply(d, {'roo-id' : Roo.id()});
18678                 
18679                 var _this = this;
18680             
18681                 Roo.each(this.parent.item, function(item){
18682                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18683                         return;
18684                     }
18685                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18686                 });
18687             }
18688             
18689             html[html.length] = Roo.util.Format.trim(
18690                 this.dataName ?
18691                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18692                     t.apply(d)
18693             );
18694         }
18695         
18696         
18697         
18698         el.update(html.join(""));
18699         this.nodes = el.dom.childNodes;
18700         this.updateIndexes(0);
18701     },
18702     
18703
18704     /**
18705      * Function to override to reformat the data that is sent to
18706      * the template for each node.
18707      * DEPRICATED - use the preparedata event handler.
18708      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18709      * a JSON object for an UpdateManager bound view).
18710      */
18711     prepareData : function(data, index, record)
18712     {
18713         this.fireEvent("preparedata", this, data, index, record);
18714         return data;
18715     },
18716
18717     onUpdate : function(ds, record){
18718         // Roo.log('on update');   
18719         this.clearSelections();
18720         var index = this.store.indexOf(record);
18721         var n = this.nodes[index];
18722         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18723         n.parentNode.removeChild(n);
18724         this.updateIndexes(index, index);
18725     },
18726
18727     
18728     
18729 // --------- FIXME     
18730     onAdd : function(ds, records, index)
18731     {
18732         //Roo.log(['on Add', ds, records, index] );        
18733         this.clearSelections();
18734         if(this.nodes.length == 0){
18735             this.refresh();
18736             return;
18737         }
18738         var n = this.nodes[index];
18739         for(var i = 0, len = records.length; i < len; i++){
18740             var d = this.prepareData(records[i].data, i, records[i]);
18741             if(n){
18742                 this.tpl.insertBefore(n, d);
18743             }else{
18744                 
18745                 this.tpl.append(this.el, d);
18746             }
18747         }
18748         this.updateIndexes(index);
18749     },
18750
18751     onRemove : function(ds, record, index){
18752        // Roo.log('onRemove');
18753         this.clearSelections();
18754         var el = this.dataName  ?
18755             this.el.child('.roo-tpl-' + this.dataName) :
18756             this.el; 
18757         
18758         el.dom.removeChild(this.nodes[index]);
18759         this.updateIndexes(index);
18760     },
18761
18762     /**
18763      * Refresh an individual node.
18764      * @param {Number} index
18765      */
18766     refreshNode : function(index){
18767         this.onUpdate(this.store, this.store.getAt(index));
18768     },
18769
18770     updateIndexes : function(startIndex, endIndex){
18771         var ns = this.nodes;
18772         startIndex = startIndex || 0;
18773         endIndex = endIndex || ns.length - 1;
18774         for(var i = startIndex; i <= endIndex; i++){
18775             ns[i].nodeIndex = i;
18776         }
18777     },
18778
18779     /**
18780      * Changes the data store this view uses and refresh the view.
18781      * @param {Store} store
18782      */
18783     setStore : function(store, initial){
18784         if(!initial && this.store){
18785             this.store.un("datachanged", this.refresh);
18786             this.store.un("add", this.onAdd);
18787             this.store.un("remove", this.onRemove);
18788             this.store.un("update", this.onUpdate);
18789             this.store.un("clear", this.refresh);
18790             this.store.un("beforeload", this.onBeforeLoad);
18791             this.store.un("load", this.onLoad);
18792             this.store.un("loadexception", this.onLoad);
18793         }
18794         if(store){
18795           
18796             store.on("datachanged", this.refresh, this);
18797             store.on("add", this.onAdd, this);
18798             store.on("remove", this.onRemove, this);
18799             store.on("update", this.onUpdate, this);
18800             store.on("clear", this.refresh, this);
18801             store.on("beforeload", this.onBeforeLoad, this);
18802             store.on("load", this.onLoad, this);
18803             store.on("loadexception", this.onLoad, this);
18804         }
18805         
18806         if(store){
18807             this.refresh();
18808         }
18809     },
18810     /**
18811      * onbeforeLoad - masks the loading area.
18812      *
18813      */
18814     onBeforeLoad : function(store,opts)
18815     {
18816          //Roo.log('onBeforeLoad');   
18817         if (!opts.add) {
18818             this.el.update("");
18819         }
18820         this.el.mask(this.mask ? this.mask : "Loading" ); 
18821     },
18822     onLoad : function ()
18823     {
18824         this.el.unmask();
18825     },
18826     
18827
18828     /**
18829      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18830      * @param {HTMLElement} node
18831      * @return {HTMLElement} The template node
18832      */
18833     findItemFromChild : function(node){
18834         var el = this.dataName  ?
18835             this.el.child('.roo-tpl-' + this.dataName,true) :
18836             this.el.dom; 
18837         
18838         if(!node || node.parentNode == el){
18839                     return node;
18840             }
18841             var p = node.parentNode;
18842             while(p && p != el){
18843             if(p.parentNode == el){
18844                 return p;
18845             }
18846             p = p.parentNode;
18847         }
18848             return null;
18849     },
18850
18851     /** @ignore */
18852     onClick : function(e){
18853         var item = this.findItemFromChild(e.getTarget());
18854         if(item){
18855             var index = this.indexOf(item);
18856             if(this.onItemClick(item, index, e) !== false){
18857                 this.fireEvent("click", this, index, item, e);
18858             }
18859         }else{
18860             this.clearSelections();
18861         }
18862     },
18863
18864     /** @ignore */
18865     onContextMenu : function(e){
18866         var item = this.findItemFromChild(e.getTarget());
18867         if(item){
18868             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18869         }
18870     },
18871
18872     /** @ignore */
18873     onDblClick : function(e){
18874         var item = this.findItemFromChild(e.getTarget());
18875         if(item){
18876             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18877         }
18878     },
18879
18880     onItemClick : function(item, index, e)
18881     {
18882         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18883             return false;
18884         }
18885         if (this.toggleSelect) {
18886             var m = this.isSelected(item) ? 'unselect' : 'select';
18887             //Roo.log(m);
18888             var _t = this;
18889             _t[m](item, true, false);
18890             return true;
18891         }
18892         if(this.multiSelect || this.singleSelect){
18893             if(this.multiSelect && e.shiftKey && this.lastSelection){
18894                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18895             }else{
18896                 this.select(item, this.multiSelect && e.ctrlKey);
18897                 this.lastSelection = item;
18898             }
18899             
18900             if(!this.tickable){
18901                 e.preventDefault();
18902             }
18903             
18904         }
18905         return true;
18906     },
18907
18908     /**
18909      * Get the number of selected nodes.
18910      * @return {Number}
18911      */
18912     getSelectionCount : function(){
18913         return this.selections.length;
18914     },
18915
18916     /**
18917      * Get the currently selected nodes.
18918      * @return {Array} An array of HTMLElements
18919      */
18920     getSelectedNodes : function(){
18921         return this.selections;
18922     },
18923
18924     /**
18925      * Get the indexes of the selected nodes.
18926      * @return {Array}
18927      */
18928     getSelectedIndexes : function(){
18929         var indexes = [], s = this.selections;
18930         for(var i = 0, len = s.length; i < len; i++){
18931             indexes.push(s[i].nodeIndex);
18932         }
18933         return indexes;
18934     },
18935
18936     /**
18937      * Clear all selections
18938      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18939      */
18940     clearSelections : function(suppressEvent){
18941         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18942             this.cmp.elements = this.selections;
18943             this.cmp.removeClass(this.selectedClass);
18944             this.selections = [];
18945             if(!suppressEvent){
18946                 this.fireEvent("selectionchange", this, this.selections);
18947             }
18948         }
18949     },
18950
18951     /**
18952      * Returns true if the passed node is selected
18953      * @param {HTMLElement/Number} node The node or node index
18954      * @return {Boolean}
18955      */
18956     isSelected : function(node){
18957         var s = this.selections;
18958         if(s.length < 1){
18959             return false;
18960         }
18961         node = this.getNode(node);
18962         return s.indexOf(node) !== -1;
18963     },
18964
18965     /**
18966      * Selects nodes.
18967      * @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
18968      * @param {Boolean} keepExisting (optional) true to keep existing selections
18969      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18970      */
18971     select : function(nodeInfo, keepExisting, suppressEvent){
18972         if(nodeInfo instanceof Array){
18973             if(!keepExisting){
18974                 this.clearSelections(true);
18975             }
18976             for(var i = 0, len = nodeInfo.length; i < len; i++){
18977                 this.select(nodeInfo[i], true, true);
18978             }
18979             return;
18980         } 
18981         var node = this.getNode(nodeInfo);
18982         if(!node || this.isSelected(node)){
18983             return; // already selected.
18984         }
18985         if(!keepExisting){
18986             this.clearSelections(true);
18987         }
18988         
18989         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18990             Roo.fly(node).addClass(this.selectedClass);
18991             this.selections.push(node);
18992             if(!suppressEvent){
18993                 this.fireEvent("selectionchange", this, this.selections);
18994             }
18995         }
18996         
18997         
18998     },
18999       /**
19000      * Unselects nodes.
19001      * @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
19002      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19003      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19004      */
19005     unselect : function(nodeInfo, keepExisting, suppressEvent)
19006     {
19007         if(nodeInfo instanceof Array){
19008             Roo.each(this.selections, function(s) {
19009                 this.unselect(s, nodeInfo);
19010             }, this);
19011             return;
19012         }
19013         var node = this.getNode(nodeInfo);
19014         if(!node || !this.isSelected(node)){
19015             //Roo.log("not selected");
19016             return; // not selected.
19017         }
19018         // fireevent???
19019         var ns = [];
19020         Roo.each(this.selections, function(s) {
19021             if (s == node ) {
19022                 Roo.fly(node).removeClass(this.selectedClass);
19023
19024                 return;
19025             }
19026             ns.push(s);
19027         },this);
19028         
19029         this.selections= ns;
19030         this.fireEvent("selectionchange", this, this.selections);
19031     },
19032
19033     /**
19034      * Gets a template node.
19035      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19036      * @return {HTMLElement} The node or null if it wasn't found
19037      */
19038     getNode : function(nodeInfo){
19039         if(typeof nodeInfo == "string"){
19040             return document.getElementById(nodeInfo);
19041         }else if(typeof nodeInfo == "number"){
19042             return this.nodes[nodeInfo];
19043         }
19044         return nodeInfo;
19045     },
19046
19047     /**
19048      * Gets a range template nodes.
19049      * @param {Number} startIndex
19050      * @param {Number} endIndex
19051      * @return {Array} An array of nodes
19052      */
19053     getNodes : function(start, end){
19054         var ns = this.nodes;
19055         start = start || 0;
19056         end = typeof end == "undefined" ? ns.length - 1 : end;
19057         var nodes = [];
19058         if(start <= end){
19059             for(var i = start; i <= end; i++){
19060                 nodes.push(ns[i]);
19061             }
19062         } else{
19063             for(var i = start; i >= end; i--){
19064                 nodes.push(ns[i]);
19065             }
19066         }
19067         return nodes;
19068     },
19069
19070     /**
19071      * Finds the index of the passed node
19072      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19073      * @return {Number} The index of the node or -1
19074      */
19075     indexOf : function(node){
19076         node = this.getNode(node);
19077         if(typeof node.nodeIndex == "number"){
19078             return node.nodeIndex;
19079         }
19080         var ns = this.nodes;
19081         for(var i = 0, len = ns.length; i < len; i++){
19082             if(ns[i] == node){
19083                 return i;
19084             }
19085         }
19086         return -1;
19087     }
19088 });
19089 /*
19090  * - LGPL
19091  *
19092  * based on jquery fullcalendar
19093  * 
19094  */
19095
19096 Roo.bootstrap = Roo.bootstrap || {};
19097 /**
19098  * @class Roo.bootstrap.Calendar
19099  * @extends Roo.bootstrap.Component
19100  * Bootstrap Calendar class
19101  * @cfg {Boolean} loadMask (true|false) default false
19102  * @cfg {Object} header generate the user specific header of the calendar, default false
19103
19104  * @constructor
19105  * Create a new Container
19106  * @param {Object} config The config object
19107  */
19108
19109
19110
19111 Roo.bootstrap.Calendar = function(config){
19112     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19113      this.addEvents({
19114         /**
19115              * @event select
19116              * Fires when a date is selected
19117              * @param {DatePicker} this
19118              * @param {Date} date The selected date
19119              */
19120         'select': true,
19121         /**
19122              * @event monthchange
19123              * Fires when the displayed month changes 
19124              * @param {DatePicker} this
19125              * @param {Date} date The selected month
19126              */
19127         'monthchange': true,
19128         /**
19129              * @event evententer
19130              * Fires when mouse over an event
19131              * @param {Calendar} this
19132              * @param {event} Event
19133              */
19134         'evententer': true,
19135         /**
19136              * @event eventleave
19137              * Fires when the mouse leaves an
19138              * @param {Calendar} this
19139              * @param {event}
19140              */
19141         'eventleave': true,
19142         /**
19143              * @event eventclick
19144              * Fires when the mouse click an
19145              * @param {Calendar} this
19146              * @param {event}
19147              */
19148         'eventclick': true
19149         
19150     });
19151
19152 };
19153
19154 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19155     
19156      /**
19157      * @cfg {Number} startDay
19158      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19159      */
19160     startDay : 0,
19161     
19162     loadMask : false,
19163     
19164     header : false,
19165       
19166     getAutoCreate : function(){
19167         
19168         
19169         var fc_button = function(name, corner, style, content ) {
19170             return Roo.apply({},{
19171                 tag : 'span',
19172                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19173                          (corner.length ?
19174                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19175                             ''
19176                         ),
19177                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19178                 unselectable: 'on'
19179             });
19180         };
19181         
19182         var header = {};
19183         
19184         if(!this.header){
19185             header = {
19186                 tag : 'table',
19187                 cls : 'fc-header',
19188                 style : 'width:100%',
19189                 cn : [
19190                     {
19191                         tag: 'tr',
19192                         cn : [
19193                             {
19194                                 tag : 'td',
19195                                 cls : 'fc-header-left',
19196                                 cn : [
19197                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19198                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19199                                     { tag: 'span', cls: 'fc-header-space' },
19200                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19201
19202
19203                                 ]
19204                             },
19205
19206                             {
19207                                 tag : 'td',
19208                                 cls : 'fc-header-center',
19209                                 cn : [
19210                                     {
19211                                         tag: 'span',
19212                                         cls: 'fc-header-title',
19213                                         cn : {
19214                                             tag: 'H2',
19215                                             html : 'month / year'
19216                                         }
19217                                     }
19218
19219                                 ]
19220                             },
19221                             {
19222                                 tag : 'td',
19223                                 cls : 'fc-header-right',
19224                                 cn : [
19225                               /*      fc_button('month', 'left', '', 'month' ),
19226                                     fc_button('week', '', '', 'week' ),
19227                                     fc_button('day', 'right', '', 'day' )
19228                                 */    
19229
19230                                 ]
19231                             }
19232
19233                         ]
19234                     }
19235                 ]
19236             };
19237         }
19238         
19239         header = this.header;
19240         
19241        
19242         var cal_heads = function() {
19243             var ret = [];
19244             // fixme - handle this.
19245             
19246             for (var i =0; i < Date.dayNames.length; i++) {
19247                 var d = Date.dayNames[i];
19248                 ret.push({
19249                     tag: 'th',
19250                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19251                     html : d.substring(0,3)
19252                 });
19253                 
19254             }
19255             ret[0].cls += ' fc-first';
19256             ret[6].cls += ' fc-last';
19257             return ret;
19258         };
19259         var cal_cell = function(n) {
19260             return  {
19261                 tag: 'td',
19262                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19263                 cn : [
19264                     {
19265                         cn : [
19266                             {
19267                                 cls: 'fc-day-number',
19268                                 html: 'D'
19269                             },
19270                             {
19271                                 cls: 'fc-day-content',
19272                              
19273                                 cn : [
19274                                      {
19275                                         style: 'position: relative;' // height: 17px;
19276                                     }
19277                                 ]
19278                             }
19279                             
19280                             
19281                         ]
19282                     }
19283                 ]
19284                 
19285             }
19286         };
19287         var cal_rows = function() {
19288             
19289             var ret = [];
19290             for (var r = 0; r < 6; r++) {
19291                 var row= {
19292                     tag : 'tr',
19293                     cls : 'fc-week',
19294                     cn : []
19295                 };
19296                 
19297                 for (var i =0; i < Date.dayNames.length; i++) {
19298                     var d = Date.dayNames[i];
19299                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19300
19301                 }
19302                 row.cn[0].cls+=' fc-first';
19303                 row.cn[0].cn[0].style = 'min-height:90px';
19304                 row.cn[6].cls+=' fc-last';
19305                 ret.push(row);
19306                 
19307             }
19308             ret[0].cls += ' fc-first';
19309             ret[4].cls += ' fc-prev-last';
19310             ret[5].cls += ' fc-last';
19311             return ret;
19312             
19313         };
19314         
19315         var cal_table = {
19316             tag: 'table',
19317             cls: 'fc-border-separate',
19318             style : 'width:100%',
19319             cellspacing  : 0,
19320             cn : [
19321                 { 
19322                     tag: 'thead',
19323                     cn : [
19324                         { 
19325                             tag: 'tr',
19326                             cls : 'fc-first fc-last',
19327                             cn : cal_heads()
19328                         }
19329                     ]
19330                 },
19331                 { 
19332                     tag: 'tbody',
19333                     cn : cal_rows()
19334                 }
19335                   
19336             ]
19337         };
19338          
19339          var cfg = {
19340             cls : 'fc fc-ltr',
19341             cn : [
19342                 header,
19343                 {
19344                     cls : 'fc-content',
19345                     style : "position: relative;",
19346                     cn : [
19347                         {
19348                             cls : 'fc-view fc-view-month fc-grid',
19349                             style : 'position: relative',
19350                             unselectable : 'on',
19351                             cn : [
19352                                 {
19353                                     cls : 'fc-event-container',
19354                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19355                                 },
19356                                 cal_table
19357                             ]
19358                         }
19359                     ]
19360     
19361                 }
19362            ] 
19363             
19364         };
19365         
19366          
19367         
19368         return cfg;
19369     },
19370     
19371     
19372     initEvents : function()
19373     {
19374         if(!this.store){
19375             throw "can not find store for calendar";
19376         }
19377         
19378         var mark = {
19379             tag: "div",
19380             cls:"x-dlg-mask",
19381             style: "text-align:center",
19382             cn: [
19383                 {
19384                     tag: "div",
19385                     style: "background-color:white;width:50%;margin:250 auto",
19386                     cn: [
19387                         {
19388                             tag: "img",
19389                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19390                         },
19391                         {
19392                             tag: "span",
19393                             html: "Loading"
19394                         }
19395                         
19396                     ]
19397                 }
19398             ]
19399         };
19400         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19401         
19402         var size = this.el.select('.fc-content', true).first().getSize();
19403         this.maskEl.setSize(size.width, size.height);
19404         this.maskEl.enableDisplayMode("block");
19405         if(!this.loadMask){
19406             this.maskEl.hide();
19407         }
19408         
19409         this.store = Roo.factory(this.store, Roo.data);
19410         this.store.on('load', this.onLoad, this);
19411         this.store.on('beforeload', this.onBeforeLoad, this);
19412         
19413         this.resize();
19414         
19415         this.cells = this.el.select('.fc-day',true);
19416         //Roo.log(this.cells);
19417         this.textNodes = this.el.query('.fc-day-number');
19418         this.cells.addClassOnOver('fc-state-hover');
19419         
19420         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19421         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19422         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19423         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19424         
19425         this.on('monthchange', this.onMonthChange, this);
19426         
19427         this.update(new Date().clearTime());
19428     },
19429     
19430     resize : function() {
19431         var sz  = this.el.getSize();
19432         
19433         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19434         this.el.select('.fc-day-content div',true).setHeight(34);
19435     },
19436     
19437     
19438     // private
19439     showPrevMonth : function(e){
19440         this.update(this.activeDate.add("mo", -1));
19441     },
19442     showToday : function(e){
19443         this.update(new Date().clearTime());
19444     },
19445     // private
19446     showNextMonth : function(e){
19447         this.update(this.activeDate.add("mo", 1));
19448     },
19449
19450     // private
19451     showPrevYear : function(){
19452         this.update(this.activeDate.add("y", -1));
19453     },
19454
19455     // private
19456     showNextYear : function(){
19457         this.update(this.activeDate.add("y", 1));
19458     },
19459
19460     
19461    // private
19462     update : function(date)
19463     {
19464         var vd = this.activeDate;
19465         this.activeDate = date;
19466 //        if(vd && this.el){
19467 //            var t = date.getTime();
19468 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19469 //                Roo.log('using add remove');
19470 //                
19471 //                this.fireEvent('monthchange', this, date);
19472 //                
19473 //                this.cells.removeClass("fc-state-highlight");
19474 //                this.cells.each(function(c){
19475 //                   if(c.dateValue == t){
19476 //                       c.addClass("fc-state-highlight");
19477 //                       setTimeout(function(){
19478 //                            try{c.dom.firstChild.focus();}catch(e){}
19479 //                       }, 50);
19480 //                       return false;
19481 //                   }
19482 //                   return true;
19483 //                });
19484 //                return;
19485 //            }
19486 //        }
19487         
19488         var days = date.getDaysInMonth();
19489         
19490         var firstOfMonth = date.getFirstDateOfMonth();
19491         var startingPos = firstOfMonth.getDay()-this.startDay;
19492         
19493         if(startingPos < this.startDay){
19494             startingPos += 7;
19495         }
19496         
19497         var pm = date.add(Date.MONTH, -1);
19498         var prevStart = pm.getDaysInMonth()-startingPos;
19499 //        
19500         this.cells = this.el.select('.fc-day',true);
19501         this.textNodes = this.el.query('.fc-day-number');
19502         this.cells.addClassOnOver('fc-state-hover');
19503         
19504         var cells = this.cells.elements;
19505         var textEls = this.textNodes;
19506         
19507         Roo.each(cells, function(cell){
19508             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19509         });
19510         
19511         days += startingPos;
19512
19513         // convert everything to numbers so it's fast
19514         var day = 86400000;
19515         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19516         //Roo.log(d);
19517         //Roo.log(pm);
19518         //Roo.log(prevStart);
19519         
19520         var today = new Date().clearTime().getTime();
19521         var sel = date.clearTime().getTime();
19522         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19523         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19524         var ddMatch = this.disabledDatesRE;
19525         var ddText = this.disabledDatesText;
19526         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19527         var ddaysText = this.disabledDaysText;
19528         var format = this.format;
19529         
19530         var setCellClass = function(cal, cell){
19531             cell.row = 0;
19532             cell.events = [];
19533             cell.more = [];
19534             //Roo.log('set Cell Class');
19535             cell.title = "";
19536             var t = d.getTime();
19537             
19538             //Roo.log(d);
19539             
19540             cell.dateValue = t;
19541             if(t == today){
19542                 cell.className += " fc-today";
19543                 cell.className += " fc-state-highlight";
19544                 cell.title = cal.todayText;
19545             }
19546             if(t == sel){
19547                 // disable highlight in other month..
19548                 //cell.className += " fc-state-highlight";
19549                 
19550             }
19551             // disabling
19552             if(t < min) {
19553                 cell.className = " fc-state-disabled";
19554                 cell.title = cal.minText;
19555                 return;
19556             }
19557             if(t > max) {
19558                 cell.className = " fc-state-disabled";
19559                 cell.title = cal.maxText;
19560                 return;
19561             }
19562             if(ddays){
19563                 if(ddays.indexOf(d.getDay()) != -1){
19564                     cell.title = ddaysText;
19565                     cell.className = " fc-state-disabled";
19566                 }
19567             }
19568             if(ddMatch && format){
19569                 var fvalue = d.dateFormat(format);
19570                 if(ddMatch.test(fvalue)){
19571                     cell.title = ddText.replace("%0", fvalue);
19572                     cell.className = " fc-state-disabled";
19573                 }
19574             }
19575             
19576             if (!cell.initialClassName) {
19577                 cell.initialClassName = cell.dom.className;
19578             }
19579             
19580             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19581         };
19582
19583         var i = 0;
19584         
19585         for(; i < startingPos; i++) {
19586             textEls[i].innerHTML = (++prevStart);
19587             d.setDate(d.getDate()+1);
19588             
19589             cells[i].className = "fc-past fc-other-month";
19590             setCellClass(this, cells[i]);
19591         }
19592         
19593         var intDay = 0;
19594         
19595         for(; i < days; i++){
19596             intDay = i - startingPos + 1;
19597             textEls[i].innerHTML = (intDay);
19598             d.setDate(d.getDate()+1);
19599             
19600             cells[i].className = ''; // "x-date-active";
19601             setCellClass(this, cells[i]);
19602         }
19603         var extraDays = 0;
19604         
19605         for(; i < 42; i++) {
19606             textEls[i].innerHTML = (++extraDays);
19607             d.setDate(d.getDate()+1);
19608             
19609             cells[i].className = "fc-future fc-other-month";
19610             setCellClass(this, cells[i]);
19611         }
19612         
19613         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19614         
19615         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19616         
19617         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19618         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19619         
19620         if(totalRows != 6){
19621             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19622             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19623         }
19624         
19625         this.fireEvent('monthchange', this, date);
19626         
19627         
19628         /*
19629         if(!this.internalRender){
19630             var main = this.el.dom.firstChild;
19631             var w = main.offsetWidth;
19632             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19633             Roo.fly(main).setWidth(w);
19634             this.internalRender = true;
19635             // opera does not respect the auto grow header center column
19636             // then, after it gets a width opera refuses to recalculate
19637             // without a second pass
19638             if(Roo.isOpera && !this.secondPass){
19639                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19640                 this.secondPass = true;
19641                 this.update.defer(10, this, [date]);
19642             }
19643         }
19644         */
19645         
19646     },
19647     
19648     findCell : function(dt) {
19649         dt = dt.clearTime().getTime();
19650         var ret = false;
19651         this.cells.each(function(c){
19652             //Roo.log("check " +c.dateValue + '?=' + dt);
19653             if(c.dateValue == dt){
19654                 ret = c;
19655                 return false;
19656             }
19657             return true;
19658         });
19659         
19660         return ret;
19661     },
19662     
19663     findCells : function(ev) {
19664         var s = ev.start.clone().clearTime().getTime();
19665        // Roo.log(s);
19666         var e= ev.end.clone().clearTime().getTime();
19667        // Roo.log(e);
19668         var ret = [];
19669         this.cells.each(function(c){
19670              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19671             
19672             if(c.dateValue > e){
19673                 return ;
19674             }
19675             if(c.dateValue < s){
19676                 return ;
19677             }
19678             ret.push(c);
19679         });
19680         
19681         return ret;    
19682     },
19683     
19684 //    findBestRow: function(cells)
19685 //    {
19686 //        var ret = 0;
19687 //        
19688 //        for (var i =0 ; i < cells.length;i++) {
19689 //            ret  = Math.max(cells[i].rows || 0,ret);
19690 //        }
19691 //        return ret;
19692 //        
19693 //    },
19694     
19695     
19696     addItem : function(ev)
19697     {
19698         // look for vertical location slot in
19699         var cells = this.findCells(ev);
19700         
19701 //        ev.row = this.findBestRow(cells);
19702         
19703         // work out the location.
19704         
19705         var crow = false;
19706         var rows = [];
19707         for(var i =0; i < cells.length; i++) {
19708             
19709             cells[i].row = cells[0].row;
19710             
19711             if(i == 0){
19712                 cells[i].row = cells[i].row + 1;
19713             }
19714             
19715             if (!crow) {
19716                 crow = {
19717                     start : cells[i],
19718                     end :  cells[i]
19719                 };
19720                 continue;
19721             }
19722             if (crow.start.getY() == cells[i].getY()) {
19723                 // on same row.
19724                 crow.end = cells[i];
19725                 continue;
19726             }
19727             // different row.
19728             rows.push(crow);
19729             crow = {
19730                 start: cells[i],
19731                 end : cells[i]
19732             };
19733             
19734         }
19735         
19736         rows.push(crow);
19737         ev.els = [];
19738         ev.rows = rows;
19739         ev.cells = cells;
19740         
19741         cells[0].events.push(ev);
19742         
19743         this.calevents.push(ev);
19744     },
19745     
19746     clearEvents: function() {
19747         
19748         if(!this.calevents){
19749             return;
19750         }
19751         
19752         Roo.each(this.cells.elements, function(c){
19753             c.row = 0;
19754             c.events = [];
19755             c.more = [];
19756         });
19757         
19758         Roo.each(this.calevents, function(e) {
19759             Roo.each(e.els, function(el) {
19760                 el.un('mouseenter' ,this.onEventEnter, this);
19761                 el.un('mouseleave' ,this.onEventLeave, this);
19762                 el.remove();
19763             },this);
19764         },this);
19765         
19766         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19767             e.remove();
19768         });
19769         
19770     },
19771     
19772     renderEvents: function()
19773     {   
19774         var _this = this;
19775         
19776         this.cells.each(function(c) {
19777             
19778             if(c.row < 5){
19779                 return;
19780             }
19781             
19782             var ev = c.events;
19783             
19784             var r = 4;
19785             if(c.row != c.events.length){
19786                 r = 4 - (4 - (c.row - c.events.length));
19787             }
19788             
19789             c.events = ev.slice(0, r);
19790             c.more = ev.slice(r);
19791             
19792             if(c.more.length && c.more.length == 1){
19793                 c.events.push(c.more.pop());
19794             }
19795             
19796             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19797             
19798         });
19799             
19800         this.cells.each(function(c) {
19801             
19802             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19803             
19804             
19805             for (var e = 0; e < c.events.length; e++){
19806                 var ev = c.events[e];
19807                 var rows = ev.rows;
19808                 
19809                 for(var i = 0; i < rows.length; i++) {
19810                 
19811                     // how many rows should it span..
19812
19813                     var  cfg = {
19814                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19815                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19816
19817                         unselectable : "on",
19818                         cn : [
19819                             {
19820                                 cls: 'fc-event-inner',
19821                                 cn : [
19822     //                                {
19823     //                                  tag:'span',
19824     //                                  cls: 'fc-event-time',
19825     //                                  html : cells.length > 1 ? '' : ev.time
19826     //                                },
19827                                     {
19828                                       tag:'span',
19829                                       cls: 'fc-event-title',
19830                                       html : String.format('{0}', ev.title)
19831                                     }
19832
19833
19834                                 ]
19835                             },
19836                             {
19837                                 cls: 'ui-resizable-handle ui-resizable-e',
19838                                 html : '&nbsp;&nbsp;&nbsp'
19839                             }
19840
19841                         ]
19842                     };
19843
19844                     if (i == 0) {
19845                         cfg.cls += ' fc-event-start';
19846                     }
19847                     if ((i+1) == rows.length) {
19848                         cfg.cls += ' fc-event-end';
19849                     }
19850
19851                     var ctr = _this.el.select('.fc-event-container',true).first();
19852                     var cg = ctr.createChild(cfg);
19853
19854                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19855                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19856
19857                     var r = (c.more.length) ? 1 : 0;
19858                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19859                     cg.setWidth(ebox.right - sbox.x -2);
19860
19861                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19862                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19863                     cg.on('click', _this.onEventClick, _this, ev);
19864
19865                     ev.els.push(cg);
19866                     
19867                 }
19868                 
19869             }
19870             
19871             
19872             if(c.more.length){
19873                 var  cfg = {
19874                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19875                     style : 'position: absolute',
19876                     unselectable : "on",
19877                     cn : [
19878                         {
19879                             cls: 'fc-event-inner',
19880                             cn : [
19881                                 {
19882                                   tag:'span',
19883                                   cls: 'fc-event-title',
19884                                   html : 'More'
19885                                 }
19886
19887
19888                             ]
19889                         },
19890                         {
19891                             cls: 'ui-resizable-handle ui-resizable-e',
19892                             html : '&nbsp;&nbsp;&nbsp'
19893                         }
19894
19895                     ]
19896                 };
19897
19898                 var ctr = _this.el.select('.fc-event-container',true).first();
19899                 var cg = ctr.createChild(cfg);
19900
19901                 var sbox = c.select('.fc-day-content',true).first().getBox();
19902                 var ebox = c.select('.fc-day-content',true).first().getBox();
19903                 //Roo.log(cg);
19904                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19905                 cg.setWidth(ebox.right - sbox.x -2);
19906
19907                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19908                 
19909             }
19910             
19911         });
19912         
19913         
19914         
19915     },
19916     
19917     onEventEnter: function (e, el,event,d) {
19918         this.fireEvent('evententer', this, el, event);
19919     },
19920     
19921     onEventLeave: function (e, el,event,d) {
19922         this.fireEvent('eventleave', this, el, event);
19923     },
19924     
19925     onEventClick: function (e, el,event,d) {
19926         this.fireEvent('eventclick', this, el, event);
19927     },
19928     
19929     onMonthChange: function () {
19930         this.store.load();
19931     },
19932     
19933     onMoreEventClick: function(e, el, more)
19934     {
19935         var _this = this;
19936         
19937         this.calpopover.placement = 'right';
19938         this.calpopover.setTitle('More');
19939         
19940         this.calpopover.setContent('');
19941         
19942         var ctr = this.calpopover.el.select('.popover-content', true).first();
19943         
19944         Roo.each(more, function(m){
19945             var cfg = {
19946                 cls : 'fc-event-hori fc-event-draggable',
19947                 html : m.title
19948             };
19949             var cg = ctr.createChild(cfg);
19950             
19951             cg.on('click', _this.onEventClick, _this, m);
19952         });
19953         
19954         this.calpopover.show(el);
19955         
19956         
19957     },
19958     
19959     onLoad: function () 
19960     {   
19961         this.calevents = [];
19962         var cal = this;
19963         
19964         if(this.store.getCount() > 0){
19965             this.store.data.each(function(d){
19966                cal.addItem({
19967                     id : d.data.id,
19968                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19969                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19970                     time : d.data.start_time,
19971                     title : d.data.title,
19972                     description : d.data.description,
19973                     venue : d.data.venue
19974                 });
19975             });
19976         }
19977         
19978         this.renderEvents();
19979         
19980         if(this.calevents.length && this.loadMask){
19981             this.maskEl.hide();
19982         }
19983     },
19984     
19985     onBeforeLoad: function()
19986     {
19987         this.clearEvents();
19988         if(this.loadMask){
19989             this.maskEl.show();
19990         }
19991     }
19992 });
19993
19994  
19995  /*
19996  * - LGPL
19997  *
19998  * element
19999  * 
20000  */
20001
20002 /**
20003  * @class Roo.bootstrap.Popover
20004  * @extends Roo.bootstrap.Component
20005  * Bootstrap Popover class
20006  * @cfg {String} html contents of the popover   (or false to use children..)
20007  * @cfg {String} title of popover (or false to hide)
20008  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20009  * @cfg {String} trigger click || hover (or false to trigger manually)
20010  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20011  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20012  *      - if false and it has a 'parent' then it will be automatically added to that element
20013  *      - if string - Roo.get  will be called 
20014  * @cfg {Number} delay - delay before showing
20015  
20016  * @constructor
20017  * Create a new Popover
20018  * @param {Object} config The config object
20019  */
20020
20021 Roo.bootstrap.Popover = function(config){
20022     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20023     
20024     this.addEvents({
20025         // raw events
20026          /**
20027          * @event show
20028          * After the popover show
20029          * 
20030          * @param {Roo.bootstrap.Popover} this
20031          */
20032         "show" : true,
20033         /**
20034          * @event hide
20035          * After the popover hide
20036          * 
20037          * @param {Roo.bootstrap.Popover} this
20038          */
20039         "hide" : true
20040     });
20041 };
20042
20043 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
20044     
20045     title: false,
20046     html: false,
20047     
20048     placement : 'right',
20049     trigger : 'hover', // hover
20050     modal : false,
20051     delay : 0,
20052     
20053     over: false,
20054     
20055     can_build_overlaid : false,
20056     
20057     maskEl : false, // the mask element
20058     headerEl : false,
20059     contentEl : false,
20060     alignEl : false, // when show is called with an element - this get's stored.
20061     
20062     getChildContainer : function()
20063     {
20064         return this.contentEl;
20065         
20066     },
20067     getPopoverHeader : function()
20068     {
20069         this.title = true; // flag not to hide it..
20070         this.headerEl.addClass('p-0');
20071         return this.headerEl
20072     },
20073     
20074     
20075     getAutoCreate : function(){
20076          
20077         var cfg = {
20078            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20079            style: 'display:block',
20080            cn : [
20081                 {
20082                     cls : 'arrow'
20083                 },
20084                 {
20085                     cls : 'popover-inner ',
20086                     cn : [
20087                         {
20088                             tag: 'h3',
20089                             cls: 'popover-title popover-header',
20090                             html : this.title === false ? '' : this.title
20091                         },
20092                         {
20093                             cls : 'popover-content popover-body '  + (this.cls || ''),
20094                             html : this.html || ''
20095                         }
20096                     ]
20097                     
20098                 }
20099            ]
20100         };
20101         
20102         return cfg;
20103     },
20104     /**
20105      * @param {string} the title
20106      */
20107     setTitle: function(str)
20108     {
20109         this.title = str;
20110         if (this.el) {
20111             this.headerEl.dom.innerHTML = str;
20112         }
20113         
20114     },
20115     /**
20116      * @param {string} the body content
20117      */
20118     setContent: function(str)
20119     {
20120         this.html = str;
20121         if (this.contentEl) {
20122             this.contentEl.dom.innerHTML = str;
20123         }
20124         
20125     },
20126     // as it get's added to the bottom of the page.
20127     onRender : function(ct, position)
20128     {
20129         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20130         
20131         
20132         
20133         if(!this.el){
20134             var cfg = Roo.apply({},  this.getAutoCreate());
20135             cfg.id = Roo.id();
20136             
20137             if (this.cls) {
20138                 cfg.cls += ' ' + this.cls;
20139             }
20140             if (this.style) {
20141                 cfg.style = this.style;
20142             }
20143             //Roo.log("adding to ");
20144             this.el = Roo.get(document.body).createChild(cfg, position);
20145 //            Roo.log(this.el);
20146         }
20147         
20148         this.contentEl = this.el.select('.popover-content',true).first();
20149         this.headerEl =  this.el.select('.popover-title',true).first();
20150         
20151         var nitems = [];
20152         if(typeof(this.items) != 'undefined'){
20153             var items = this.items;
20154             delete this.items;
20155
20156             for(var i =0;i < items.length;i++) {
20157                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20158             }
20159         }
20160
20161         this.items = nitems;
20162         
20163         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20164         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20165         
20166         
20167         
20168         this.initEvents();
20169     },
20170     
20171     resizeMask : function()
20172     {
20173         this.maskEl.setSize(
20174             Roo.lib.Dom.getViewWidth(true),
20175             Roo.lib.Dom.getViewHeight(true)
20176         );
20177     },
20178     
20179     initEvents : function()
20180     {
20181         
20182         if (!this.modal) { 
20183             Roo.bootstrap.Popover.register(this);
20184         }
20185          
20186         this.arrowEl = this.el.select('.arrow',true).first();
20187         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20188         this.el.enableDisplayMode('block');
20189         this.el.hide();
20190  
20191         
20192         if (this.over === false && !this.parent()) {
20193             return; 
20194         }
20195         if (this.triggers === false) {
20196             return;
20197         }
20198          
20199         // support parent
20200         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20201         var triggers = this.trigger ? this.trigger.split(' ') : [];
20202         Roo.each(triggers, function(trigger) {
20203         
20204             if (trigger == 'click') {
20205                 on_el.on('click', this.toggle, this);
20206             } else if (trigger != 'manual') {
20207                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20208                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20209       
20210                 on_el.on(eventIn  ,this.enter, this);
20211                 on_el.on(eventOut, this.leave, this);
20212             }
20213         }, this);
20214     },
20215     
20216     
20217     // private
20218     timeout : null,
20219     hoverState : null,
20220     
20221     toggle : function () {
20222         this.hoverState == 'in' ? this.leave() : this.enter();
20223     },
20224     
20225     enter : function () {
20226         
20227         clearTimeout(this.timeout);
20228     
20229         this.hoverState = 'in';
20230     
20231         if (!this.delay || !this.delay.show) {
20232             this.show();
20233             return;
20234         }
20235         var _t = this;
20236         this.timeout = setTimeout(function () {
20237             if (_t.hoverState == 'in') {
20238                 _t.show();
20239             }
20240         }, this.delay.show)
20241     },
20242     
20243     leave : function() {
20244         clearTimeout(this.timeout);
20245     
20246         this.hoverState = 'out';
20247     
20248         if (!this.delay || !this.delay.hide) {
20249             this.hide();
20250             return;
20251         }
20252         var _t = this;
20253         this.timeout = setTimeout(function () {
20254             if (_t.hoverState == 'out') {
20255                 _t.hide();
20256             }
20257         }, this.delay.hide)
20258     },
20259     /**
20260      * Show the popover
20261      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20262      * @param {string} (left|right|top|bottom) position
20263      */
20264     show : function (on_el, placement)
20265     {
20266         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20267         on_el = on_el || false; // default to false
20268          
20269         if (!on_el) {
20270             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20271                 on_el = this.parent().el;
20272             } else if (this.over) {
20273                 on_el = Roo.get(this.over);
20274             }
20275             
20276         }
20277         
20278         this.alignEl = Roo.get( on_el );
20279
20280         if (!this.el) {
20281             this.render(document.body);
20282         }
20283         
20284         
20285          
20286         
20287         if (this.title === false) {
20288             this.headerEl.hide();
20289         }
20290         
20291        
20292         this.el.show();
20293         this.el.dom.style.display = 'block';
20294          
20295  
20296         if (this.alignEl) {
20297             this.updatePosition(this.placement, true);
20298              
20299         } else {
20300             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20301             var es = this.el.getSize();
20302             var x = Roo.lib.Dom.getViewWidth()/2;
20303             var y = Roo.lib.Dom.getViewHeight()/2;
20304             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20305             
20306         }
20307
20308         
20309         //var arrow = this.el.select('.arrow',true).first();
20310         //arrow.set(align[2], 
20311         
20312         this.el.addClass('in');
20313         
20314          
20315         
20316         this.hoverState = 'in';
20317         
20318         if (this.modal) {
20319             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20320             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20321             this.maskEl.dom.style.display = 'block';
20322             this.maskEl.addClass('show');
20323         }
20324         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20325  
20326         this.fireEvent('show', this);
20327         
20328     },
20329     /**
20330      * fire this manually after loading a grid in the table for example
20331      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20332      * @param {Boolean} try and move it if we cant get right position.
20333      */
20334     updatePosition : function(placement, try_move)
20335     {
20336         // allow for calling with no parameters
20337         placement = placement   ? placement :  this.placement;
20338         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20339         
20340         this.el.removeClass([
20341             'fade','top','bottom', 'left', 'right','in',
20342             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20343         ]);
20344         this.el.addClass(placement + ' bs-popover-' + placement);
20345         
20346         if (!this.alignEl ) {
20347             return false;
20348         }
20349         
20350         switch (placement) {
20351             case 'right':
20352                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20353                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20354                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20355                     //normal display... or moved up/down.
20356                     this.el.setXY(offset);
20357                     var xy = this.alignEl.getAnchorXY('tr', false);
20358                     xy[0]+=2;xy[1]+=5;
20359                     this.arrowEl.setXY(xy);
20360                     return true;
20361                 }
20362                 // continue through...
20363                 return this.updatePosition('left', false);
20364                 
20365             
20366             case 'left':
20367                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20368                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20369                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20370                     //normal display... or moved up/down.
20371                     this.el.setXY(offset);
20372                     var xy = this.alignEl.getAnchorXY('tl', false);
20373                     xy[0]-=10;xy[1]+=5; // << fix me
20374                     this.arrowEl.setXY(xy);
20375                     return true;
20376                 }
20377                 // call self...
20378                 return this.updatePosition('right', false);
20379             
20380             case 'top':
20381                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20382                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20383                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20384                     //normal display... or moved up/down.
20385                     this.el.setXY(offset);
20386                     var xy = this.alignEl.getAnchorXY('t', false);
20387                     xy[1]-=10; // << fix me
20388                     this.arrowEl.setXY(xy);
20389                     return true;
20390                 }
20391                 // fall through
20392                return this.updatePosition('bottom', false);
20393             
20394             case 'bottom':
20395                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20396                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20397                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20398                     //normal display... or moved up/down.
20399                     this.el.setXY(offset);
20400                     var xy = this.alignEl.getAnchorXY('b', false);
20401                      xy[1]+=2; // << fix me
20402                     this.arrowEl.setXY(xy);
20403                     return true;
20404                 }
20405                 // fall through
20406                 return this.updatePosition('top', false);
20407                 
20408             
20409         }
20410         
20411         
20412         return false;
20413     },
20414     
20415     hide : function()
20416     {
20417         this.el.setXY([0,0]);
20418         this.el.removeClass('in');
20419         this.el.hide();
20420         this.hoverState = null;
20421         this.maskEl.hide(); // always..
20422         this.fireEvent('hide', this);
20423     }
20424     
20425 });
20426
20427
20428 Roo.apply(Roo.bootstrap.Popover, {
20429
20430     alignment : {
20431         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20432         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20433         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20434         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20435     },
20436     
20437     zIndex : 20001,
20438
20439     clickHander : false,
20440     
20441     
20442
20443     onMouseDown : function(e)
20444     {
20445         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
20446             /// what is nothing is showing..
20447             this.hideAll();
20448         }
20449          
20450     },
20451     
20452     
20453     popups : [],
20454     
20455     register : function(popup)
20456     {
20457         if (!Roo.bootstrap.Popover.clickHandler) {
20458             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20459         }
20460         // hide other popups.
20461         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
20462         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
20463         this.hideAll(); //<< why?
20464         //this.popups.push(popup);
20465     },
20466     hideAll : function()
20467     {
20468         this.popups.forEach(function(p) {
20469             p.hide();
20470         });
20471     },
20472     onShow : function() {
20473         Roo.bootstrap.Popover.popups.push(this);
20474     },
20475     onHide : function() {
20476         Roo.bootstrap.Popover.popups.remove(this);
20477     } 
20478
20479 });/*
20480  * - LGPL
20481  *
20482  * Card header - holder for the card header elements.
20483  * 
20484  */
20485
20486 /**
20487  * @class Roo.bootstrap.PopoverNav
20488  * @extends Roo.bootstrap.NavGroup
20489  * Bootstrap Popover header navigation class
20490  * @constructor
20491  * Create a new Popover Header Navigation 
20492  * @param {Object} config The config object
20493  */
20494
20495 Roo.bootstrap.PopoverNav = function(config){
20496     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20497 };
20498
20499 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20500     
20501     
20502     container_method : 'getPopoverHeader' 
20503     
20504      
20505     
20506     
20507    
20508 });
20509
20510  
20511
20512  /*
20513  * - LGPL
20514  *
20515  * Progress
20516  * 
20517  */
20518
20519 /**
20520  * @class Roo.bootstrap.Progress
20521  * @extends Roo.bootstrap.Component
20522  * Bootstrap Progress class
20523  * @cfg {Boolean} striped striped of the progress bar
20524  * @cfg {Boolean} active animated of the progress bar
20525  * 
20526  * 
20527  * @constructor
20528  * Create a new Progress
20529  * @param {Object} config The config object
20530  */
20531
20532 Roo.bootstrap.Progress = function(config){
20533     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20534 };
20535
20536 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20537     
20538     striped : false,
20539     active: false,
20540     
20541     getAutoCreate : function(){
20542         var cfg = {
20543             tag: 'div',
20544             cls: 'progress'
20545         };
20546         
20547         
20548         if(this.striped){
20549             cfg.cls += ' progress-striped';
20550         }
20551       
20552         if(this.active){
20553             cfg.cls += ' active';
20554         }
20555         
20556         
20557         return cfg;
20558     }
20559    
20560 });
20561
20562  
20563
20564  /*
20565  * - LGPL
20566  *
20567  * ProgressBar
20568  * 
20569  */
20570
20571 /**
20572  * @class Roo.bootstrap.ProgressBar
20573  * @extends Roo.bootstrap.Component
20574  * Bootstrap ProgressBar class
20575  * @cfg {Number} aria_valuenow aria-value now
20576  * @cfg {Number} aria_valuemin aria-value min
20577  * @cfg {Number} aria_valuemax aria-value max
20578  * @cfg {String} label label for the progress bar
20579  * @cfg {String} panel (success | info | warning | danger )
20580  * @cfg {String} role role of the progress bar
20581  * @cfg {String} sr_only text
20582  * 
20583  * 
20584  * @constructor
20585  * Create a new ProgressBar
20586  * @param {Object} config The config object
20587  */
20588
20589 Roo.bootstrap.ProgressBar = function(config){
20590     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20591 };
20592
20593 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20594     
20595     aria_valuenow : 0,
20596     aria_valuemin : 0,
20597     aria_valuemax : 100,
20598     label : false,
20599     panel : false,
20600     role : false,
20601     sr_only: false,
20602     
20603     getAutoCreate : function()
20604     {
20605         
20606         var cfg = {
20607             tag: 'div',
20608             cls: 'progress-bar',
20609             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20610         };
20611         
20612         if(this.sr_only){
20613             cfg.cn = {
20614                 tag: 'span',
20615                 cls: 'sr-only',
20616                 html: this.sr_only
20617             }
20618         }
20619         
20620         if(this.role){
20621             cfg.role = this.role;
20622         }
20623         
20624         if(this.aria_valuenow){
20625             cfg['aria-valuenow'] = this.aria_valuenow;
20626         }
20627         
20628         if(this.aria_valuemin){
20629             cfg['aria-valuemin'] = this.aria_valuemin;
20630         }
20631         
20632         if(this.aria_valuemax){
20633             cfg['aria-valuemax'] = this.aria_valuemax;
20634         }
20635         
20636         if(this.label && !this.sr_only){
20637             cfg.html = this.label;
20638         }
20639         
20640         if(this.panel){
20641             cfg.cls += ' progress-bar-' + this.panel;
20642         }
20643         
20644         return cfg;
20645     },
20646     
20647     update : function(aria_valuenow)
20648     {
20649         this.aria_valuenow = aria_valuenow;
20650         
20651         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20652     }
20653    
20654 });
20655
20656  
20657
20658  /*
20659  * - LGPL
20660  *
20661  * column
20662  * 
20663  */
20664
20665 /**
20666  * @class Roo.bootstrap.TabGroup
20667  * @extends Roo.bootstrap.Column
20668  * Bootstrap Column class
20669  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20670  * @cfg {Boolean} carousel true to make the group behave like a carousel
20671  * @cfg {Boolean} bullets show bullets for the panels
20672  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20673  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20674  * @cfg {Boolean} showarrow (true|false) show arrow default true
20675  * 
20676  * @constructor
20677  * Create a new TabGroup
20678  * @param {Object} config The config object
20679  */
20680
20681 Roo.bootstrap.TabGroup = function(config){
20682     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20683     if (!this.navId) {
20684         this.navId = Roo.id();
20685     }
20686     this.tabs = [];
20687     Roo.bootstrap.TabGroup.register(this);
20688     
20689 };
20690
20691 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20692     
20693     carousel : false,
20694     transition : false,
20695     bullets : 0,
20696     timer : 0,
20697     autoslide : false,
20698     slideFn : false,
20699     slideOnTouch : false,
20700     showarrow : true,
20701     
20702     getAutoCreate : function()
20703     {
20704         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20705         
20706         cfg.cls += ' tab-content';
20707         
20708         if (this.carousel) {
20709             cfg.cls += ' carousel slide';
20710             
20711             cfg.cn = [{
20712                cls : 'carousel-inner',
20713                cn : []
20714             }];
20715         
20716             if(this.bullets  && !Roo.isTouch){
20717                 
20718                 var bullets = {
20719                     cls : 'carousel-bullets',
20720                     cn : []
20721                 };
20722                
20723                 if(this.bullets_cls){
20724                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20725                 }
20726                 
20727                 bullets.cn.push({
20728                     cls : 'clear'
20729                 });
20730                 
20731                 cfg.cn[0].cn.push(bullets);
20732             }
20733             
20734             if(this.showarrow){
20735                 cfg.cn[0].cn.push({
20736                     tag : 'div',
20737                     class : 'carousel-arrow',
20738                     cn : [
20739                         {
20740                             tag : 'div',
20741                             class : 'carousel-prev',
20742                             cn : [
20743                                 {
20744                                     tag : 'i',
20745                                     class : 'fa fa-chevron-left'
20746                                 }
20747                             ]
20748                         },
20749                         {
20750                             tag : 'div',
20751                             class : 'carousel-next',
20752                             cn : [
20753                                 {
20754                                     tag : 'i',
20755                                     class : 'fa fa-chevron-right'
20756                                 }
20757                             ]
20758                         }
20759                     ]
20760                 });
20761             }
20762             
20763         }
20764         
20765         return cfg;
20766     },
20767     
20768     initEvents:  function()
20769     {
20770 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20771 //            this.el.on("touchstart", this.onTouchStart, this);
20772 //        }
20773         
20774         if(this.autoslide){
20775             var _this = this;
20776             
20777             this.slideFn = window.setInterval(function() {
20778                 _this.showPanelNext();
20779             }, this.timer);
20780         }
20781         
20782         if(this.showarrow){
20783             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20784             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20785         }
20786         
20787         
20788     },
20789     
20790 //    onTouchStart : function(e, el, o)
20791 //    {
20792 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20793 //            return;
20794 //        }
20795 //        
20796 //        this.showPanelNext();
20797 //    },
20798     
20799     
20800     getChildContainer : function()
20801     {
20802         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20803     },
20804     
20805     /**
20806     * register a Navigation item
20807     * @param {Roo.bootstrap.NavItem} the navitem to add
20808     */
20809     register : function(item)
20810     {
20811         this.tabs.push( item);
20812         item.navId = this.navId; // not really needed..
20813         this.addBullet();
20814     
20815     },
20816     
20817     getActivePanel : function()
20818     {
20819         var r = false;
20820         Roo.each(this.tabs, function(t) {
20821             if (t.active) {
20822                 r = t;
20823                 return false;
20824             }
20825             return null;
20826         });
20827         return r;
20828         
20829     },
20830     getPanelByName : function(n)
20831     {
20832         var r = false;
20833         Roo.each(this.tabs, function(t) {
20834             if (t.tabId == n) {
20835                 r = t;
20836                 return false;
20837             }
20838             return null;
20839         });
20840         return r;
20841     },
20842     indexOfPanel : function(p)
20843     {
20844         var r = false;
20845         Roo.each(this.tabs, function(t,i) {
20846             if (t.tabId == p.tabId) {
20847                 r = i;
20848                 return false;
20849             }
20850             return null;
20851         });
20852         return r;
20853     },
20854     /**
20855      * show a specific panel
20856      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20857      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20858      */
20859     showPanel : function (pan)
20860     {
20861         if(this.transition || typeof(pan) == 'undefined'){
20862             Roo.log("waiting for the transitionend");
20863             return false;
20864         }
20865         
20866         if (typeof(pan) == 'number') {
20867             pan = this.tabs[pan];
20868         }
20869         
20870         if (typeof(pan) == 'string') {
20871             pan = this.getPanelByName(pan);
20872         }
20873         
20874         var cur = this.getActivePanel();
20875         
20876         if(!pan || !cur){
20877             Roo.log('pan or acitve pan is undefined');
20878             return false;
20879         }
20880         
20881         if (pan.tabId == this.getActivePanel().tabId) {
20882             return true;
20883         }
20884         
20885         if (false === cur.fireEvent('beforedeactivate')) {
20886             return false;
20887         }
20888         
20889         if(this.bullets > 0 && !Roo.isTouch){
20890             this.setActiveBullet(this.indexOfPanel(pan));
20891         }
20892         
20893         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20894             
20895             //class="carousel-item carousel-item-next carousel-item-left"
20896             
20897             this.transition = true;
20898             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20899             var lr = dir == 'next' ? 'left' : 'right';
20900             pan.el.addClass(dir); // or prev
20901             pan.el.addClass('carousel-item-' + dir); // or prev
20902             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20903             cur.el.addClass(lr); // or right
20904             pan.el.addClass(lr);
20905             cur.el.addClass('carousel-item-' +lr); // or right
20906             pan.el.addClass('carousel-item-' +lr);
20907             
20908             
20909             var _this = this;
20910             cur.el.on('transitionend', function() {
20911                 Roo.log("trans end?");
20912                 
20913                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20914                 pan.setActive(true);
20915                 
20916                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20917                 cur.setActive(false);
20918                 
20919                 _this.transition = false;
20920                 
20921             }, this, { single:  true } );
20922             
20923             return true;
20924         }
20925         
20926         cur.setActive(false);
20927         pan.setActive(true);
20928         
20929         return true;
20930         
20931     },
20932     showPanelNext : function()
20933     {
20934         var i = this.indexOfPanel(this.getActivePanel());
20935         
20936         if (i >= this.tabs.length - 1 && !this.autoslide) {
20937             return;
20938         }
20939         
20940         if (i >= this.tabs.length - 1 && this.autoslide) {
20941             i = -1;
20942         }
20943         
20944         this.showPanel(this.tabs[i+1]);
20945     },
20946     
20947     showPanelPrev : function()
20948     {
20949         var i = this.indexOfPanel(this.getActivePanel());
20950         
20951         if (i  < 1 && !this.autoslide) {
20952             return;
20953         }
20954         
20955         if (i < 1 && this.autoslide) {
20956             i = this.tabs.length;
20957         }
20958         
20959         this.showPanel(this.tabs[i-1]);
20960     },
20961     
20962     
20963     addBullet: function()
20964     {
20965         if(!this.bullets || Roo.isTouch){
20966             return;
20967         }
20968         var ctr = this.el.select('.carousel-bullets',true).first();
20969         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20970         var bullet = ctr.createChild({
20971             cls : 'bullet bullet-' + i
20972         },ctr.dom.lastChild);
20973         
20974         
20975         var _this = this;
20976         
20977         bullet.on('click', (function(e, el, o, ii, t){
20978
20979             e.preventDefault();
20980
20981             this.showPanel(ii);
20982
20983             if(this.autoslide && this.slideFn){
20984                 clearInterval(this.slideFn);
20985                 this.slideFn = window.setInterval(function() {
20986                     _this.showPanelNext();
20987                 }, this.timer);
20988             }
20989
20990         }).createDelegate(this, [i, bullet], true));
20991                 
20992         
20993     },
20994      
20995     setActiveBullet : function(i)
20996     {
20997         if(Roo.isTouch){
20998             return;
20999         }
21000         
21001         Roo.each(this.el.select('.bullet', true).elements, function(el){
21002             el.removeClass('selected');
21003         });
21004
21005         var bullet = this.el.select('.bullet-' + i, true).first();
21006         
21007         if(!bullet){
21008             return;
21009         }
21010         
21011         bullet.addClass('selected');
21012     }
21013     
21014     
21015   
21016 });
21017
21018  
21019
21020  
21021  
21022 Roo.apply(Roo.bootstrap.TabGroup, {
21023     
21024     groups: {},
21025      /**
21026     * register a Navigation Group
21027     * @param {Roo.bootstrap.NavGroup} the navgroup to add
21028     */
21029     register : function(navgrp)
21030     {
21031         this.groups[navgrp.navId] = navgrp;
21032         
21033     },
21034     /**
21035     * fetch a Navigation Group based on the navigation ID
21036     * if one does not exist , it will get created.
21037     * @param {string} the navgroup to add
21038     * @returns {Roo.bootstrap.NavGroup} the navgroup 
21039     */
21040     get: function(navId) {
21041         if (typeof(this.groups[navId]) == 'undefined') {
21042             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21043         }
21044         return this.groups[navId] ;
21045     }
21046     
21047     
21048     
21049 });
21050
21051  /*
21052  * - LGPL
21053  *
21054  * TabPanel
21055  * 
21056  */
21057
21058 /**
21059  * @class Roo.bootstrap.TabPanel
21060  * @extends Roo.bootstrap.Component
21061  * Bootstrap TabPanel class
21062  * @cfg {Boolean} active panel active
21063  * @cfg {String} html panel content
21064  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21065  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21066  * @cfg {String} href click to link..
21067  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21068  * 
21069  * 
21070  * @constructor
21071  * Create a new TabPanel
21072  * @param {Object} config The config object
21073  */
21074
21075 Roo.bootstrap.TabPanel = function(config){
21076     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21077     this.addEvents({
21078         /**
21079              * @event changed
21080              * Fires when the active status changes
21081              * @param {Roo.bootstrap.TabPanel} this
21082              * @param {Boolean} state the new state
21083             
21084          */
21085         'changed': true,
21086         /**
21087              * @event beforedeactivate
21088              * Fires before a tab is de-activated - can be used to do validation on a form.
21089              * @param {Roo.bootstrap.TabPanel} this
21090              * @return {Boolean} false if there is an error
21091             
21092          */
21093         'beforedeactivate': true
21094      });
21095     
21096     this.tabId = this.tabId || Roo.id();
21097   
21098 };
21099
21100 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21101     
21102     active: false,
21103     html: false,
21104     tabId: false,
21105     navId : false,
21106     href : '',
21107     touchSlide : false,
21108     getAutoCreate : function(){
21109         
21110         
21111         var cfg = {
21112             tag: 'div',
21113             // item is needed for carousel - not sure if it has any effect otherwise
21114             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21115             html: this.html || ''
21116         };
21117         
21118         if(this.active){
21119             cfg.cls += ' active';
21120         }
21121         
21122         if(this.tabId){
21123             cfg.tabId = this.tabId;
21124         }
21125         
21126         
21127         
21128         return cfg;
21129     },
21130     
21131     initEvents:  function()
21132     {
21133         var p = this.parent();
21134         
21135         this.navId = this.navId || p.navId;
21136         
21137         if (typeof(this.navId) != 'undefined') {
21138             // not really needed.. but just in case.. parent should be a NavGroup.
21139             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21140             
21141             tg.register(this);
21142             
21143             var i = tg.tabs.length - 1;
21144             
21145             if(this.active && tg.bullets > 0 && i < tg.bullets){
21146                 tg.setActiveBullet(i);
21147             }
21148         }
21149         
21150         this.el.on('click', this.onClick, this);
21151         
21152         if(Roo.isTouch && this.touchSlide){
21153             this.el.on("touchstart", this.onTouchStart, this);
21154             this.el.on("touchmove", this.onTouchMove, this);
21155             this.el.on("touchend", this.onTouchEnd, this);
21156         }
21157         
21158     },
21159     
21160     onRender : function(ct, position)
21161     {
21162         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21163     },
21164     
21165     setActive : function(state)
21166     {
21167         Roo.log("panel - set active " + this.tabId + "=" + state);
21168         
21169         this.active = state;
21170         if (!state) {
21171             this.el.removeClass('active');
21172             
21173         } else  if (!this.el.hasClass('active')) {
21174             this.el.addClass('active');
21175         }
21176         
21177         this.fireEvent('changed', this, state);
21178     },
21179     
21180     onClick : function(e)
21181     {
21182         e.preventDefault();
21183         
21184         if(!this.href.length){
21185             return;
21186         }
21187         
21188         window.location.href = this.href;
21189     },
21190     
21191     startX : 0,
21192     startY : 0,
21193     endX : 0,
21194     endY : 0,
21195     swiping : false,
21196     
21197     onTouchStart : function(e)
21198     {
21199         this.swiping = false;
21200         
21201         this.startX = e.browserEvent.touches[0].clientX;
21202         this.startY = e.browserEvent.touches[0].clientY;
21203     },
21204     
21205     onTouchMove : function(e)
21206     {
21207         this.swiping = true;
21208         
21209         this.endX = e.browserEvent.touches[0].clientX;
21210         this.endY = e.browserEvent.touches[0].clientY;
21211     },
21212     
21213     onTouchEnd : function(e)
21214     {
21215         if(!this.swiping){
21216             this.onClick(e);
21217             return;
21218         }
21219         
21220         var tabGroup = this.parent();
21221         
21222         if(this.endX > this.startX){ // swiping right
21223             tabGroup.showPanelPrev();
21224             return;
21225         }
21226         
21227         if(this.startX > this.endX){ // swiping left
21228             tabGroup.showPanelNext();
21229             return;
21230         }
21231     }
21232     
21233     
21234 });
21235  
21236
21237  
21238
21239  /*
21240  * - LGPL
21241  *
21242  * DateField
21243  * 
21244  */
21245
21246 /**
21247  * @class Roo.bootstrap.DateField
21248  * @extends Roo.bootstrap.Input
21249  * Bootstrap DateField class
21250  * @cfg {Number} weekStart default 0
21251  * @cfg {String} viewMode default empty, (months|years)
21252  * @cfg {String} minViewMode default empty, (months|years)
21253  * @cfg {Number} startDate default -Infinity
21254  * @cfg {Number} endDate default Infinity
21255  * @cfg {Boolean} todayHighlight default false
21256  * @cfg {Boolean} todayBtn default false
21257  * @cfg {Boolean} calendarWeeks default false
21258  * @cfg {Object} daysOfWeekDisabled default empty
21259  * @cfg {Boolean} singleMode default false (true | false)
21260  * 
21261  * @cfg {Boolean} keyboardNavigation default true
21262  * @cfg {String} language default en
21263  * 
21264  * @constructor
21265  * Create a new DateField
21266  * @param {Object} config The config object
21267  */
21268
21269 Roo.bootstrap.DateField = function(config){
21270     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21271      this.addEvents({
21272             /**
21273              * @event show
21274              * Fires when this field show.
21275              * @param {Roo.bootstrap.DateField} this
21276              * @param {Mixed} date The date value
21277              */
21278             show : true,
21279             /**
21280              * @event show
21281              * Fires when this field hide.
21282              * @param {Roo.bootstrap.DateField} this
21283              * @param {Mixed} date The date value
21284              */
21285             hide : true,
21286             /**
21287              * @event select
21288              * Fires when select a date.
21289              * @param {Roo.bootstrap.DateField} this
21290              * @param {Mixed} date The date value
21291              */
21292             select : true,
21293             /**
21294              * @event beforeselect
21295              * Fires when before select a date.
21296              * @param {Roo.bootstrap.DateField} this
21297              * @param {Mixed} date The date value
21298              */
21299             beforeselect : true
21300         });
21301 };
21302
21303 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21304     
21305     /**
21306      * @cfg {String} format
21307      * The default date format string which can be overriden for localization support.  The format must be
21308      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21309      */
21310     format : "m/d/y",
21311     /**
21312      * @cfg {String} altFormats
21313      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21314      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21315      */
21316     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21317     
21318     weekStart : 0,
21319     
21320     viewMode : '',
21321     
21322     minViewMode : '',
21323     
21324     todayHighlight : false,
21325     
21326     todayBtn: false,
21327     
21328     language: 'en',
21329     
21330     keyboardNavigation: true,
21331     
21332     calendarWeeks: false,
21333     
21334     startDate: -Infinity,
21335     
21336     endDate: Infinity,
21337     
21338     daysOfWeekDisabled: [],
21339     
21340     _events: [],
21341     
21342     singleMode : false,
21343     
21344     UTCDate: function()
21345     {
21346         return new Date(Date.UTC.apply(Date, arguments));
21347     },
21348     
21349     UTCToday: function()
21350     {
21351         var today = new Date();
21352         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21353     },
21354     
21355     getDate: function() {
21356             var d = this.getUTCDate();
21357             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21358     },
21359     
21360     getUTCDate: function() {
21361             return this.date;
21362     },
21363     
21364     setDate: function(d) {
21365             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21366     },
21367     
21368     setUTCDate: function(d) {
21369             this.date = d;
21370             this.setValue(this.formatDate(this.date));
21371     },
21372         
21373     onRender: function(ct, position)
21374     {
21375         
21376         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21377         
21378         this.language = this.language || 'en';
21379         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21380         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21381         
21382         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21383         this.format = this.format || 'm/d/y';
21384         this.isInline = false;
21385         this.isInput = true;
21386         this.component = this.el.select('.add-on', true).first() || false;
21387         this.component = (this.component && this.component.length === 0) ? false : this.component;
21388         this.hasInput = this.component && this.inputEl().length;
21389         
21390         if (typeof(this.minViewMode === 'string')) {
21391             switch (this.minViewMode) {
21392                 case 'months':
21393                     this.minViewMode = 1;
21394                     break;
21395                 case 'years':
21396                     this.minViewMode = 2;
21397                     break;
21398                 default:
21399                     this.minViewMode = 0;
21400                     break;
21401             }
21402         }
21403         
21404         if (typeof(this.viewMode === 'string')) {
21405             switch (this.viewMode) {
21406                 case 'months':
21407                     this.viewMode = 1;
21408                     break;
21409                 case 'years':
21410                     this.viewMode = 2;
21411                     break;
21412                 default:
21413                     this.viewMode = 0;
21414                     break;
21415             }
21416         }
21417                 
21418         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21419         
21420 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21421         
21422         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21423         
21424         this.picker().on('mousedown', this.onMousedown, this);
21425         this.picker().on('click', this.onClick, this);
21426         
21427         this.picker().addClass('datepicker-dropdown');
21428         
21429         this.startViewMode = this.viewMode;
21430         
21431         if(this.singleMode){
21432             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21433                 v.setVisibilityMode(Roo.Element.DISPLAY);
21434                 v.hide();
21435             });
21436             
21437             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21438                 v.setStyle('width', '189px');
21439             });
21440         }
21441         
21442         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21443             if(!this.calendarWeeks){
21444                 v.remove();
21445                 return;
21446             }
21447             
21448             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21449             v.attr('colspan', function(i, val){
21450                 return parseInt(val) + 1;
21451             });
21452         });
21453                         
21454         
21455         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21456         
21457         this.setStartDate(this.startDate);
21458         this.setEndDate(this.endDate);
21459         
21460         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21461         
21462         this.fillDow();
21463         this.fillMonths();
21464         this.update();
21465         this.showMode();
21466         
21467         if(this.isInline) {
21468             this.showPopup();
21469         }
21470     },
21471     
21472     picker : function()
21473     {
21474         return this.pickerEl;
21475 //        return this.el.select('.datepicker', true).first();
21476     },
21477     
21478     fillDow: function()
21479     {
21480         var dowCnt = this.weekStart;
21481         
21482         var dow = {
21483             tag: 'tr',
21484             cn: [
21485                 
21486             ]
21487         };
21488         
21489         if(this.calendarWeeks){
21490             dow.cn.push({
21491                 tag: 'th',
21492                 cls: 'cw',
21493                 html: '&nbsp;'
21494             })
21495         }
21496         
21497         while (dowCnt < this.weekStart + 7) {
21498             dow.cn.push({
21499                 tag: 'th',
21500                 cls: 'dow',
21501                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21502             });
21503         }
21504         
21505         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21506     },
21507     
21508     fillMonths: function()
21509     {    
21510         var i = 0;
21511         var months = this.picker().select('>.datepicker-months td', true).first();
21512         
21513         months.dom.innerHTML = '';
21514         
21515         while (i < 12) {
21516             var month = {
21517                 tag: 'span',
21518                 cls: 'month',
21519                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21520             };
21521             
21522             months.createChild(month);
21523         }
21524         
21525     },
21526     
21527     update: function()
21528     {
21529         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;
21530         
21531         if (this.date < this.startDate) {
21532             this.viewDate = new Date(this.startDate);
21533         } else if (this.date > this.endDate) {
21534             this.viewDate = new Date(this.endDate);
21535         } else {
21536             this.viewDate = new Date(this.date);
21537         }
21538         
21539         this.fill();
21540     },
21541     
21542     fill: function() 
21543     {
21544         var d = new Date(this.viewDate),
21545                 year = d.getUTCFullYear(),
21546                 month = d.getUTCMonth(),
21547                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21548                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21549                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21550                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21551                 currentDate = this.date && this.date.valueOf(),
21552                 today = this.UTCToday();
21553         
21554         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21555         
21556 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21557         
21558 //        this.picker.select('>tfoot th.today').
21559 //                                              .text(dates[this.language].today)
21560 //                                              .toggle(this.todayBtn !== false);
21561     
21562         this.updateNavArrows();
21563         this.fillMonths();
21564                                                 
21565         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21566         
21567         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21568          
21569         prevMonth.setUTCDate(day);
21570         
21571         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21572         
21573         var nextMonth = new Date(prevMonth);
21574         
21575         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21576         
21577         nextMonth = nextMonth.valueOf();
21578         
21579         var fillMonths = false;
21580         
21581         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21582         
21583         while(prevMonth.valueOf() <= nextMonth) {
21584             var clsName = '';
21585             
21586             if (prevMonth.getUTCDay() === this.weekStart) {
21587                 if(fillMonths){
21588                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21589                 }
21590                     
21591                 fillMonths = {
21592                     tag: 'tr',
21593                     cn: []
21594                 };
21595                 
21596                 if(this.calendarWeeks){
21597                     // ISO 8601: First week contains first thursday.
21598                     // ISO also states week starts on Monday, but we can be more abstract here.
21599                     var
21600                     // Start of current week: based on weekstart/current date
21601                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21602                     // Thursday of this week
21603                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21604                     // First Thursday of year, year from thursday
21605                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21606                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21607                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21608                     
21609                     fillMonths.cn.push({
21610                         tag: 'td',
21611                         cls: 'cw',
21612                         html: calWeek
21613                     });
21614                 }
21615             }
21616             
21617             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21618                 clsName += ' old';
21619             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21620                 clsName += ' new';
21621             }
21622             if (this.todayHighlight &&
21623                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21624                 prevMonth.getUTCMonth() == today.getMonth() &&
21625                 prevMonth.getUTCDate() == today.getDate()) {
21626                 clsName += ' today';
21627             }
21628             
21629             if (currentDate && prevMonth.valueOf() === currentDate) {
21630                 clsName += ' active';
21631             }
21632             
21633             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21634                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21635                     clsName += ' disabled';
21636             }
21637             
21638             fillMonths.cn.push({
21639                 tag: 'td',
21640                 cls: 'day ' + clsName,
21641                 html: prevMonth.getDate()
21642             });
21643             
21644             prevMonth.setDate(prevMonth.getDate()+1);
21645         }
21646           
21647         var currentYear = this.date && this.date.getUTCFullYear();
21648         var currentMonth = this.date && this.date.getUTCMonth();
21649         
21650         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21651         
21652         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21653             v.removeClass('active');
21654             
21655             if(currentYear === year && k === currentMonth){
21656                 v.addClass('active');
21657             }
21658             
21659             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21660                 v.addClass('disabled');
21661             }
21662             
21663         });
21664         
21665         
21666         year = parseInt(year/10, 10) * 10;
21667         
21668         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21669         
21670         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21671         
21672         year -= 1;
21673         for (var i = -1; i < 11; i++) {
21674             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21675                 tag: 'span',
21676                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21677                 html: year
21678             });
21679             
21680             year += 1;
21681         }
21682     },
21683     
21684     showMode: function(dir) 
21685     {
21686         if (dir) {
21687             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21688         }
21689         
21690         Roo.each(this.picker().select('>div',true).elements, function(v){
21691             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21692             v.hide();
21693         });
21694         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21695     },
21696     
21697     place: function()
21698     {
21699         if(this.isInline) {
21700             return;
21701         }
21702         
21703         this.picker().removeClass(['bottom', 'top']);
21704         
21705         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21706             /*
21707              * place to the top of element!
21708              *
21709              */
21710             
21711             this.picker().addClass('top');
21712             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21713             
21714             return;
21715         }
21716         
21717         this.picker().addClass('bottom');
21718         
21719         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21720     },
21721     
21722     parseDate : function(value)
21723     {
21724         if(!value || value instanceof Date){
21725             return value;
21726         }
21727         var v = Date.parseDate(value, this.format);
21728         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21729             v = Date.parseDate(value, 'Y-m-d');
21730         }
21731         if(!v && this.altFormats){
21732             if(!this.altFormatsArray){
21733                 this.altFormatsArray = this.altFormats.split("|");
21734             }
21735             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21736                 v = Date.parseDate(value, this.altFormatsArray[i]);
21737             }
21738         }
21739         return v;
21740     },
21741     
21742     formatDate : function(date, fmt)
21743     {   
21744         return (!date || !(date instanceof Date)) ?
21745         date : date.dateFormat(fmt || this.format);
21746     },
21747     
21748     onFocus : function()
21749     {
21750         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21751         this.showPopup();
21752     },
21753     
21754     onBlur : function()
21755     {
21756         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21757         
21758         var d = this.inputEl().getValue();
21759         
21760         this.setValue(d);
21761                 
21762         this.hidePopup();
21763     },
21764     
21765     showPopup : function()
21766     {
21767         this.picker().show();
21768         this.update();
21769         this.place();
21770         
21771         this.fireEvent('showpopup', this, this.date);
21772     },
21773     
21774     hidePopup : function()
21775     {
21776         if(this.isInline) {
21777             return;
21778         }
21779         this.picker().hide();
21780         this.viewMode = this.startViewMode;
21781         this.showMode();
21782         
21783         this.fireEvent('hidepopup', this, this.date);
21784         
21785     },
21786     
21787     onMousedown: function(e)
21788     {
21789         e.stopPropagation();
21790         e.preventDefault();
21791     },
21792     
21793     keyup: function(e)
21794     {
21795         Roo.bootstrap.DateField.superclass.keyup.call(this);
21796         this.update();
21797     },
21798
21799     setValue: function(v)
21800     {
21801         if(this.fireEvent('beforeselect', this, v) !== false){
21802             var d = new Date(this.parseDate(v) ).clearTime();
21803         
21804             if(isNaN(d.getTime())){
21805                 this.date = this.viewDate = '';
21806                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21807                 return;
21808             }
21809
21810             v = this.formatDate(d);
21811
21812             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21813
21814             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21815
21816             this.update();
21817
21818             this.fireEvent('select', this, this.date);
21819         }
21820     },
21821     
21822     getValue: function()
21823     {
21824         return this.formatDate(this.date);
21825     },
21826     
21827     fireKey: function(e)
21828     {
21829         if (!this.picker().isVisible()){
21830             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21831                 this.showPopup();
21832             }
21833             return;
21834         }
21835         
21836         var dateChanged = false,
21837         dir, day, month,
21838         newDate, newViewDate;
21839         
21840         switch(e.keyCode){
21841             case 27: // escape
21842                 this.hidePopup();
21843                 e.preventDefault();
21844                 break;
21845             case 37: // left
21846             case 39: // right
21847                 if (!this.keyboardNavigation) {
21848                     break;
21849                 }
21850                 dir = e.keyCode == 37 ? -1 : 1;
21851                 
21852                 if (e.ctrlKey){
21853                     newDate = this.moveYear(this.date, dir);
21854                     newViewDate = this.moveYear(this.viewDate, dir);
21855                 } else if (e.shiftKey){
21856                     newDate = this.moveMonth(this.date, dir);
21857                     newViewDate = this.moveMonth(this.viewDate, dir);
21858                 } else {
21859                     newDate = new Date(this.date);
21860                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21861                     newViewDate = new Date(this.viewDate);
21862                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21863                 }
21864                 if (this.dateWithinRange(newDate)){
21865                     this.date = newDate;
21866                     this.viewDate = newViewDate;
21867                     this.setValue(this.formatDate(this.date));
21868 //                    this.update();
21869                     e.preventDefault();
21870                     dateChanged = true;
21871                 }
21872                 break;
21873             case 38: // up
21874             case 40: // down
21875                 if (!this.keyboardNavigation) {
21876                     break;
21877                 }
21878                 dir = e.keyCode == 38 ? -1 : 1;
21879                 if (e.ctrlKey){
21880                     newDate = this.moveYear(this.date, dir);
21881                     newViewDate = this.moveYear(this.viewDate, dir);
21882                 } else if (e.shiftKey){
21883                     newDate = this.moveMonth(this.date, dir);
21884                     newViewDate = this.moveMonth(this.viewDate, dir);
21885                 } else {
21886                     newDate = new Date(this.date);
21887                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21888                     newViewDate = new Date(this.viewDate);
21889                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21890                 }
21891                 if (this.dateWithinRange(newDate)){
21892                     this.date = newDate;
21893                     this.viewDate = newViewDate;
21894                     this.setValue(this.formatDate(this.date));
21895 //                    this.update();
21896                     e.preventDefault();
21897                     dateChanged = true;
21898                 }
21899                 break;
21900             case 13: // enter
21901                 this.setValue(this.formatDate(this.date));
21902                 this.hidePopup();
21903                 e.preventDefault();
21904                 break;
21905             case 9: // tab
21906                 this.setValue(this.formatDate(this.date));
21907                 this.hidePopup();
21908                 break;
21909             case 16: // shift
21910             case 17: // ctrl
21911             case 18: // alt
21912                 break;
21913             default :
21914                 this.hidePopup();
21915                 
21916         }
21917     },
21918     
21919     
21920     onClick: function(e) 
21921     {
21922         e.stopPropagation();
21923         e.preventDefault();
21924         
21925         var target = e.getTarget();
21926         
21927         if(target.nodeName.toLowerCase() === 'i'){
21928             target = Roo.get(target).dom.parentNode;
21929         }
21930         
21931         var nodeName = target.nodeName;
21932         var className = target.className;
21933         var html = target.innerHTML;
21934         //Roo.log(nodeName);
21935         
21936         switch(nodeName.toLowerCase()) {
21937             case 'th':
21938                 switch(className) {
21939                     case 'switch':
21940                         this.showMode(1);
21941                         break;
21942                     case 'prev':
21943                     case 'next':
21944                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21945                         switch(this.viewMode){
21946                                 case 0:
21947                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21948                                         break;
21949                                 case 1:
21950                                 case 2:
21951                                         this.viewDate = this.moveYear(this.viewDate, dir);
21952                                         break;
21953                         }
21954                         this.fill();
21955                         break;
21956                     case 'today':
21957                         var date = new Date();
21958                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21959 //                        this.fill()
21960                         this.setValue(this.formatDate(this.date));
21961                         
21962                         this.hidePopup();
21963                         break;
21964                 }
21965                 break;
21966             case 'span':
21967                 if (className.indexOf('disabled') < 0) {
21968                 if (!this.viewDate) {
21969                     this.viewDate = new Date();
21970                 }
21971                 this.viewDate.setUTCDate(1);
21972                     if (className.indexOf('month') > -1) {
21973                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21974                     } else {
21975                         var year = parseInt(html, 10) || 0;
21976                         this.viewDate.setUTCFullYear(year);
21977                         
21978                     }
21979                     
21980                     if(this.singleMode){
21981                         this.setValue(this.formatDate(this.viewDate));
21982                         this.hidePopup();
21983                         return;
21984                     }
21985                     
21986                     this.showMode(-1);
21987                     this.fill();
21988                 }
21989                 break;
21990                 
21991             case 'td':
21992                 //Roo.log(className);
21993                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21994                     var day = parseInt(html, 10) || 1;
21995                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21996                         month = (this.viewDate || new Date()).getUTCMonth();
21997
21998                     if (className.indexOf('old') > -1) {
21999                         if(month === 0 ){
22000                             month = 11;
22001                             year -= 1;
22002                         }else{
22003                             month -= 1;
22004                         }
22005                     } else if (className.indexOf('new') > -1) {
22006                         if (month == 11) {
22007                             month = 0;
22008                             year += 1;
22009                         } else {
22010                             month += 1;
22011                         }
22012                     }
22013                     //Roo.log([year,month,day]);
22014                     this.date = this.UTCDate(year, month, day,0,0,0,0);
22015                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22016 //                    this.fill();
22017                     //Roo.log(this.formatDate(this.date));
22018                     this.setValue(this.formatDate(this.date));
22019                     this.hidePopup();
22020                 }
22021                 break;
22022         }
22023     },
22024     
22025     setStartDate: function(startDate)
22026     {
22027         this.startDate = startDate || -Infinity;
22028         if (this.startDate !== -Infinity) {
22029             this.startDate = this.parseDate(this.startDate);
22030         }
22031         this.update();
22032         this.updateNavArrows();
22033     },
22034
22035     setEndDate: function(endDate)
22036     {
22037         this.endDate = endDate || Infinity;
22038         if (this.endDate !== Infinity) {
22039             this.endDate = this.parseDate(this.endDate);
22040         }
22041         this.update();
22042         this.updateNavArrows();
22043     },
22044     
22045     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22046     {
22047         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22048         if (typeof(this.daysOfWeekDisabled) !== 'object') {
22049             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22050         }
22051         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22052             return parseInt(d, 10);
22053         });
22054         this.update();
22055         this.updateNavArrows();
22056     },
22057     
22058     updateNavArrows: function() 
22059     {
22060         if(this.singleMode){
22061             return;
22062         }
22063         
22064         var d = new Date(this.viewDate),
22065         year = d.getUTCFullYear(),
22066         month = d.getUTCMonth();
22067         
22068         Roo.each(this.picker().select('.prev', true).elements, function(v){
22069             v.show();
22070             switch (this.viewMode) {
22071                 case 0:
22072
22073                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22074                         v.hide();
22075                     }
22076                     break;
22077                 case 1:
22078                 case 2:
22079                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22080                         v.hide();
22081                     }
22082                     break;
22083             }
22084         });
22085         
22086         Roo.each(this.picker().select('.next', true).elements, function(v){
22087             v.show();
22088             switch (this.viewMode) {
22089                 case 0:
22090
22091                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22092                         v.hide();
22093                     }
22094                     break;
22095                 case 1:
22096                 case 2:
22097                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22098                         v.hide();
22099                     }
22100                     break;
22101             }
22102         })
22103     },
22104     
22105     moveMonth: function(date, dir)
22106     {
22107         if (!dir) {
22108             return date;
22109         }
22110         var new_date = new Date(date.valueOf()),
22111         day = new_date.getUTCDate(),
22112         month = new_date.getUTCMonth(),
22113         mag = Math.abs(dir),
22114         new_month, test;
22115         dir = dir > 0 ? 1 : -1;
22116         if (mag == 1){
22117             test = dir == -1
22118             // If going back one month, make sure month is not current month
22119             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22120             ? function(){
22121                 return new_date.getUTCMonth() == month;
22122             }
22123             // If going forward one month, make sure month is as expected
22124             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22125             : function(){
22126                 return new_date.getUTCMonth() != new_month;
22127             };
22128             new_month = month + dir;
22129             new_date.setUTCMonth(new_month);
22130             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22131             if (new_month < 0 || new_month > 11) {
22132                 new_month = (new_month + 12) % 12;
22133             }
22134         } else {
22135             // For magnitudes >1, move one month at a time...
22136             for (var i=0; i<mag; i++) {
22137                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22138                 new_date = this.moveMonth(new_date, dir);
22139             }
22140             // ...then reset the day, keeping it in the new month
22141             new_month = new_date.getUTCMonth();
22142             new_date.setUTCDate(day);
22143             test = function(){
22144                 return new_month != new_date.getUTCMonth();
22145             };
22146         }
22147         // Common date-resetting loop -- if date is beyond end of month, make it
22148         // end of month
22149         while (test()){
22150             new_date.setUTCDate(--day);
22151             new_date.setUTCMonth(new_month);
22152         }
22153         return new_date;
22154     },
22155
22156     moveYear: function(date, dir)
22157     {
22158         return this.moveMonth(date, dir*12);
22159     },
22160
22161     dateWithinRange: function(date)
22162     {
22163         return date >= this.startDate && date <= this.endDate;
22164     },
22165
22166     
22167     remove: function() 
22168     {
22169         this.picker().remove();
22170     },
22171     
22172     validateValue : function(value)
22173     {
22174         if(this.getVisibilityEl().hasClass('hidden')){
22175             return true;
22176         }
22177         
22178         if(value.length < 1)  {
22179             if(this.allowBlank){
22180                 return true;
22181             }
22182             return false;
22183         }
22184         
22185         if(value.length < this.minLength){
22186             return false;
22187         }
22188         if(value.length > this.maxLength){
22189             return false;
22190         }
22191         if(this.vtype){
22192             var vt = Roo.form.VTypes;
22193             if(!vt[this.vtype](value, this)){
22194                 return false;
22195             }
22196         }
22197         if(typeof this.validator == "function"){
22198             var msg = this.validator(value);
22199             if(msg !== true){
22200                 return false;
22201             }
22202         }
22203         
22204         if(this.regex && !this.regex.test(value)){
22205             return false;
22206         }
22207         
22208         if(typeof(this.parseDate(value)) == 'undefined'){
22209             return false;
22210         }
22211         
22212         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22213             return false;
22214         }      
22215         
22216         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22217             return false;
22218         } 
22219         
22220         
22221         return true;
22222     },
22223     
22224     reset : function()
22225     {
22226         this.date = this.viewDate = '';
22227         
22228         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22229     }
22230    
22231 });
22232
22233 Roo.apply(Roo.bootstrap.DateField,  {
22234     
22235     head : {
22236         tag: 'thead',
22237         cn: [
22238         {
22239             tag: 'tr',
22240             cn: [
22241             {
22242                 tag: 'th',
22243                 cls: 'prev',
22244                 html: '<i class="fa fa-arrow-left"/>'
22245             },
22246             {
22247                 tag: 'th',
22248                 cls: 'switch',
22249                 colspan: '5'
22250             },
22251             {
22252                 tag: 'th',
22253                 cls: 'next',
22254                 html: '<i class="fa fa-arrow-right"/>'
22255             }
22256
22257             ]
22258         }
22259         ]
22260     },
22261     
22262     content : {
22263         tag: 'tbody',
22264         cn: [
22265         {
22266             tag: 'tr',
22267             cn: [
22268             {
22269                 tag: 'td',
22270                 colspan: '7'
22271             }
22272             ]
22273         }
22274         ]
22275     },
22276     
22277     footer : {
22278         tag: 'tfoot',
22279         cn: [
22280         {
22281             tag: 'tr',
22282             cn: [
22283             {
22284                 tag: 'th',
22285                 colspan: '7',
22286                 cls: 'today'
22287             }
22288                     
22289             ]
22290         }
22291         ]
22292     },
22293     
22294     dates:{
22295         en: {
22296             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22297             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22298             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22299             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22300             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22301             today: "Today"
22302         }
22303     },
22304     
22305     modes: [
22306     {
22307         clsName: 'days',
22308         navFnc: 'Month',
22309         navStep: 1
22310     },
22311     {
22312         clsName: 'months',
22313         navFnc: 'FullYear',
22314         navStep: 1
22315     },
22316     {
22317         clsName: 'years',
22318         navFnc: 'FullYear',
22319         navStep: 10
22320     }]
22321 });
22322
22323 Roo.apply(Roo.bootstrap.DateField,  {
22324   
22325     template : {
22326         tag: 'div',
22327         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22328         cn: [
22329         {
22330             tag: 'div',
22331             cls: 'datepicker-days',
22332             cn: [
22333             {
22334                 tag: 'table',
22335                 cls: 'table-condensed',
22336                 cn:[
22337                 Roo.bootstrap.DateField.head,
22338                 {
22339                     tag: 'tbody'
22340                 },
22341                 Roo.bootstrap.DateField.footer
22342                 ]
22343             }
22344             ]
22345         },
22346         {
22347             tag: 'div',
22348             cls: 'datepicker-months',
22349             cn: [
22350             {
22351                 tag: 'table',
22352                 cls: 'table-condensed',
22353                 cn:[
22354                 Roo.bootstrap.DateField.head,
22355                 Roo.bootstrap.DateField.content,
22356                 Roo.bootstrap.DateField.footer
22357                 ]
22358             }
22359             ]
22360         },
22361         {
22362             tag: 'div',
22363             cls: 'datepicker-years',
22364             cn: [
22365             {
22366                 tag: 'table',
22367                 cls: 'table-condensed',
22368                 cn:[
22369                 Roo.bootstrap.DateField.head,
22370                 Roo.bootstrap.DateField.content,
22371                 Roo.bootstrap.DateField.footer
22372                 ]
22373             }
22374             ]
22375         }
22376         ]
22377     }
22378 });
22379
22380  
22381
22382  /*
22383  * - LGPL
22384  *
22385  * TimeField
22386  * 
22387  */
22388
22389 /**
22390  * @class Roo.bootstrap.TimeField
22391  * @extends Roo.bootstrap.Input
22392  * Bootstrap DateField class
22393  * 
22394  * 
22395  * @constructor
22396  * Create a new TimeField
22397  * @param {Object} config The config object
22398  */
22399
22400 Roo.bootstrap.TimeField = function(config){
22401     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22402     this.addEvents({
22403             /**
22404              * @event show
22405              * Fires when this field show.
22406              * @param {Roo.bootstrap.DateField} thisthis
22407              * @param {Mixed} date The date value
22408              */
22409             show : true,
22410             /**
22411              * @event show
22412              * Fires when this field hide.
22413              * @param {Roo.bootstrap.DateField} this
22414              * @param {Mixed} date The date value
22415              */
22416             hide : true,
22417             /**
22418              * @event select
22419              * Fires when select a date.
22420              * @param {Roo.bootstrap.DateField} this
22421              * @param {Mixed} date The date value
22422              */
22423             select : true
22424         });
22425 };
22426
22427 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22428     
22429     /**
22430      * @cfg {String} format
22431      * The default time format string which can be overriden for localization support.  The format must be
22432      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22433      */
22434     format : "H:i",
22435
22436     getAutoCreate : function()
22437     {
22438         this.after = '<i class="fa far fa-clock"></i>';
22439         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22440         
22441          
22442     },
22443     onRender: function(ct, position)
22444     {
22445         
22446         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22447                 
22448         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22449         
22450         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22451         
22452         this.pop = this.picker().select('>.datepicker-time',true).first();
22453         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22454         
22455         this.picker().on('mousedown', this.onMousedown, this);
22456         this.picker().on('click', this.onClick, this);
22457         
22458         this.picker().addClass('datepicker-dropdown');
22459     
22460         this.fillTime();
22461         this.update();
22462             
22463         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22464         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22465         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22466         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22467         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22468         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22469
22470     },
22471     
22472     fireKey: function(e){
22473         if (!this.picker().isVisible()){
22474             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22475                 this.show();
22476             }
22477             return;
22478         }
22479
22480         e.preventDefault();
22481         
22482         switch(e.keyCode){
22483             case 27: // escape
22484                 this.hide();
22485                 break;
22486             case 37: // left
22487             case 39: // right
22488                 this.onTogglePeriod();
22489                 break;
22490             case 38: // up
22491                 this.onIncrementMinutes();
22492                 break;
22493             case 40: // down
22494                 this.onDecrementMinutes();
22495                 break;
22496             case 13: // enter
22497             case 9: // tab
22498                 this.setTime();
22499                 break;
22500         }
22501     },
22502     
22503     onClick: function(e) {
22504         e.stopPropagation();
22505         e.preventDefault();
22506     },
22507     
22508     picker : function()
22509     {
22510         return this.pickerEl;
22511     },
22512     
22513     fillTime: function()
22514     {    
22515         var time = this.pop.select('tbody', true).first();
22516         
22517         time.dom.innerHTML = '';
22518         
22519         time.createChild({
22520             tag: 'tr',
22521             cn: [
22522                 {
22523                     tag: 'td',
22524                     cn: [
22525                         {
22526                             tag: 'a',
22527                             href: '#',
22528                             cls: 'btn',
22529                             cn: [
22530                                 {
22531                                     tag: 'i',
22532                                     cls: 'hours-up fa fas fa-chevron-up'
22533                                 }
22534                             ]
22535                         } 
22536                     ]
22537                 },
22538                 {
22539                     tag: 'td',
22540                     cls: 'separator'
22541                 },
22542                 {
22543                     tag: 'td',
22544                     cn: [
22545                         {
22546                             tag: 'a',
22547                             href: '#',
22548                             cls: 'btn',
22549                             cn: [
22550                                 {
22551                                     tag: 'i',
22552                                     cls: 'minutes-up fa fas fa-chevron-up'
22553                                 }
22554                             ]
22555                         }
22556                     ]
22557                 },
22558                 {
22559                     tag: 'td',
22560                     cls: 'separator'
22561                 }
22562             ]
22563         });
22564         
22565         time.createChild({
22566             tag: 'tr',
22567             cn: [
22568                 {
22569                     tag: 'td',
22570                     cn: [
22571                         {
22572                             tag: 'span',
22573                             cls: 'timepicker-hour',
22574                             html: '00'
22575                         }  
22576                     ]
22577                 },
22578                 {
22579                     tag: 'td',
22580                     cls: 'separator',
22581                     html: ':'
22582                 },
22583                 {
22584                     tag: 'td',
22585                     cn: [
22586                         {
22587                             tag: 'span',
22588                             cls: 'timepicker-minute',
22589                             html: '00'
22590                         }  
22591                     ]
22592                 },
22593                 {
22594                     tag: 'td',
22595                     cls: 'separator'
22596                 },
22597                 {
22598                     tag: 'td',
22599                     cn: [
22600                         {
22601                             tag: 'button',
22602                             type: 'button',
22603                             cls: 'btn btn-primary period',
22604                             html: 'AM'
22605                             
22606                         }
22607                     ]
22608                 }
22609             ]
22610         });
22611         
22612         time.createChild({
22613             tag: 'tr',
22614             cn: [
22615                 {
22616                     tag: 'td',
22617                     cn: [
22618                         {
22619                             tag: 'a',
22620                             href: '#',
22621                             cls: 'btn',
22622                             cn: [
22623                                 {
22624                                     tag: 'span',
22625                                     cls: 'hours-down fa fas fa-chevron-down'
22626                                 }
22627                             ]
22628                         }
22629                     ]
22630                 },
22631                 {
22632                     tag: 'td',
22633                     cls: 'separator'
22634                 },
22635                 {
22636                     tag: 'td',
22637                     cn: [
22638                         {
22639                             tag: 'a',
22640                             href: '#',
22641                             cls: 'btn',
22642                             cn: [
22643                                 {
22644                                     tag: 'span',
22645                                     cls: 'minutes-down fa fas fa-chevron-down'
22646                                 }
22647                             ]
22648                         }
22649                     ]
22650                 },
22651                 {
22652                     tag: 'td',
22653                     cls: 'separator'
22654                 }
22655             ]
22656         });
22657         
22658     },
22659     
22660     update: function()
22661     {
22662         
22663         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22664         
22665         this.fill();
22666     },
22667     
22668     fill: function() 
22669     {
22670         var hours = this.time.getHours();
22671         var minutes = this.time.getMinutes();
22672         var period = 'AM';
22673         
22674         if(hours > 11){
22675             period = 'PM';
22676         }
22677         
22678         if(hours == 0){
22679             hours = 12;
22680         }
22681         
22682         
22683         if(hours > 12){
22684             hours = hours - 12;
22685         }
22686         
22687         if(hours < 10){
22688             hours = '0' + hours;
22689         }
22690         
22691         if(minutes < 10){
22692             minutes = '0' + minutes;
22693         }
22694         
22695         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22696         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22697         this.pop.select('button', true).first().dom.innerHTML = period;
22698         
22699     },
22700     
22701     place: function()
22702     {   
22703         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22704         
22705         var cls = ['bottom'];
22706         
22707         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22708             cls.pop();
22709             cls.push('top');
22710         }
22711         
22712         cls.push('right');
22713         
22714         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22715             cls.pop();
22716             cls.push('left');
22717         }
22718         //this.picker().setXY(20000,20000);
22719         this.picker().addClass(cls.join('-'));
22720         
22721         var _this = this;
22722         
22723         Roo.each(cls, function(c){
22724             if(c == 'bottom'){
22725                 (function() {
22726                  //  
22727                 }).defer(200);
22728                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22729                 //_this.picker().setTop(_this.inputEl().getHeight());
22730                 return;
22731             }
22732             if(c == 'top'){
22733                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22734                 
22735                 //_this.picker().setTop(0 - _this.picker().getHeight());
22736                 return;
22737             }
22738             /*
22739             if(c == 'left'){
22740                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22741                 return;
22742             }
22743             if(c == 'right'){
22744                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22745                 return;
22746             }
22747             */
22748         });
22749         
22750     },
22751   
22752     onFocus : function()
22753     {
22754         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22755         this.show();
22756     },
22757     
22758     onBlur : function()
22759     {
22760         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22761         this.hide();
22762     },
22763     
22764     show : function()
22765     {
22766         this.picker().show();
22767         this.pop.show();
22768         this.update();
22769         this.place();
22770         
22771         this.fireEvent('show', this, this.date);
22772     },
22773     
22774     hide : function()
22775     {
22776         this.picker().hide();
22777         this.pop.hide();
22778         
22779         this.fireEvent('hide', this, this.date);
22780     },
22781     
22782     setTime : function()
22783     {
22784         this.hide();
22785         this.setValue(this.time.format(this.format));
22786         
22787         this.fireEvent('select', this, this.date);
22788         
22789         
22790     },
22791     
22792     onMousedown: function(e){
22793         e.stopPropagation();
22794         e.preventDefault();
22795     },
22796     
22797     onIncrementHours: function()
22798     {
22799         Roo.log('onIncrementHours');
22800         this.time = this.time.add(Date.HOUR, 1);
22801         this.update();
22802         
22803     },
22804     
22805     onDecrementHours: function()
22806     {
22807         Roo.log('onDecrementHours');
22808         this.time = this.time.add(Date.HOUR, -1);
22809         this.update();
22810     },
22811     
22812     onIncrementMinutes: function()
22813     {
22814         Roo.log('onIncrementMinutes');
22815         this.time = this.time.add(Date.MINUTE, 1);
22816         this.update();
22817     },
22818     
22819     onDecrementMinutes: function()
22820     {
22821         Roo.log('onDecrementMinutes');
22822         this.time = this.time.add(Date.MINUTE, -1);
22823         this.update();
22824     },
22825     
22826     onTogglePeriod: function()
22827     {
22828         Roo.log('onTogglePeriod');
22829         this.time = this.time.add(Date.HOUR, 12);
22830         this.update();
22831     }
22832     
22833    
22834 });
22835  
22836
22837 Roo.apply(Roo.bootstrap.TimeField,  {
22838   
22839     template : {
22840         tag: 'div',
22841         cls: 'datepicker dropdown-menu',
22842         cn: [
22843             {
22844                 tag: 'div',
22845                 cls: 'datepicker-time',
22846                 cn: [
22847                 {
22848                     tag: 'table',
22849                     cls: 'table-condensed',
22850                     cn:[
22851                         {
22852                             tag: 'tbody',
22853                             cn: [
22854                                 {
22855                                     tag: 'tr',
22856                                     cn: [
22857                                     {
22858                                         tag: 'td',
22859                                         colspan: '7'
22860                                     }
22861                                     ]
22862                                 }
22863                             ]
22864                         },
22865                         {
22866                             tag: 'tfoot',
22867                             cn: [
22868                                 {
22869                                     tag: 'tr',
22870                                     cn: [
22871                                     {
22872                                         tag: 'th',
22873                                         colspan: '7',
22874                                         cls: '',
22875                                         cn: [
22876                                             {
22877                                                 tag: 'button',
22878                                                 cls: 'btn btn-info ok',
22879                                                 html: 'OK'
22880                                             }
22881                                         ]
22882                                     }
22883                     
22884                                     ]
22885                                 }
22886                             ]
22887                         }
22888                     ]
22889                 }
22890                 ]
22891             }
22892         ]
22893     }
22894 });
22895
22896  
22897
22898  /*
22899  * - LGPL
22900  *
22901  * MonthField
22902  * 
22903  */
22904
22905 /**
22906  * @class Roo.bootstrap.MonthField
22907  * @extends Roo.bootstrap.Input
22908  * Bootstrap MonthField class
22909  * 
22910  * @cfg {String} language default en
22911  * 
22912  * @constructor
22913  * Create a new MonthField
22914  * @param {Object} config The config object
22915  */
22916
22917 Roo.bootstrap.MonthField = function(config){
22918     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22919     
22920     this.addEvents({
22921         /**
22922          * @event show
22923          * Fires when this field show.
22924          * @param {Roo.bootstrap.MonthField} this
22925          * @param {Mixed} date The date value
22926          */
22927         show : true,
22928         /**
22929          * @event show
22930          * Fires when this field hide.
22931          * @param {Roo.bootstrap.MonthField} this
22932          * @param {Mixed} date The date value
22933          */
22934         hide : true,
22935         /**
22936          * @event select
22937          * Fires when select a date.
22938          * @param {Roo.bootstrap.MonthField} this
22939          * @param {String} oldvalue The old value
22940          * @param {String} newvalue The new value
22941          */
22942         select : true
22943     });
22944 };
22945
22946 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22947     
22948     onRender: function(ct, position)
22949     {
22950         
22951         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22952         
22953         this.language = this.language || 'en';
22954         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22955         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22956         
22957         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22958         this.isInline = false;
22959         this.isInput = true;
22960         this.component = this.el.select('.add-on', true).first() || false;
22961         this.component = (this.component && this.component.length === 0) ? false : this.component;
22962         this.hasInput = this.component && this.inputEL().length;
22963         
22964         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22965         
22966         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22967         
22968         this.picker().on('mousedown', this.onMousedown, this);
22969         this.picker().on('click', this.onClick, this);
22970         
22971         this.picker().addClass('datepicker-dropdown');
22972         
22973         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22974             v.setStyle('width', '189px');
22975         });
22976         
22977         this.fillMonths();
22978         
22979         this.update();
22980         
22981         if(this.isInline) {
22982             this.show();
22983         }
22984         
22985     },
22986     
22987     setValue: function(v, suppressEvent)
22988     {   
22989         var o = this.getValue();
22990         
22991         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22992         
22993         this.update();
22994
22995         if(suppressEvent !== true){
22996             this.fireEvent('select', this, o, v);
22997         }
22998         
22999     },
23000     
23001     getValue: function()
23002     {
23003         return this.value;
23004     },
23005     
23006     onClick: function(e) 
23007     {
23008         e.stopPropagation();
23009         e.preventDefault();
23010         
23011         var target = e.getTarget();
23012         
23013         if(target.nodeName.toLowerCase() === 'i'){
23014             target = Roo.get(target).dom.parentNode;
23015         }
23016         
23017         var nodeName = target.nodeName;
23018         var className = target.className;
23019         var html = target.innerHTML;
23020         
23021         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23022             return;
23023         }
23024         
23025         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23026         
23027         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23028         
23029         this.hide();
23030                         
23031     },
23032     
23033     picker : function()
23034     {
23035         return this.pickerEl;
23036     },
23037     
23038     fillMonths: function()
23039     {    
23040         var i = 0;
23041         var months = this.picker().select('>.datepicker-months td', true).first();
23042         
23043         months.dom.innerHTML = '';
23044         
23045         while (i < 12) {
23046             var month = {
23047                 tag: 'span',
23048                 cls: 'month',
23049                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23050             };
23051             
23052             months.createChild(month);
23053         }
23054         
23055     },
23056     
23057     update: function()
23058     {
23059         var _this = this;
23060         
23061         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23062             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23063         }
23064         
23065         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23066             e.removeClass('active');
23067             
23068             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23069                 e.addClass('active');
23070             }
23071         })
23072     },
23073     
23074     place: function()
23075     {
23076         if(this.isInline) {
23077             return;
23078         }
23079         
23080         this.picker().removeClass(['bottom', 'top']);
23081         
23082         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23083             /*
23084              * place to the top of element!
23085              *
23086              */
23087             
23088             this.picker().addClass('top');
23089             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23090             
23091             return;
23092         }
23093         
23094         this.picker().addClass('bottom');
23095         
23096         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23097     },
23098     
23099     onFocus : function()
23100     {
23101         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23102         this.show();
23103     },
23104     
23105     onBlur : function()
23106     {
23107         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23108         
23109         var d = this.inputEl().getValue();
23110         
23111         this.setValue(d);
23112                 
23113         this.hide();
23114     },
23115     
23116     show : function()
23117     {
23118         this.picker().show();
23119         this.picker().select('>.datepicker-months', true).first().show();
23120         this.update();
23121         this.place();
23122         
23123         this.fireEvent('show', this, this.date);
23124     },
23125     
23126     hide : function()
23127     {
23128         if(this.isInline) {
23129             return;
23130         }
23131         this.picker().hide();
23132         this.fireEvent('hide', this, this.date);
23133         
23134     },
23135     
23136     onMousedown: function(e)
23137     {
23138         e.stopPropagation();
23139         e.preventDefault();
23140     },
23141     
23142     keyup: function(e)
23143     {
23144         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23145         this.update();
23146     },
23147
23148     fireKey: function(e)
23149     {
23150         if (!this.picker().isVisible()){
23151             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23152                 this.show();
23153             }
23154             return;
23155         }
23156         
23157         var dir;
23158         
23159         switch(e.keyCode){
23160             case 27: // escape
23161                 this.hide();
23162                 e.preventDefault();
23163                 break;
23164             case 37: // left
23165             case 39: // right
23166                 dir = e.keyCode == 37 ? -1 : 1;
23167                 
23168                 this.vIndex = this.vIndex + dir;
23169                 
23170                 if(this.vIndex < 0){
23171                     this.vIndex = 0;
23172                 }
23173                 
23174                 if(this.vIndex > 11){
23175                     this.vIndex = 11;
23176                 }
23177                 
23178                 if(isNaN(this.vIndex)){
23179                     this.vIndex = 0;
23180                 }
23181                 
23182                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23183                 
23184                 break;
23185             case 38: // up
23186             case 40: // down
23187                 
23188                 dir = e.keyCode == 38 ? -1 : 1;
23189                 
23190                 this.vIndex = this.vIndex + dir * 4;
23191                 
23192                 if(this.vIndex < 0){
23193                     this.vIndex = 0;
23194                 }
23195                 
23196                 if(this.vIndex > 11){
23197                     this.vIndex = 11;
23198                 }
23199                 
23200                 if(isNaN(this.vIndex)){
23201                     this.vIndex = 0;
23202                 }
23203                 
23204                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23205                 break;
23206                 
23207             case 13: // enter
23208                 
23209                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23210                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23211                 }
23212                 
23213                 this.hide();
23214                 e.preventDefault();
23215                 break;
23216             case 9: // tab
23217                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23218                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23219                 }
23220                 this.hide();
23221                 break;
23222             case 16: // shift
23223             case 17: // ctrl
23224             case 18: // alt
23225                 break;
23226             default :
23227                 this.hide();
23228                 
23229         }
23230     },
23231     
23232     remove: function() 
23233     {
23234         this.picker().remove();
23235     }
23236    
23237 });
23238
23239 Roo.apply(Roo.bootstrap.MonthField,  {
23240     
23241     content : {
23242         tag: 'tbody',
23243         cn: [
23244         {
23245             tag: 'tr',
23246             cn: [
23247             {
23248                 tag: 'td',
23249                 colspan: '7'
23250             }
23251             ]
23252         }
23253         ]
23254     },
23255     
23256     dates:{
23257         en: {
23258             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23259             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23260         }
23261     }
23262 });
23263
23264 Roo.apply(Roo.bootstrap.MonthField,  {
23265   
23266     template : {
23267         tag: 'div',
23268         cls: 'datepicker dropdown-menu roo-dynamic',
23269         cn: [
23270             {
23271                 tag: 'div',
23272                 cls: 'datepicker-months',
23273                 cn: [
23274                 {
23275                     tag: 'table',
23276                     cls: 'table-condensed',
23277                     cn:[
23278                         Roo.bootstrap.DateField.content
23279                     ]
23280                 }
23281                 ]
23282             }
23283         ]
23284     }
23285 });
23286
23287  
23288
23289  
23290  /*
23291  * - LGPL
23292  *
23293  * CheckBox
23294  * 
23295  */
23296
23297 /**
23298  * @class Roo.bootstrap.CheckBox
23299  * @extends Roo.bootstrap.Input
23300  * Bootstrap CheckBox class
23301  * 
23302  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23303  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23304  * @cfg {String} boxLabel The text that appears beside the checkbox
23305  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23306  * @cfg {Boolean} checked initnal the element
23307  * @cfg {Boolean} inline inline the element (default false)
23308  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23309  * @cfg {String} tooltip label tooltip
23310  * 
23311  * @constructor
23312  * Create a new CheckBox
23313  * @param {Object} config The config object
23314  */
23315
23316 Roo.bootstrap.CheckBox = function(config){
23317     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23318    
23319     this.addEvents({
23320         /**
23321         * @event check
23322         * Fires when the element is checked or unchecked.
23323         * @param {Roo.bootstrap.CheckBox} this This input
23324         * @param {Boolean} checked The new checked value
23325         */
23326        check : true,
23327        /**
23328         * @event click
23329         * Fires when the element is click.
23330         * @param {Roo.bootstrap.CheckBox} this This input
23331         */
23332        click : true
23333     });
23334     
23335 };
23336
23337 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23338   
23339     inputType: 'checkbox',
23340     inputValue: 1,
23341     valueOff: 0,
23342     boxLabel: false,
23343     checked: false,
23344     weight : false,
23345     inline: false,
23346     tooltip : '',
23347     
23348     // checkbox success does not make any sense really.. 
23349     invalidClass : "",
23350     validClass : "",
23351     
23352     
23353     getAutoCreate : function()
23354     {
23355         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23356         
23357         var id = Roo.id();
23358         
23359         var cfg = {};
23360         
23361         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23362         
23363         if(this.inline){
23364             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23365         }
23366         
23367         var input =  {
23368             tag: 'input',
23369             id : id,
23370             type : this.inputType,
23371             value : this.inputValue,
23372             cls : 'roo-' + this.inputType, //'form-box',
23373             placeholder : this.placeholder || ''
23374             
23375         };
23376         
23377         if(this.inputType != 'radio'){
23378             var hidden =  {
23379                 tag: 'input',
23380                 type : 'hidden',
23381                 cls : 'roo-hidden-value',
23382                 value : this.checked ? this.inputValue : this.valueOff
23383             };
23384         }
23385         
23386             
23387         if (this.weight) { // Validity check?
23388             cfg.cls += " " + this.inputType + "-" + this.weight;
23389         }
23390         
23391         if (this.disabled) {
23392             input.disabled=true;
23393         }
23394         
23395         if(this.checked){
23396             input.checked = this.checked;
23397         }
23398         
23399         if (this.name) {
23400             
23401             input.name = this.name;
23402             
23403             if(this.inputType != 'radio'){
23404                 hidden.name = this.name;
23405                 input.name = '_hidden_' + this.name;
23406             }
23407         }
23408         
23409         if (this.size) {
23410             input.cls += ' input-' + this.size;
23411         }
23412         
23413         var settings=this;
23414         
23415         ['xs','sm','md','lg'].map(function(size){
23416             if (settings[size]) {
23417                 cfg.cls += ' col-' + size + '-' + settings[size];
23418             }
23419         });
23420         
23421         var inputblock = input;
23422          
23423         if (this.before || this.after) {
23424             
23425             inputblock = {
23426                 cls : 'input-group',
23427                 cn :  [] 
23428             };
23429             
23430             if (this.before) {
23431                 inputblock.cn.push({
23432                     tag :'span',
23433                     cls : 'input-group-addon',
23434                     html : this.before
23435                 });
23436             }
23437             
23438             inputblock.cn.push(input);
23439             
23440             if(this.inputType != 'radio'){
23441                 inputblock.cn.push(hidden);
23442             }
23443             
23444             if (this.after) {
23445                 inputblock.cn.push({
23446                     tag :'span',
23447                     cls : 'input-group-addon',
23448                     html : this.after
23449                 });
23450             }
23451             
23452         }
23453         var boxLabelCfg = false;
23454         
23455         if(this.boxLabel){
23456            
23457             boxLabelCfg = {
23458                 tag: 'label',
23459                 //'for': id, // box label is handled by onclick - so no for...
23460                 cls: 'box-label',
23461                 html: this.boxLabel
23462             };
23463             if(this.tooltip){
23464                 boxLabelCfg.tooltip = this.tooltip;
23465             }
23466              
23467         }
23468         
23469         
23470         if (align ==='left' && this.fieldLabel.length) {
23471 //                Roo.log("left and has label");
23472             cfg.cn = [
23473                 {
23474                     tag: 'label',
23475                     'for' :  id,
23476                     cls : 'control-label',
23477                     html : this.fieldLabel
23478                 },
23479                 {
23480                     cls : "", 
23481                     cn: [
23482                         inputblock
23483                     ]
23484                 }
23485             ];
23486             
23487             if (boxLabelCfg) {
23488                 cfg.cn[1].cn.push(boxLabelCfg);
23489             }
23490             
23491             if(this.labelWidth > 12){
23492                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23493             }
23494             
23495             if(this.labelWidth < 13 && this.labelmd == 0){
23496                 this.labelmd = this.labelWidth;
23497             }
23498             
23499             if(this.labellg > 0){
23500                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23501                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23502             }
23503             
23504             if(this.labelmd > 0){
23505                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23506                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23507             }
23508             
23509             if(this.labelsm > 0){
23510                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23511                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23512             }
23513             
23514             if(this.labelxs > 0){
23515                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23516                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23517             }
23518             
23519         } else if ( this.fieldLabel.length) {
23520 //                Roo.log(" label");
23521                 cfg.cn = [
23522                    
23523                     {
23524                         tag: this.boxLabel ? 'span' : 'label',
23525                         'for': id,
23526                         cls: 'control-label box-input-label',
23527                         //cls : 'input-group-addon',
23528                         html : this.fieldLabel
23529                     },
23530                     
23531                     inputblock
23532                     
23533                 ];
23534                 if (boxLabelCfg) {
23535                     cfg.cn.push(boxLabelCfg);
23536                 }
23537
23538         } else {
23539             
23540 //                Roo.log(" no label && no align");
23541                 cfg.cn = [  inputblock ] ;
23542                 if (boxLabelCfg) {
23543                     cfg.cn.push(boxLabelCfg);
23544                 }
23545
23546                 
23547         }
23548         
23549        
23550         
23551         if(this.inputType != 'radio'){
23552             cfg.cn.push(hidden);
23553         }
23554         
23555         return cfg;
23556         
23557     },
23558     
23559     /**
23560      * return the real input element.
23561      */
23562     inputEl: function ()
23563     {
23564         return this.el.select('input.roo-' + this.inputType,true).first();
23565     },
23566     hiddenEl: function ()
23567     {
23568         return this.el.select('input.roo-hidden-value',true).first();
23569     },
23570     
23571     labelEl: function()
23572     {
23573         return this.el.select('label.control-label',true).first();
23574     },
23575     /* depricated... */
23576     
23577     label: function()
23578     {
23579         return this.labelEl();
23580     },
23581     
23582     boxLabelEl: function()
23583     {
23584         return this.el.select('label.box-label',true).first();
23585     },
23586     
23587     initEvents : function()
23588     {
23589 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23590         
23591         this.inputEl().on('click', this.onClick,  this);
23592         
23593         if (this.boxLabel) { 
23594             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23595         }
23596         
23597         this.startValue = this.getValue();
23598         
23599         if(this.groupId){
23600             Roo.bootstrap.CheckBox.register(this);
23601         }
23602     },
23603     
23604     onClick : function(e)
23605     {   
23606         if(this.fireEvent('click', this, e) !== false){
23607             this.setChecked(!this.checked);
23608         }
23609         
23610     },
23611     
23612     setChecked : function(state,suppressEvent)
23613     {
23614         this.startValue = this.getValue();
23615
23616         if(this.inputType == 'radio'){
23617             
23618             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23619                 e.dom.checked = false;
23620             });
23621             
23622             this.inputEl().dom.checked = true;
23623             
23624             this.inputEl().dom.value = this.inputValue;
23625             
23626             if(suppressEvent !== true){
23627                 this.fireEvent('check', this, true);
23628             }
23629             
23630             this.validate();
23631             
23632             return;
23633         }
23634         
23635         this.checked = state;
23636         
23637         this.inputEl().dom.checked = state;
23638         
23639         
23640         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23641         
23642         if(suppressEvent !== true){
23643             this.fireEvent('check', this, state);
23644         }
23645         
23646         this.validate();
23647     },
23648     
23649     getValue : function()
23650     {
23651         if(this.inputType == 'radio'){
23652             return this.getGroupValue();
23653         }
23654         
23655         return this.hiddenEl().dom.value;
23656         
23657     },
23658     
23659     getGroupValue : function()
23660     {
23661         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23662             return '';
23663         }
23664         
23665         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23666     },
23667     
23668     setValue : function(v,suppressEvent)
23669     {
23670         if(this.inputType == 'radio'){
23671             this.setGroupValue(v, suppressEvent);
23672             return;
23673         }
23674         
23675         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23676         
23677         this.validate();
23678     },
23679     
23680     setGroupValue : function(v, suppressEvent)
23681     {
23682         this.startValue = this.getValue();
23683         
23684         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23685             e.dom.checked = false;
23686             
23687             if(e.dom.value == v){
23688                 e.dom.checked = true;
23689             }
23690         });
23691         
23692         if(suppressEvent !== true){
23693             this.fireEvent('check', this, true);
23694         }
23695
23696         this.validate();
23697         
23698         return;
23699     },
23700     
23701     validate : function()
23702     {
23703         if(this.getVisibilityEl().hasClass('hidden')){
23704             return true;
23705         }
23706         
23707         if(
23708                 this.disabled || 
23709                 (this.inputType == 'radio' && this.validateRadio()) ||
23710                 (this.inputType == 'checkbox' && this.validateCheckbox())
23711         ){
23712             this.markValid();
23713             return true;
23714         }
23715         
23716         this.markInvalid();
23717         return false;
23718     },
23719     
23720     validateRadio : function()
23721     {
23722         if(this.getVisibilityEl().hasClass('hidden')){
23723             return true;
23724         }
23725         
23726         if(this.allowBlank){
23727             return true;
23728         }
23729         
23730         var valid = false;
23731         
23732         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23733             if(!e.dom.checked){
23734                 return;
23735             }
23736             
23737             valid = true;
23738             
23739             return false;
23740         });
23741         
23742         return valid;
23743     },
23744     
23745     validateCheckbox : function()
23746     {
23747         if(!this.groupId){
23748             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23749             //return (this.getValue() == this.inputValue) ? true : false;
23750         }
23751         
23752         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23753         
23754         if(!group){
23755             return false;
23756         }
23757         
23758         var r = false;
23759         
23760         for(var i in group){
23761             if(group[i].el.isVisible(true)){
23762                 r = false;
23763                 break;
23764             }
23765             
23766             r = true;
23767         }
23768         
23769         for(var i in group){
23770             if(r){
23771                 break;
23772             }
23773             
23774             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23775         }
23776         
23777         return r;
23778     },
23779     
23780     /**
23781      * Mark this field as valid
23782      */
23783     markValid : function()
23784     {
23785         var _this = this;
23786         
23787         this.fireEvent('valid', this);
23788         
23789         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23790         
23791         if(this.groupId){
23792             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23793         }
23794         
23795         if(label){
23796             label.markValid();
23797         }
23798
23799         if(this.inputType == 'radio'){
23800             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23801                 var fg = e.findParent('.form-group', false, true);
23802                 if (Roo.bootstrap.version == 3) {
23803                     fg.removeClass([_this.invalidClass, _this.validClass]);
23804                     fg.addClass(_this.validClass);
23805                 } else {
23806                     fg.removeClass(['is-valid', 'is-invalid']);
23807                     fg.addClass('is-valid');
23808                 }
23809             });
23810             
23811             return;
23812         }
23813
23814         if(!this.groupId){
23815             var fg = this.el.findParent('.form-group', false, true);
23816             if (Roo.bootstrap.version == 3) {
23817                 fg.removeClass([this.invalidClass, this.validClass]);
23818                 fg.addClass(this.validClass);
23819             } else {
23820                 fg.removeClass(['is-valid', 'is-invalid']);
23821                 fg.addClass('is-valid');
23822             }
23823             return;
23824         }
23825         
23826         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23827         
23828         if(!group){
23829             return;
23830         }
23831         
23832         for(var i in group){
23833             var fg = group[i].el.findParent('.form-group', false, true);
23834             if (Roo.bootstrap.version == 3) {
23835                 fg.removeClass([this.invalidClass, this.validClass]);
23836                 fg.addClass(this.validClass);
23837             } else {
23838                 fg.removeClass(['is-valid', 'is-invalid']);
23839                 fg.addClass('is-valid');
23840             }
23841         }
23842     },
23843     
23844      /**
23845      * Mark this field as invalid
23846      * @param {String} msg The validation message
23847      */
23848     markInvalid : function(msg)
23849     {
23850         if(this.allowBlank){
23851             return;
23852         }
23853         
23854         var _this = this;
23855         
23856         this.fireEvent('invalid', this, msg);
23857         
23858         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23859         
23860         if(this.groupId){
23861             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23862         }
23863         
23864         if(label){
23865             label.markInvalid();
23866         }
23867             
23868         if(this.inputType == 'radio'){
23869             
23870             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23871                 var fg = e.findParent('.form-group', false, true);
23872                 if (Roo.bootstrap.version == 3) {
23873                     fg.removeClass([_this.invalidClass, _this.validClass]);
23874                     fg.addClass(_this.invalidClass);
23875                 } else {
23876                     fg.removeClass(['is-invalid', 'is-valid']);
23877                     fg.addClass('is-invalid');
23878                 }
23879             });
23880             
23881             return;
23882         }
23883         
23884         if(!this.groupId){
23885             var fg = this.el.findParent('.form-group', false, true);
23886             if (Roo.bootstrap.version == 3) {
23887                 fg.removeClass([_this.invalidClass, _this.validClass]);
23888                 fg.addClass(_this.invalidClass);
23889             } else {
23890                 fg.removeClass(['is-invalid', 'is-valid']);
23891                 fg.addClass('is-invalid');
23892             }
23893             return;
23894         }
23895         
23896         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23897         
23898         if(!group){
23899             return;
23900         }
23901         
23902         for(var i in group){
23903             var fg = group[i].el.findParent('.form-group', false, true);
23904             if (Roo.bootstrap.version == 3) {
23905                 fg.removeClass([_this.invalidClass, _this.validClass]);
23906                 fg.addClass(_this.invalidClass);
23907             } else {
23908                 fg.removeClass(['is-invalid', 'is-valid']);
23909                 fg.addClass('is-invalid');
23910             }
23911         }
23912         
23913     },
23914     
23915     clearInvalid : function()
23916     {
23917         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23918         
23919         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23920         
23921         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23922         
23923         if (label && label.iconEl) {
23924             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23925             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23926         }
23927     },
23928     
23929     disable : function()
23930     {
23931         if(this.inputType != 'radio'){
23932             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23933             return;
23934         }
23935         
23936         var _this = this;
23937         
23938         if(this.rendered){
23939             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23940                 _this.getActionEl().addClass(this.disabledClass);
23941                 e.dom.disabled = true;
23942             });
23943         }
23944         
23945         this.disabled = true;
23946         this.fireEvent("disable", this);
23947         return this;
23948     },
23949
23950     enable : function()
23951     {
23952         if(this.inputType != 'radio'){
23953             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23954             return;
23955         }
23956         
23957         var _this = this;
23958         
23959         if(this.rendered){
23960             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23961                 _this.getActionEl().removeClass(this.disabledClass);
23962                 e.dom.disabled = false;
23963             });
23964         }
23965         
23966         this.disabled = false;
23967         this.fireEvent("enable", this);
23968         return this;
23969     },
23970     
23971     setBoxLabel : function(v)
23972     {
23973         this.boxLabel = v;
23974         
23975         if(this.rendered){
23976             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23977         }
23978     }
23979
23980 });
23981
23982 Roo.apply(Roo.bootstrap.CheckBox, {
23983     
23984     groups: {},
23985     
23986      /**
23987     * register a CheckBox Group
23988     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23989     */
23990     register : function(checkbox)
23991     {
23992         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23993             this.groups[checkbox.groupId] = {};
23994         }
23995         
23996         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23997             return;
23998         }
23999         
24000         this.groups[checkbox.groupId][checkbox.name] = checkbox;
24001         
24002     },
24003     /**
24004     * fetch a CheckBox Group based on the group ID
24005     * @param {string} the group ID
24006     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24007     */
24008     get: function(groupId) {
24009         if (typeof(this.groups[groupId]) == 'undefined') {
24010             return false;
24011         }
24012         
24013         return this.groups[groupId] ;
24014     }
24015     
24016     
24017 });
24018 /*
24019  * - LGPL
24020  *
24021  * RadioItem
24022  * 
24023  */
24024
24025 /**
24026  * @class Roo.bootstrap.Radio
24027  * @extends Roo.bootstrap.Component
24028  * Bootstrap Radio class
24029  * @cfg {String} boxLabel - the label associated
24030  * @cfg {String} value - the value of radio
24031  * 
24032  * @constructor
24033  * Create a new Radio
24034  * @param {Object} config The config object
24035  */
24036 Roo.bootstrap.Radio = function(config){
24037     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24038     
24039 };
24040
24041 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24042     
24043     boxLabel : '',
24044     
24045     value : '',
24046     
24047     getAutoCreate : function()
24048     {
24049         var cfg = {
24050             tag : 'div',
24051             cls : 'form-group radio',
24052             cn : [
24053                 {
24054                     tag : 'label',
24055                     cls : 'box-label',
24056                     html : this.boxLabel
24057                 }
24058             ]
24059         };
24060         
24061         return cfg;
24062     },
24063     
24064     initEvents : function() 
24065     {
24066         this.parent().register(this);
24067         
24068         this.el.on('click', this.onClick, this);
24069         
24070     },
24071     
24072     onClick : function(e)
24073     {
24074         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24075             this.setChecked(true);
24076         }
24077     },
24078     
24079     setChecked : function(state, suppressEvent)
24080     {
24081         this.parent().setValue(this.value, suppressEvent);
24082         
24083     },
24084     
24085     setBoxLabel : function(v)
24086     {
24087         this.boxLabel = v;
24088         
24089         if(this.rendered){
24090             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24091         }
24092     }
24093     
24094 });
24095  
24096
24097  /*
24098  * - LGPL
24099  *
24100  * Input
24101  * 
24102  */
24103
24104 /**
24105  * @class Roo.bootstrap.SecurePass
24106  * @extends Roo.bootstrap.Input
24107  * Bootstrap SecurePass class
24108  *
24109  * 
24110  * @constructor
24111  * Create a new SecurePass
24112  * @param {Object} config The config object
24113  */
24114  
24115 Roo.bootstrap.SecurePass = function (config) {
24116     // these go here, so the translation tool can replace them..
24117     this.errors = {
24118         PwdEmpty: "Please type a password, and then retype it to confirm.",
24119         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24120         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24121         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24122         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24123         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24124         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24125         TooWeak: "Your password is Too Weak."
24126     },
24127     this.meterLabel = "Password strength:";
24128     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24129     this.meterClass = [
24130         "roo-password-meter-tooweak", 
24131         "roo-password-meter-weak", 
24132         "roo-password-meter-medium", 
24133         "roo-password-meter-strong", 
24134         "roo-password-meter-grey"
24135     ];
24136     
24137     this.errors = {};
24138     
24139     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24140 }
24141
24142 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24143     /**
24144      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24145      * {
24146      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24147      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24148      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24149      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24150      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24151      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24152      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24153      * })
24154      */
24155     // private
24156     
24157     meterWidth: 300,
24158     errorMsg :'',    
24159     errors: false,
24160     imageRoot: '/',
24161     /**
24162      * @cfg {String/Object} Label for the strength meter (defaults to
24163      * 'Password strength:')
24164      */
24165     // private
24166     meterLabel: '',
24167     /**
24168      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24169      * ['Weak', 'Medium', 'Strong'])
24170      */
24171     // private    
24172     pwdStrengths: false,    
24173     // private
24174     strength: 0,
24175     // private
24176     _lastPwd: null,
24177     // private
24178     kCapitalLetter: 0,
24179     kSmallLetter: 1,
24180     kDigit: 2,
24181     kPunctuation: 3,
24182     
24183     insecure: false,
24184     // private
24185     initEvents: function ()
24186     {
24187         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24188
24189         if (this.el.is('input[type=password]') && Roo.isSafari) {
24190             this.el.on('keydown', this.SafariOnKeyDown, this);
24191         }
24192
24193         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24194     },
24195     // private
24196     onRender: function (ct, position)
24197     {
24198         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24199         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24200         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24201
24202         this.trigger.createChild({
24203                    cn: [
24204                     {
24205                     //id: 'PwdMeter',
24206                     tag: 'div',
24207                     cls: 'roo-password-meter-grey col-xs-12',
24208                     style: {
24209                         //width: 0,
24210                         //width: this.meterWidth + 'px'                                                
24211                         }
24212                     },
24213                     {                            
24214                          cls: 'roo-password-meter-text'                          
24215                     }
24216                 ]            
24217         });
24218
24219          
24220         if (this.hideTrigger) {
24221             this.trigger.setDisplayed(false);
24222         }
24223         this.setSize(this.width || '', this.height || '');
24224     },
24225     // private
24226     onDestroy: function ()
24227     {
24228         if (this.trigger) {
24229             this.trigger.removeAllListeners();
24230             this.trigger.remove();
24231         }
24232         if (this.wrap) {
24233             this.wrap.remove();
24234         }
24235         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24236     },
24237     // private
24238     checkStrength: function ()
24239     {
24240         var pwd = this.inputEl().getValue();
24241         if (pwd == this._lastPwd) {
24242             return;
24243         }
24244
24245         var strength;
24246         if (this.ClientSideStrongPassword(pwd)) {
24247             strength = 3;
24248         } else if (this.ClientSideMediumPassword(pwd)) {
24249             strength = 2;
24250         } else if (this.ClientSideWeakPassword(pwd)) {
24251             strength = 1;
24252         } else {
24253             strength = 0;
24254         }
24255         
24256         Roo.log('strength1: ' + strength);
24257         
24258         //var pm = this.trigger.child('div/div/div').dom;
24259         var pm = this.trigger.child('div/div');
24260         pm.removeClass(this.meterClass);
24261         pm.addClass(this.meterClass[strength]);
24262                 
24263         
24264         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24265                 
24266         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24267         
24268         this._lastPwd = pwd;
24269     },
24270     reset: function ()
24271     {
24272         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24273         
24274         this._lastPwd = '';
24275         
24276         var pm = this.trigger.child('div/div');
24277         pm.removeClass(this.meterClass);
24278         pm.addClass('roo-password-meter-grey');        
24279         
24280         
24281         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24282         
24283         pt.innerHTML = '';
24284         this.inputEl().dom.type='password';
24285     },
24286     // private
24287     validateValue: function (value)
24288     {
24289         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24290             return false;
24291         }
24292         if (value.length == 0) {
24293             if (this.allowBlank) {
24294                 this.clearInvalid();
24295                 return true;
24296             }
24297
24298             this.markInvalid(this.errors.PwdEmpty);
24299             this.errorMsg = this.errors.PwdEmpty;
24300             return false;
24301         }
24302         
24303         if(this.insecure){
24304             return true;
24305         }
24306         
24307         if (!value.match(/[\x21-\x7e]+/)) {
24308             this.markInvalid(this.errors.PwdBadChar);
24309             this.errorMsg = this.errors.PwdBadChar;
24310             return false;
24311         }
24312         if (value.length < 6) {
24313             this.markInvalid(this.errors.PwdShort);
24314             this.errorMsg = this.errors.PwdShort;
24315             return false;
24316         }
24317         if (value.length > 16) {
24318             this.markInvalid(this.errors.PwdLong);
24319             this.errorMsg = this.errors.PwdLong;
24320             return false;
24321         }
24322         var strength;
24323         if (this.ClientSideStrongPassword(value)) {
24324             strength = 3;
24325         } else if (this.ClientSideMediumPassword(value)) {
24326             strength = 2;
24327         } else if (this.ClientSideWeakPassword(value)) {
24328             strength = 1;
24329         } else {
24330             strength = 0;
24331         }
24332
24333         
24334         if (strength < 2) {
24335             //this.markInvalid(this.errors.TooWeak);
24336             this.errorMsg = this.errors.TooWeak;
24337             //return false;
24338         }
24339         
24340         
24341         console.log('strength2: ' + strength);
24342         
24343         //var pm = this.trigger.child('div/div/div').dom;
24344         
24345         var pm = this.trigger.child('div/div');
24346         pm.removeClass(this.meterClass);
24347         pm.addClass(this.meterClass[strength]);
24348                 
24349         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24350                 
24351         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24352         
24353         this.errorMsg = ''; 
24354         return true;
24355     },
24356     // private
24357     CharacterSetChecks: function (type)
24358     {
24359         this.type = type;
24360         this.fResult = false;
24361     },
24362     // private
24363     isctype: function (character, type)
24364     {
24365         switch (type) {  
24366             case this.kCapitalLetter:
24367                 if (character >= 'A' && character <= 'Z') {
24368                     return true;
24369                 }
24370                 break;
24371             
24372             case this.kSmallLetter:
24373                 if (character >= 'a' && character <= 'z') {
24374                     return true;
24375                 }
24376                 break;
24377             
24378             case this.kDigit:
24379                 if (character >= '0' && character <= '9') {
24380                     return true;
24381                 }
24382                 break;
24383             
24384             case this.kPunctuation:
24385                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24386                     return true;
24387                 }
24388                 break;
24389             
24390             default:
24391                 return false;
24392         }
24393
24394     },
24395     // private
24396     IsLongEnough: function (pwd, size)
24397     {
24398         return !(pwd == null || isNaN(size) || pwd.length < size);
24399     },
24400     // private
24401     SpansEnoughCharacterSets: function (word, nb)
24402     {
24403         if (!this.IsLongEnough(word, nb))
24404         {
24405             return false;
24406         }
24407
24408         var characterSetChecks = new Array(
24409             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24410             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24411         );
24412         
24413         for (var index = 0; index < word.length; ++index) {
24414             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24415                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24416                     characterSetChecks[nCharSet].fResult = true;
24417                     break;
24418                 }
24419             }
24420         }
24421
24422         var nCharSets = 0;
24423         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24424             if (characterSetChecks[nCharSet].fResult) {
24425                 ++nCharSets;
24426             }
24427         }
24428
24429         if (nCharSets < nb) {
24430             return false;
24431         }
24432         return true;
24433     },
24434     // private
24435     ClientSideStrongPassword: function (pwd)
24436     {
24437         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24438     },
24439     // private
24440     ClientSideMediumPassword: function (pwd)
24441     {
24442         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24443     },
24444     // private
24445     ClientSideWeakPassword: function (pwd)
24446     {
24447         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24448     }
24449           
24450 })//<script type="text/javascript">
24451
24452 /*
24453  * Based  Ext JS Library 1.1.1
24454  * Copyright(c) 2006-2007, Ext JS, LLC.
24455  * LGPL
24456  *
24457  */
24458  
24459 /**
24460  * @class Roo.HtmlEditorCore
24461  * @extends Roo.Component
24462  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24463  *
24464  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24465  */
24466
24467 Roo.HtmlEditorCore = function(config){
24468     
24469     
24470     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24471     
24472     
24473     this.addEvents({
24474         /**
24475          * @event initialize
24476          * Fires when the editor is fully initialized (including the iframe)
24477          * @param {Roo.HtmlEditorCore} this
24478          */
24479         initialize: true,
24480         /**
24481          * @event activate
24482          * Fires when the editor is first receives the focus. Any insertion must wait
24483          * until after this event.
24484          * @param {Roo.HtmlEditorCore} this
24485          */
24486         activate: true,
24487          /**
24488          * @event beforesync
24489          * Fires before the textarea is updated with content from the editor iframe. Return false
24490          * to cancel the sync.
24491          * @param {Roo.HtmlEditorCore} this
24492          * @param {String} html
24493          */
24494         beforesync: true,
24495          /**
24496          * @event beforepush
24497          * Fires before the iframe editor is updated with content from the textarea. Return false
24498          * to cancel the push.
24499          * @param {Roo.HtmlEditorCore} this
24500          * @param {String} html
24501          */
24502         beforepush: true,
24503          /**
24504          * @event sync
24505          * Fires when the textarea is updated with content from the editor iframe.
24506          * @param {Roo.HtmlEditorCore} this
24507          * @param {String} html
24508          */
24509         sync: true,
24510          /**
24511          * @event push
24512          * Fires when the iframe editor is updated with content from the textarea.
24513          * @param {Roo.HtmlEditorCore} this
24514          * @param {String} html
24515          */
24516         push: true,
24517         
24518         /**
24519          * @event editorevent
24520          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24521          * @param {Roo.HtmlEditorCore} this
24522          */
24523         editorevent: true
24524         
24525     });
24526     
24527     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24528     
24529     // defaults : white / black...
24530     this.applyBlacklists();
24531     
24532     
24533     
24534 };
24535
24536
24537 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24538
24539
24540      /**
24541      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24542      */
24543     
24544     owner : false,
24545     
24546      /**
24547      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24548      *                        Roo.resizable.
24549      */
24550     resizable : false,
24551      /**
24552      * @cfg {Number} height (in pixels)
24553      */   
24554     height: 300,
24555    /**
24556      * @cfg {Number} width (in pixels)
24557      */   
24558     width: 500,
24559     
24560     /**
24561      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24562      * 
24563      */
24564     stylesheets: false,
24565     
24566     // id of frame..
24567     frameId: false,
24568     
24569     // private properties
24570     validationEvent : false,
24571     deferHeight: true,
24572     initialized : false,
24573     activated : false,
24574     sourceEditMode : false,
24575     onFocus : Roo.emptyFn,
24576     iframePad:3,
24577     hideMode:'offsets',
24578     
24579     clearUp: true,
24580     
24581     // blacklist + whitelisted elements..
24582     black: false,
24583     white: false,
24584      
24585     bodyCls : '',
24586
24587     /**
24588      * Protected method that will not generally be called directly. It
24589      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24590      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24591      */
24592     getDocMarkup : function(){
24593         // body styles..
24594         var st = '';
24595         
24596         // inherit styels from page...?? 
24597         if (this.stylesheets === false) {
24598             
24599             Roo.get(document.head).select('style').each(function(node) {
24600                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24601             });
24602             
24603             Roo.get(document.head).select('link').each(function(node) { 
24604                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24605             });
24606             
24607         } else if (!this.stylesheets.length) {
24608                 // simple..
24609                 st = '<style type="text/css">' +
24610                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24611                    '</style>';
24612         } else {
24613             for (var i in this.stylesheets) { 
24614                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24615             }
24616             
24617         }
24618         
24619         st +=  '<style type="text/css">' +
24620             'IMG { cursor: pointer } ' +
24621         '</style>';
24622
24623         var cls = 'roo-htmleditor-body';
24624         
24625         if(this.bodyCls.length){
24626             cls += ' ' + this.bodyCls;
24627         }
24628         
24629         return '<html><head>' + st  +
24630             //<style type="text/css">' +
24631             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24632             //'</style>' +
24633             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24634     },
24635
24636     // private
24637     onRender : function(ct, position)
24638     {
24639         var _t = this;
24640         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24641         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24642         
24643         
24644         this.el.dom.style.border = '0 none';
24645         this.el.dom.setAttribute('tabIndex', -1);
24646         this.el.addClass('x-hidden hide');
24647         
24648         
24649         
24650         if(Roo.isIE){ // fix IE 1px bogus margin
24651             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24652         }
24653        
24654         
24655         this.frameId = Roo.id();
24656         
24657          
24658         
24659         var iframe = this.owner.wrap.createChild({
24660             tag: 'iframe',
24661             cls: 'form-control', // bootstrap..
24662             id: this.frameId,
24663             name: this.frameId,
24664             frameBorder : 'no',
24665             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24666         }, this.el
24667         );
24668         
24669         
24670         this.iframe = iframe.dom;
24671
24672          this.assignDocWin();
24673         
24674         this.doc.designMode = 'on';
24675        
24676         this.doc.open();
24677         this.doc.write(this.getDocMarkup());
24678         this.doc.close();
24679
24680         
24681         var task = { // must defer to wait for browser to be ready
24682             run : function(){
24683                 //console.log("run task?" + this.doc.readyState);
24684                 this.assignDocWin();
24685                 if(this.doc.body || this.doc.readyState == 'complete'){
24686                     try {
24687                         this.doc.designMode="on";
24688                     } catch (e) {
24689                         return;
24690                     }
24691                     Roo.TaskMgr.stop(task);
24692                     this.initEditor.defer(10, this);
24693                 }
24694             },
24695             interval : 10,
24696             duration: 10000,
24697             scope: this
24698         };
24699         Roo.TaskMgr.start(task);
24700
24701     },
24702
24703     // private
24704     onResize : function(w, h)
24705     {
24706          Roo.log('resize: ' +w + ',' + h );
24707         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24708         if(!this.iframe){
24709             return;
24710         }
24711         if(typeof w == 'number'){
24712             
24713             this.iframe.style.width = w + 'px';
24714         }
24715         if(typeof h == 'number'){
24716             
24717             this.iframe.style.height = h + 'px';
24718             if(this.doc){
24719                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24720             }
24721         }
24722         
24723     },
24724
24725     /**
24726      * Toggles the editor between standard and source edit mode.
24727      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24728      */
24729     toggleSourceEdit : function(sourceEditMode){
24730         
24731         this.sourceEditMode = sourceEditMode === true;
24732         
24733         if(this.sourceEditMode){
24734  
24735             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24736             
24737         }else{
24738             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24739             //this.iframe.className = '';
24740             this.deferFocus();
24741         }
24742         //this.setSize(this.owner.wrap.getSize());
24743         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24744     },
24745
24746     
24747   
24748
24749     /**
24750      * Protected method that will not generally be called directly. If you need/want
24751      * custom HTML cleanup, this is the method you should override.
24752      * @param {String} html The HTML to be cleaned
24753      * return {String} The cleaned HTML
24754      */
24755     cleanHtml : function(html){
24756         html = String(html);
24757         if(html.length > 5){
24758             if(Roo.isSafari){ // strip safari nonsense
24759                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24760             }
24761         }
24762         if(html == '&nbsp;'){
24763             html = '';
24764         }
24765         return html;
24766     },
24767
24768     /**
24769      * HTML Editor -> Textarea
24770      * Protected method that will not generally be called directly. Syncs the contents
24771      * of the editor iframe with the textarea.
24772      */
24773     syncValue : function(){
24774         if(this.initialized){
24775             var bd = (this.doc.body || this.doc.documentElement);
24776             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24777             var html = bd.innerHTML;
24778             if(Roo.isSafari){
24779                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24780                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24781                 if(m && m[1]){
24782                     html = '<div style="'+m[0]+'">' + html + '</div>';
24783                 }
24784             }
24785             html = this.cleanHtml(html);
24786             // fix up the special chars.. normaly like back quotes in word...
24787             // however we do not want to do this with chinese..
24788             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24789                 
24790                 var cc = match.charCodeAt();
24791
24792                 // Get the character value, handling surrogate pairs
24793                 if (match.length == 2) {
24794                     // It's a surrogate pair, calculate the Unicode code point
24795                     var high = match.charCodeAt(0) - 0xD800;
24796                     var low  = match.charCodeAt(1) - 0xDC00;
24797                     cc = (high * 0x400) + low + 0x10000;
24798                 }  else if (
24799                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24800                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24801                     (cc >= 0xf900 && cc < 0xfb00 )
24802                 ) {
24803                         return match;
24804                 }  
24805          
24806                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24807                 return "&#" + cc + ";";
24808                 
24809                 
24810             });
24811             
24812             
24813              
24814             if(this.owner.fireEvent('beforesync', this, html) !== false){
24815                 this.el.dom.value = html;
24816                 this.owner.fireEvent('sync', this, html);
24817             }
24818         }
24819     },
24820
24821     /**
24822      * Protected method that will not generally be called directly. Pushes the value of the textarea
24823      * into the iframe editor.
24824      */
24825     pushValue : function(){
24826         if(this.initialized){
24827             var v = this.el.dom.value.trim();
24828             
24829 //            if(v.length < 1){
24830 //                v = '&#160;';
24831 //            }
24832             
24833             if(this.owner.fireEvent('beforepush', this, v) !== false){
24834                 var d = (this.doc.body || this.doc.documentElement);
24835                 d.innerHTML = v;
24836                 this.cleanUpPaste();
24837                 this.el.dom.value = d.innerHTML;
24838                 this.owner.fireEvent('push', this, v);
24839             }
24840         }
24841     },
24842
24843     // private
24844     deferFocus : function(){
24845         this.focus.defer(10, this);
24846     },
24847
24848     // doc'ed in Field
24849     focus : function(){
24850         if(this.win && !this.sourceEditMode){
24851             this.win.focus();
24852         }else{
24853             this.el.focus();
24854         }
24855     },
24856     
24857     assignDocWin: function()
24858     {
24859         var iframe = this.iframe;
24860         
24861          if(Roo.isIE){
24862             this.doc = iframe.contentWindow.document;
24863             this.win = iframe.contentWindow;
24864         } else {
24865 //            if (!Roo.get(this.frameId)) {
24866 //                return;
24867 //            }
24868 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24869 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24870             
24871             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24872                 return;
24873             }
24874             
24875             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24876             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24877         }
24878     },
24879     
24880     // private
24881     initEditor : function(){
24882         //console.log("INIT EDITOR");
24883         this.assignDocWin();
24884         
24885         
24886         
24887         this.doc.designMode="on";
24888         this.doc.open();
24889         this.doc.write(this.getDocMarkup());
24890         this.doc.close();
24891         
24892         var dbody = (this.doc.body || this.doc.documentElement);
24893         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24894         // this copies styles from the containing element into thsi one..
24895         // not sure why we need all of this..
24896         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24897         
24898         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24899         //ss['background-attachment'] = 'fixed'; // w3c
24900         dbody.bgProperties = 'fixed'; // ie
24901         //Roo.DomHelper.applyStyles(dbody, ss);
24902         Roo.EventManager.on(this.doc, {
24903             //'mousedown': this.onEditorEvent,
24904             'mouseup': this.onEditorEvent,
24905             'dblclick': this.onEditorEvent,
24906             'click': this.onEditorEvent,
24907             'keyup': this.onEditorEvent,
24908             buffer:100,
24909             scope: this
24910         });
24911         if(Roo.isGecko){
24912             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24913         }
24914         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24915             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24916         }
24917         this.initialized = true;
24918
24919         this.owner.fireEvent('initialize', this);
24920         this.pushValue();
24921     },
24922
24923     // private
24924     onDestroy : function(){
24925         
24926         
24927         
24928         if(this.rendered){
24929             
24930             //for (var i =0; i < this.toolbars.length;i++) {
24931             //    // fixme - ask toolbars for heights?
24932             //    this.toolbars[i].onDestroy();
24933            // }
24934             
24935             //this.wrap.dom.innerHTML = '';
24936             //this.wrap.remove();
24937         }
24938     },
24939
24940     // private
24941     onFirstFocus : function(){
24942         
24943         this.assignDocWin();
24944         
24945         
24946         this.activated = true;
24947          
24948     
24949         if(Roo.isGecko){ // prevent silly gecko errors
24950             this.win.focus();
24951             var s = this.win.getSelection();
24952             if(!s.focusNode || s.focusNode.nodeType != 3){
24953                 var r = s.getRangeAt(0);
24954                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24955                 r.collapse(true);
24956                 this.deferFocus();
24957             }
24958             try{
24959                 this.execCmd('useCSS', true);
24960                 this.execCmd('styleWithCSS', false);
24961             }catch(e){}
24962         }
24963         this.owner.fireEvent('activate', this);
24964     },
24965
24966     // private
24967     adjustFont: function(btn){
24968         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24969         //if(Roo.isSafari){ // safari
24970         //    adjust *= 2;
24971        // }
24972         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24973         if(Roo.isSafari){ // safari
24974             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24975             v =  (v < 10) ? 10 : v;
24976             v =  (v > 48) ? 48 : v;
24977             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24978             
24979         }
24980         
24981         
24982         v = Math.max(1, v+adjust);
24983         
24984         this.execCmd('FontSize', v  );
24985     },
24986
24987     onEditorEvent : function(e)
24988     {
24989         this.owner.fireEvent('editorevent', this, e);
24990       //  this.updateToolbar();
24991         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24992     },
24993
24994     insertTag : function(tg)
24995     {
24996         // could be a bit smarter... -> wrap the current selected tRoo..
24997         if (tg.toLowerCase() == 'span' ||
24998             tg.toLowerCase() == 'code' ||
24999             tg.toLowerCase() == 'sup' ||
25000             tg.toLowerCase() == 'sub' 
25001             ) {
25002             
25003             range = this.createRange(this.getSelection());
25004             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25005             wrappingNode.appendChild(range.extractContents());
25006             range.insertNode(wrappingNode);
25007
25008             return;
25009             
25010             
25011             
25012         }
25013         this.execCmd("formatblock",   tg);
25014         
25015     },
25016     
25017     insertText : function(txt)
25018     {
25019         
25020         
25021         var range = this.createRange();
25022         range.deleteContents();
25023                //alert(Sender.getAttribute('label'));
25024                
25025         range.insertNode(this.doc.createTextNode(txt));
25026     } ,
25027     
25028      
25029
25030     /**
25031      * Executes a Midas editor command on the editor document and performs necessary focus and
25032      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25033      * @param {String} cmd The Midas command
25034      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25035      */
25036     relayCmd : function(cmd, value){
25037         this.win.focus();
25038         this.execCmd(cmd, value);
25039         this.owner.fireEvent('editorevent', this);
25040         //this.updateToolbar();
25041         this.owner.deferFocus();
25042     },
25043
25044     /**
25045      * Executes a Midas editor command directly on the editor document.
25046      * For visual commands, you should use {@link #relayCmd} instead.
25047      * <b>This should only be called after the editor is initialized.</b>
25048      * @param {String} cmd The Midas command
25049      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25050      */
25051     execCmd : function(cmd, value){
25052         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25053         this.syncValue();
25054     },
25055  
25056  
25057    
25058     /**
25059      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25060      * to insert tRoo.
25061      * @param {String} text | dom node.. 
25062      */
25063     insertAtCursor : function(text)
25064     {
25065         
25066         if(!this.activated){
25067             return;
25068         }
25069         /*
25070         if(Roo.isIE){
25071             this.win.focus();
25072             var r = this.doc.selection.createRange();
25073             if(r){
25074                 r.collapse(true);
25075                 r.pasteHTML(text);
25076                 this.syncValue();
25077                 this.deferFocus();
25078             
25079             }
25080             return;
25081         }
25082         */
25083         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25084             this.win.focus();
25085             
25086             
25087             // from jquery ui (MIT licenced)
25088             var range, node;
25089             var win = this.win;
25090             
25091             if (win.getSelection && win.getSelection().getRangeAt) {
25092                 range = win.getSelection().getRangeAt(0);
25093                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25094                 range.insertNode(node);
25095             } else if (win.document.selection && win.document.selection.createRange) {
25096                 // no firefox support
25097                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25098                 win.document.selection.createRange().pasteHTML(txt);
25099             } else {
25100                 // no firefox support
25101                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25102                 this.execCmd('InsertHTML', txt);
25103             } 
25104             
25105             this.syncValue();
25106             
25107             this.deferFocus();
25108         }
25109     },
25110  // private
25111     mozKeyPress : function(e){
25112         if(e.ctrlKey){
25113             var c = e.getCharCode(), cmd;
25114           
25115             if(c > 0){
25116                 c = String.fromCharCode(c).toLowerCase();
25117                 switch(c){
25118                     case 'b':
25119                         cmd = 'bold';
25120                         break;
25121                     case 'i':
25122                         cmd = 'italic';
25123                         break;
25124                     
25125                     case 'u':
25126                         cmd = 'underline';
25127                         break;
25128                     
25129                     case 'v':
25130                         this.cleanUpPaste.defer(100, this);
25131                         return;
25132                         
25133                 }
25134                 if(cmd){
25135                     this.win.focus();
25136                     this.execCmd(cmd);
25137                     this.deferFocus();
25138                     e.preventDefault();
25139                 }
25140                 
25141             }
25142         }
25143     },
25144
25145     // private
25146     fixKeys : function(){ // load time branching for fastest keydown performance
25147         if(Roo.isIE){
25148             return function(e){
25149                 var k = e.getKey(), r;
25150                 if(k == e.TAB){
25151                     e.stopEvent();
25152                     r = this.doc.selection.createRange();
25153                     if(r){
25154                         r.collapse(true);
25155                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25156                         this.deferFocus();
25157                     }
25158                     return;
25159                 }
25160                 
25161                 if(k == e.ENTER){
25162                     r = this.doc.selection.createRange();
25163                     if(r){
25164                         var target = r.parentElement();
25165                         if(!target || target.tagName.toLowerCase() != 'li'){
25166                             e.stopEvent();
25167                             r.pasteHTML('<br />');
25168                             r.collapse(false);
25169                             r.select();
25170                         }
25171                     }
25172                 }
25173                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25174                     this.cleanUpPaste.defer(100, this);
25175                     return;
25176                 }
25177                 
25178                 
25179             };
25180         }else if(Roo.isOpera){
25181             return function(e){
25182                 var k = e.getKey();
25183                 if(k == e.TAB){
25184                     e.stopEvent();
25185                     this.win.focus();
25186                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25187                     this.deferFocus();
25188                 }
25189                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25190                     this.cleanUpPaste.defer(100, this);
25191                     return;
25192                 }
25193                 
25194             };
25195         }else if(Roo.isSafari){
25196             return function(e){
25197                 var k = e.getKey();
25198                 
25199                 if(k == e.TAB){
25200                     e.stopEvent();
25201                     this.execCmd('InsertText','\t');
25202                     this.deferFocus();
25203                     return;
25204                 }
25205                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25206                     this.cleanUpPaste.defer(100, this);
25207                     return;
25208                 }
25209                 
25210              };
25211         }
25212     }(),
25213     
25214     getAllAncestors: function()
25215     {
25216         var p = this.getSelectedNode();
25217         var a = [];
25218         if (!p) {
25219             a.push(p); // push blank onto stack..
25220             p = this.getParentElement();
25221         }
25222         
25223         
25224         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25225             a.push(p);
25226             p = p.parentNode;
25227         }
25228         a.push(this.doc.body);
25229         return a;
25230     },
25231     lastSel : false,
25232     lastSelNode : false,
25233     
25234     
25235     getSelection : function() 
25236     {
25237         this.assignDocWin();
25238         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25239     },
25240     
25241     getSelectedNode: function() 
25242     {
25243         // this may only work on Gecko!!!
25244         
25245         // should we cache this!!!!
25246         
25247         
25248         
25249          
25250         var range = this.createRange(this.getSelection()).cloneRange();
25251         
25252         if (Roo.isIE) {
25253             var parent = range.parentElement();
25254             while (true) {
25255                 var testRange = range.duplicate();
25256                 testRange.moveToElementText(parent);
25257                 if (testRange.inRange(range)) {
25258                     break;
25259                 }
25260                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25261                     break;
25262                 }
25263                 parent = parent.parentElement;
25264             }
25265             return parent;
25266         }
25267         
25268         // is ancestor a text element.
25269         var ac =  range.commonAncestorContainer;
25270         if (ac.nodeType == 3) {
25271             ac = ac.parentNode;
25272         }
25273         
25274         var ar = ac.childNodes;
25275          
25276         var nodes = [];
25277         var other_nodes = [];
25278         var has_other_nodes = false;
25279         for (var i=0;i<ar.length;i++) {
25280             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25281                 continue;
25282             }
25283             // fullly contained node.
25284             
25285             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25286                 nodes.push(ar[i]);
25287                 continue;
25288             }
25289             
25290             // probably selected..
25291             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25292                 other_nodes.push(ar[i]);
25293                 continue;
25294             }
25295             // outer..
25296             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25297                 continue;
25298             }
25299             
25300             
25301             has_other_nodes = true;
25302         }
25303         if (!nodes.length && other_nodes.length) {
25304             nodes= other_nodes;
25305         }
25306         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25307             return false;
25308         }
25309         
25310         return nodes[0];
25311     },
25312     createRange: function(sel)
25313     {
25314         // this has strange effects when using with 
25315         // top toolbar - not sure if it's a great idea.
25316         //this.editor.contentWindow.focus();
25317         if (typeof sel != "undefined") {
25318             try {
25319                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25320             } catch(e) {
25321                 return this.doc.createRange();
25322             }
25323         } else {
25324             return this.doc.createRange();
25325         }
25326     },
25327     getParentElement: function()
25328     {
25329         
25330         this.assignDocWin();
25331         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25332         
25333         var range = this.createRange(sel);
25334          
25335         try {
25336             var p = range.commonAncestorContainer;
25337             while (p.nodeType == 3) { // text node
25338                 p = p.parentNode;
25339             }
25340             return p;
25341         } catch (e) {
25342             return null;
25343         }
25344     
25345     },
25346     /***
25347      *
25348      * Range intersection.. the hard stuff...
25349      *  '-1' = before
25350      *  '0' = hits..
25351      *  '1' = after.
25352      *         [ -- selected range --- ]
25353      *   [fail]                        [fail]
25354      *
25355      *    basically..
25356      *      if end is before start or  hits it. fail.
25357      *      if start is after end or hits it fail.
25358      *
25359      *   if either hits (but other is outside. - then it's not 
25360      *   
25361      *    
25362      **/
25363     
25364     
25365     // @see http://www.thismuchiknow.co.uk/?p=64.
25366     rangeIntersectsNode : function(range, node)
25367     {
25368         var nodeRange = node.ownerDocument.createRange();
25369         try {
25370             nodeRange.selectNode(node);
25371         } catch (e) {
25372             nodeRange.selectNodeContents(node);
25373         }
25374     
25375         var rangeStartRange = range.cloneRange();
25376         rangeStartRange.collapse(true);
25377     
25378         var rangeEndRange = range.cloneRange();
25379         rangeEndRange.collapse(false);
25380     
25381         var nodeStartRange = nodeRange.cloneRange();
25382         nodeStartRange.collapse(true);
25383     
25384         var nodeEndRange = nodeRange.cloneRange();
25385         nodeEndRange.collapse(false);
25386     
25387         return rangeStartRange.compareBoundaryPoints(
25388                  Range.START_TO_START, nodeEndRange) == -1 &&
25389                rangeEndRange.compareBoundaryPoints(
25390                  Range.START_TO_START, nodeStartRange) == 1;
25391         
25392          
25393     },
25394     rangeCompareNode : function(range, node)
25395     {
25396         var nodeRange = node.ownerDocument.createRange();
25397         try {
25398             nodeRange.selectNode(node);
25399         } catch (e) {
25400             nodeRange.selectNodeContents(node);
25401         }
25402         
25403         
25404         range.collapse(true);
25405     
25406         nodeRange.collapse(true);
25407      
25408         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25409         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25410          
25411         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25412         
25413         var nodeIsBefore   =  ss == 1;
25414         var nodeIsAfter    = ee == -1;
25415         
25416         if (nodeIsBefore && nodeIsAfter) {
25417             return 0; // outer
25418         }
25419         if (!nodeIsBefore && nodeIsAfter) {
25420             return 1; //right trailed.
25421         }
25422         
25423         if (nodeIsBefore && !nodeIsAfter) {
25424             return 2;  // left trailed.
25425         }
25426         // fully contined.
25427         return 3;
25428     },
25429
25430     // private? - in a new class?
25431     cleanUpPaste :  function()
25432     {
25433         // cleans up the whole document..
25434         Roo.log('cleanuppaste');
25435         
25436         this.cleanUpChildren(this.doc.body);
25437         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25438         if (clean != this.doc.body.innerHTML) {
25439             this.doc.body.innerHTML = clean;
25440         }
25441         
25442     },
25443     
25444     cleanWordChars : function(input) {// change the chars to hex code
25445         var he = Roo.HtmlEditorCore;
25446         
25447         var output = input;
25448         Roo.each(he.swapCodes, function(sw) { 
25449             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25450             
25451             output = output.replace(swapper, sw[1]);
25452         });
25453         
25454         return output;
25455     },
25456     
25457     
25458     cleanUpChildren : function (n)
25459     {
25460         if (!n.childNodes.length) {
25461             return;
25462         }
25463         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25464            this.cleanUpChild(n.childNodes[i]);
25465         }
25466     },
25467     
25468     
25469         
25470     
25471     cleanUpChild : function (node)
25472     {
25473         var ed = this;
25474         //console.log(node);
25475         if (node.nodeName == "#text") {
25476             // clean up silly Windows -- stuff?
25477             return; 
25478         }
25479         if (node.nodeName == "#comment") {
25480             node.parentNode.removeChild(node);
25481             // clean up silly Windows -- stuff?
25482             return; 
25483         }
25484         var lcname = node.tagName.toLowerCase();
25485         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25486         // whitelist of tags..
25487         
25488         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25489             // remove node.
25490             node.parentNode.removeChild(node);
25491             return;
25492             
25493         }
25494         
25495         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25496         
25497         // spans with no attributes - just remove them..
25498         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25499             remove_keep_children = true;
25500         }
25501         
25502         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25503         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25504         
25505         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25506         //    remove_keep_children = true;
25507         //}
25508         
25509         if (remove_keep_children) {
25510             this.cleanUpChildren(node);
25511             // inserts everything just before this node...
25512             while (node.childNodes.length) {
25513                 var cn = node.childNodes[0];
25514                 node.removeChild(cn);
25515                 node.parentNode.insertBefore(cn, node);
25516             }
25517             node.parentNode.removeChild(node);
25518             return;
25519         }
25520         
25521         if (!node.attributes || !node.attributes.length) {
25522             
25523           
25524             
25525             
25526             this.cleanUpChildren(node);
25527             return;
25528         }
25529         
25530         function cleanAttr(n,v)
25531         {
25532             
25533             if (v.match(/^\./) || v.match(/^\//)) {
25534                 return;
25535             }
25536             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25537                 return;
25538             }
25539             if (v.match(/^#/)) {
25540                 return;
25541             }
25542             if (v.match(/^\{/)) { // allow template editing.
25543                 return;
25544             }
25545 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25546             node.removeAttribute(n);
25547             
25548         }
25549         
25550         var cwhite = this.cwhite;
25551         var cblack = this.cblack;
25552             
25553         function cleanStyle(n,v)
25554         {
25555             if (v.match(/expression/)) { //XSS?? should we even bother..
25556                 node.removeAttribute(n);
25557                 return;
25558             }
25559             
25560             var parts = v.split(/;/);
25561             var clean = [];
25562             
25563             Roo.each(parts, function(p) {
25564                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25565                 if (!p.length) {
25566                     return true;
25567                 }
25568                 var l = p.split(':').shift().replace(/\s+/g,'');
25569                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25570                 
25571                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25572 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25573                     //node.removeAttribute(n);
25574                     return true;
25575                 }
25576                 //Roo.log()
25577                 // only allow 'c whitelisted system attributes'
25578                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25579 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25580                     //node.removeAttribute(n);
25581                     return true;
25582                 }
25583                 
25584                 
25585                  
25586                 
25587                 clean.push(p);
25588                 return true;
25589             });
25590             if (clean.length) { 
25591                 node.setAttribute(n, clean.join(';'));
25592             } else {
25593                 node.removeAttribute(n);
25594             }
25595             
25596         }
25597         
25598         
25599         for (var i = node.attributes.length-1; i > -1 ; i--) {
25600             var a = node.attributes[i];
25601             //console.log(a);
25602             
25603             if (a.name.toLowerCase().substr(0,2)=='on')  {
25604                 node.removeAttribute(a.name);
25605                 continue;
25606             }
25607             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25608                 node.removeAttribute(a.name);
25609                 continue;
25610             }
25611             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25612                 cleanAttr(a.name,a.value); // fixme..
25613                 continue;
25614             }
25615             if (a.name == 'style') {
25616                 cleanStyle(a.name,a.value);
25617                 continue;
25618             }
25619             /// clean up MS crap..
25620             // tecnically this should be a list of valid class'es..
25621             
25622             
25623             if (a.name == 'class') {
25624                 if (a.value.match(/^Mso/)) {
25625                     node.removeAttribute('class');
25626                 }
25627                 
25628                 if (a.value.match(/^body$/)) {
25629                     node.removeAttribute('class');
25630                 }
25631                 continue;
25632             }
25633             
25634             // style cleanup!?
25635             // class cleanup?
25636             
25637         }
25638         
25639         
25640         this.cleanUpChildren(node);
25641         
25642         
25643     },
25644     
25645     /**
25646      * Clean up MS wordisms...
25647      */
25648     cleanWord : function(node)
25649     {
25650         if (!node) {
25651             this.cleanWord(this.doc.body);
25652             return;
25653         }
25654         
25655         if(
25656                 node.nodeName == 'SPAN' &&
25657                 !node.hasAttributes() &&
25658                 node.childNodes.length == 1 &&
25659                 node.firstChild.nodeName == "#text"  
25660         ) {
25661             var textNode = node.firstChild;
25662             node.removeChild(textNode);
25663             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25664                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25665             }
25666             node.parentNode.insertBefore(textNode, node);
25667             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25668                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25669             }
25670             node.parentNode.removeChild(node);
25671         }
25672         
25673         if (node.nodeName == "#text") {
25674             // clean up silly Windows -- stuff?
25675             return; 
25676         }
25677         if (node.nodeName == "#comment") {
25678             node.parentNode.removeChild(node);
25679             // clean up silly Windows -- stuff?
25680             return; 
25681         }
25682         
25683         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25684             node.parentNode.removeChild(node);
25685             return;
25686         }
25687         //Roo.log(node.tagName);
25688         // remove - but keep children..
25689         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25690             //Roo.log('-- removed');
25691             while (node.childNodes.length) {
25692                 var cn = node.childNodes[0];
25693                 node.removeChild(cn);
25694                 node.parentNode.insertBefore(cn, node);
25695                 // move node to parent - and clean it..
25696                 this.cleanWord(cn);
25697             }
25698             node.parentNode.removeChild(node);
25699             /// no need to iterate chidlren = it's got none..
25700             //this.iterateChildren(node, this.cleanWord);
25701             return;
25702         }
25703         // clean styles
25704         if (node.className.length) {
25705             
25706             var cn = node.className.split(/\W+/);
25707             var cna = [];
25708             Roo.each(cn, function(cls) {
25709                 if (cls.match(/Mso[a-zA-Z]+/)) {
25710                     return;
25711                 }
25712                 cna.push(cls);
25713             });
25714             node.className = cna.length ? cna.join(' ') : '';
25715             if (!cna.length) {
25716                 node.removeAttribute("class");
25717             }
25718         }
25719         
25720         if (node.hasAttribute("lang")) {
25721             node.removeAttribute("lang");
25722         }
25723         
25724         if (node.hasAttribute("style")) {
25725             
25726             var styles = node.getAttribute("style").split(";");
25727             var nstyle = [];
25728             Roo.each(styles, function(s) {
25729                 if (!s.match(/:/)) {
25730                     return;
25731                 }
25732                 var kv = s.split(":");
25733                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25734                     return;
25735                 }
25736                 // what ever is left... we allow.
25737                 nstyle.push(s);
25738             });
25739             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25740             if (!nstyle.length) {
25741                 node.removeAttribute('style');
25742             }
25743         }
25744         this.iterateChildren(node, this.cleanWord);
25745         
25746         
25747         
25748     },
25749     /**
25750      * iterateChildren of a Node, calling fn each time, using this as the scole..
25751      * @param {DomNode} node node to iterate children of.
25752      * @param {Function} fn method of this class to call on each item.
25753      */
25754     iterateChildren : function(node, fn)
25755     {
25756         if (!node.childNodes.length) {
25757                 return;
25758         }
25759         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25760            fn.call(this, node.childNodes[i])
25761         }
25762     },
25763     
25764     
25765     /**
25766      * cleanTableWidths.
25767      *
25768      * Quite often pasting from word etc.. results in tables with column and widths.
25769      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25770      *
25771      */
25772     cleanTableWidths : function(node)
25773     {
25774          
25775          
25776         if (!node) {
25777             this.cleanTableWidths(this.doc.body);
25778             return;
25779         }
25780         
25781         // ignore list...
25782         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25783             return; 
25784         }
25785         Roo.log(node.tagName);
25786         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25787             this.iterateChildren(node, this.cleanTableWidths);
25788             return;
25789         }
25790         if (node.hasAttribute('width')) {
25791             node.removeAttribute('width');
25792         }
25793         
25794          
25795         if (node.hasAttribute("style")) {
25796             // pretty basic...
25797             
25798             var styles = node.getAttribute("style").split(";");
25799             var nstyle = [];
25800             Roo.each(styles, function(s) {
25801                 if (!s.match(/:/)) {
25802                     return;
25803                 }
25804                 var kv = s.split(":");
25805                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25806                     return;
25807                 }
25808                 // what ever is left... we allow.
25809                 nstyle.push(s);
25810             });
25811             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25812             if (!nstyle.length) {
25813                 node.removeAttribute('style');
25814             }
25815         }
25816         
25817         this.iterateChildren(node, this.cleanTableWidths);
25818         
25819         
25820     },
25821     
25822     
25823     
25824     
25825     domToHTML : function(currentElement, depth, nopadtext) {
25826         
25827         depth = depth || 0;
25828         nopadtext = nopadtext || false;
25829     
25830         if (!currentElement) {
25831             return this.domToHTML(this.doc.body);
25832         }
25833         
25834         //Roo.log(currentElement);
25835         var j;
25836         var allText = false;
25837         var nodeName = currentElement.nodeName;
25838         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25839         
25840         if  (nodeName == '#text') {
25841             
25842             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25843         }
25844         
25845         
25846         var ret = '';
25847         if (nodeName != 'BODY') {
25848              
25849             var i = 0;
25850             // Prints the node tagName, such as <A>, <IMG>, etc
25851             if (tagName) {
25852                 var attr = [];
25853                 for(i = 0; i < currentElement.attributes.length;i++) {
25854                     // quoting?
25855                     var aname = currentElement.attributes.item(i).name;
25856                     if (!currentElement.attributes.item(i).value.length) {
25857                         continue;
25858                     }
25859                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25860                 }
25861                 
25862                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25863             } 
25864             else {
25865                 
25866                 // eack
25867             }
25868         } else {
25869             tagName = false;
25870         }
25871         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25872             return ret;
25873         }
25874         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25875             nopadtext = true;
25876         }
25877         
25878         
25879         // Traverse the tree
25880         i = 0;
25881         var currentElementChild = currentElement.childNodes.item(i);
25882         var allText = true;
25883         var innerHTML  = '';
25884         lastnode = '';
25885         while (currentElementChild) {
25886             // Formatting code (indent the tree so it looks nice on the screen)
25887             var nopad = nopadtext;
25888             if (lastnode == 'SPAN') {
25889                 nopad  = true;
25890             }
25891             // text
25892             if  (currentElementChild.nodeName == '#text') {
25893                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25894                 toadd = nopadtext ? toadd : toadd.trim();
25895                 if (!nopad && toadd.length > 80) {
25896                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25897                 }
25898                 innerHTML  += toadd;
25899                 
25900                 i++;
25901                 currentElementChild = currentElement.childNodes.item(i);
25902                 lastNode = '';
25903                 continue;
25904             }
25905             allText = false;
25906             
25907             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25908                 
25909             // Recursively traverse the tree structure of the child node
25910             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25911             lastnode = currentElementChild.nodeName;
25912             i++;
25913             currentElementChild=currentElement.childNodes.item(i);
25914         }
25915         
25916         ret += innerHTML;
25917         
25918         if (!allText) {
25919                 // The remaining code is mostly for formatting the tree
25920             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25921         }
25922         
25923         
25924         if (tagName) {
25925             ret+= "</"+tagName+">";
25926         }
25927         return ret;
25928         
25929     },
25930         
25931     applyBlacklists : function()
25932     {
25933         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25934         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25935         
25936         this.white = [];
25937         this.black = [];
25938         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25939             if (b.indexOf(tag) > -1) {
25940                 return;
25941             }
25942             this.white.push(tag);
25943             
25944         }, this);
25945         
25946         Roo.each(w, function(tag) {
25947             if (b.indexOf(tag) > -1) {
25948                 return;
25949             }
25950             if (this.white.indexOf(tag) > -1) {
25951                 return;
25952             }
25953             this.white.push(tag);
25954             
25955         }, this);
25956         
25957         
25958         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25959             if (w.indexOf(tag) > -1) {
25960                 return;
25961             }
25962             this.black.push(tag);
25963             
25964         }, this);
25965         
25966         Roo.each(b, function(tag) {
25967             if (w.indexOf(tag) > -1) {
25968                 return;
25969             }
25970             if (this.black.indexOf(tag) > -1) {
25971                 return;
25972             }
25973             this.black.push(tag);
25974             
25975         }, this);
25976         
25977         
25978         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25979         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25980         
25981         this.cwhite = [];
25982         this.cblack = [];
25983         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25984             if (b.indexOf(tag) > -1) {
25985                 return;
25986             }
25987             this.cwhite.push(tag);
25988             
25989         }, this);
25990         
25991         Roo.each(w, function(tag) {
25992             if (b.indexOf(tag) > -1) {
25993                 return;
25994             }
25995             if (this.cwhite.indexOf(tag) > -1) {
25996                 return;
25997             }
25998             this.cwhite.push(tag);
25999             
26000         }, this);
26001         
26002         
26003         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26004             if (w.indexOf(tag) > -1) {
26005                 return;
26006             }
26007             this.cblack.push(tag);
26008             
26009         }, this);
26010         
26011         Roo.each(b, function(tag) {
26012             if (w.indexOf(tag) > -1) {
26013                 return;
26014             }
26015             if (this.cblack.indexOf(tag) > -1) {
26016                 return;
26017             }
26018             this.cblack.push(tag);
26019             
26020         }, this);
26021     },
26022     
26023     setStylesheets : function(stylesheets)
26024     {
26025         if(typeof(stylesheets) == 'string'){
26026             Roo.get(this.iframe.contentDocument.head).createChild({
26027                 tag : 'link',
26028                 rel : 'stylesheet',
26029                 type : 'text/css',
26030                 href : stylesheets
26031             });
26032             
26033             return;
26034         }
26035         var _this = this;
26036      
26037         Roo.each(stylesheets, function(s) {
26038             if(!s.length){
26039                 return;
26040             }
26041             
26042             Roo.get(_this.iframe.contentDocument.head).createChild({
26043                 tag : 'link',
26044                 rel : 'stylesheet',
26045                 type : 'text/css',
26046                 href : s
26047             });
26048         });
26049
26050         
26051     },
26052     
26053     removeStylesheets : function()
26054     {
26055         var _this = this;
26056         
26057         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26058             s.remove();
26059         });
26060     },
26061     
26062     setStyle : function(style)
26063     {
26064         Roo.get(this.iframe.contentDocument.head).createChild({
26065             tag : 'style',
26066             type : 'text/css',
26067             html : style
26068         });
26069
26070         return;
26071     }
26072     
26073     // hide stuff that is not compatible
26074     /**
26075      * @event blur
26076      * @hide
26077      */
26078     /**
26079      * @event change
26080      * @hide
26081      */
26082     /**
26083      * @event focus
26084      * @hide
26085      */
26086     /**
26087      * @event specialkey
26088      * @hide
26089      */
26090     /**
26091      * @cfg {String} fieldClass @hide
26092      */
26093     /**
26094      * @cfg {String} focusClass @hide
26095      */
26096     /**
26097      * @cfg {String} autoCreate @hide
26098      */
26099     /**
26100      * @cfg {String} inputType @hide
26101      */
26102     /**
26103      * @cfg {String} invalidClass @hide
26104      */
26105     /**
26106      * @cfg {String} invalidText @hide
26107      */
26108     /**
26109      * @cfg {String} msgFx @hide
26110      */
26111     /**
26112      * @cfg {String} validateOnBlur @hide
26113      */
26114 });
26115
26116 Roo.HtmlEditorCore.white = [
26117         'area', 'br', 'img', 'input', 'hr', 'wbr',
26118         
26119        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26120        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26121        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26122        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26123        'table',   'ul',         'xmp', 
26124        
26125        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26126       'thead',   'tr', 
26127      
26128       'dir', 'menu', 'ol', 'ul', 'dl',
26129        
26130       'embed',  'object'
26131 ];
26132
26133
26134 Roo.HtmlEditorCore.black = [
26135     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26136         'applet', // 
26137         'base',   'basefont', 'bgsound', 'blink',  'body', 
26138         'frame',  'frameset', 'head',    'html',   'ilayer', 
26139         'iframe', 'layer',  'link',     'meta',    'object',   
26140         'script', 'style' ,'title',  'xml' // clean later..
26141 ];
26142 Roo.HtmlEditorCore.clean = [
26143     'script', 'style', 'title', 'xml'
26144 ];
26145 Roo.HtmlEditorCore.remove = [
26146     'font'
26147 ];
26148 // attributes..
26149
26150 Roo.HtmlEditorCore.ablack = [
26151     'on'
26152 ];
26153     
26154 Roo.HtmlEditorCore.aclean = [ 
26155     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26156 ];
26157
26158 // protocols..
26159 Roo.HtmlEditorCore.pwhite= [
26160         'http',  'https',  'mailto'
26161 ];
26162
26163 // white listed style attributes.
26164 Roo.HtmlEditorCore.cwhite= [
26165       //  'text-align', /// default is to allow most things..
26166       
26167          
26168 //        'font-size'//??
26169 ];
26170
26171 // black listed style attributes.
26172 Roo.HtmlEditorCore.cblack= [
26173       //  'font-size' -- this can be set by the project 
26174 ];
26175
26176
26177 Roo.HtmlEditorCore.swapCodes   =[ 
26178     [    8211, "&#8211;" ], 
26179     [    8212, "&#8212;" ], 
26180     [    8216,  "'" ],  
26181     [    8217, "'" ],  
26182     [    8220, '"' ],  
26183     [    8221, '"' ],  
26184     [    8226, "*" ],  
26185     [    8230, "..." ]
26186 ]; 
26187
26188     /*
26189  * - LGPL
26190  *
26191  * HtmlEditor
26192  * 
26193  */
26194
26195 /**
26196  * @class Roo.bootstrap.HtmlEditor
26197  * @extends Roo.bootstrap.TextArea
26198  * Bootstrap HtmlEditor class
26199
26200  * @constructor
26201  * Create a new HtmlEditor
26202  * @param {Object} config The config object
26203  */
26204
26205 Roo.bootstrap.HtmlEditor = function(config){
26206     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26207     if (!this.toolbars) {
26208         this.toolbars = [];
26209     }
26210     
26211     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26212     this.addEvents({
26213             /**
26214              * @event initialize
26215              * Fires when the editor is fully initialized (including the iframe)
26216              * @param {HtmlEditor} this
26217              */
26218             initialize: true,
26219             /**
26220              * @event activate
26221              * Fires when the editor is first receives the focus. Any insertion must wait
26222              * until after this event.
26223              * @param {HtmlEditor} this
26224              */
26225             activate: true,
26226              /**
26227              * @event beforesync
26228              * Fires before the textarea is updated with content from the editor iframe. Return false
26229              * to cancel the sync.
26230              * @param {HtmlEditor} this
26231              * @param {String} html
26232              */
26233             beforesync: true,
26234              /**
26235              * @event beforepush
26236              * Fires before the iframe editor is updated with content from the textarea. Return false
26237              * to cancel the push.
26238              * @param {HtmlEditor} this
26239              * @param {String} html
26240              */
26241             beforepush: true,
26242              /**
26243              * @event sync
26244              * Fires when the textarea is updated with content from the editor iframe.
26245              * @param {HtmlEditor} this
26246              * @param {String} html
26247              */
26248             sync: true,
26249              /**
26250              * @event push
26251              * Fires when the iframe editor is updated with content from the textarea.
26252              * @param {HtmlEditor} this
26253              * @param {String} html
26254              */
26255             push: true,
26256              /**
26257              * @event editmodechange
26258              * Fires when the editor switches edit modes
26259              * @param {HtmlEditor} this
26260              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26261              */
26262             editmodechange: true,
26263             /**
26264              * @event editorevent
26265              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26266              * @param {HtmlEditor} this
26267              */
26268             editorevent: true,
26269             /**
26270              * @event firstfocus
26271              * Fires when on first focus - needed by toolbars..
26272              * @param {HtmlEditor} this
26273              */
26274             firstfocus: true,
26275             /**
26276              * @event autosave
26277              * Auto save the htmlEditor value as a file into Events
26278              * @param {HtmlEditor} this
26279              */
26280             autosave: true,
26281             /**
26282              * @event savedpreview
26283              * preview the saved version of htmlEditor
26284              * @param {HtmlEditor} this
26285              */
26286             savedpreview: true
26287         });
26288 };
26289
26290
26291 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26292     
26293     
26294       /**
26295      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26296      */
26297     toolbars : false,
26298     
26299      /**
26300     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26301     */
26302     btns : [],
26303    
26304      /**
26305      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26306      *                        Roo.resizable.
26307      */
26308     resizable : false,
26309      /**
26310      * @cfg {Number} height (in pixels)
26311      */   
26312     height: 300,
26313    /**
26314      * @cfg {Number} width (in pixels)
26315      */   
26316     width: false,
26317     
26318     /**
26319      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26320      * 
26321      */
26322     stylesheets: false,
26323     
26324     // id of frame..
26325     frameId: false,
26326     
26327     // private properties
26328     validationEvent : false,
26329     deferHeight: true,
26330     initialized : false,
26331     activated : false,
26332     
26333     onFocus : Roo.emptyFn,
26334     iframePad:3,
26335     hideMode:'offsets',
26336     
26337     tbContainer : false,
26338     
26339     bodyCls : '',
26340     
26341     toolbarContainer :function() {
26342         return this.wrap.select('.x-html-editor-tb',true).first();
26343     },
26344
26345     /**
26346      * Protected method that will not generally be called directly. It
26347      * is called when the editor creates its toolbar. Override this method if you need to
26348      * add custom toolbar buttons.
26349      * @param {HtmlEditor} editor
26350      */
26351     createToolbar : function(){
26352         Roo.log('renewing');
26353         Roo.log("create toolbars");
26354         
26355         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26356         this.toolbars[0].render(this.toolbarContainer());
26357         
26358         return;
26359         
26360 //        if (!editor.toolbars || !editor.toolbars.length) {
26361 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26362 //        }
26363 //        
26364 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26365 //            editor.toolbars[i] = Roo.factory(
26366 //                    typeof(editor.toolbars[i]) == 'string' ?
26367 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26368 //                Roo.bootstrap.HtmlEditor);
26369 //            editor.toolbars[i].init(editor);
26370 //        }
26371     },
26372
26373      
26374     // private
26375     onRender : function(ct, position)
26376     {
26377        // Roo.log("Call onRender: " + this.xtype);
26378         var _t = this;
26379         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26380       
26381         this.wrap = this.inputEl().wrap({
26382             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26383         });
26384         
26385         this.editorcore.onRender(ct, position);
26386          
26387         if (this.resizable) {
26388             this.resizeEl = new Roo.Resizable(this.wrap, {
26389                 pinned : true,
26390                 wrap: true,
26391                 dynamic : true,
26392                 minHeight : this.height,
26393                 height: this.height,
26394                 handles : this.resizable,
26395                 width: this.width,
26396                 listeners : {
26397                     resize : function(r, w, h) {
26398                         _t.onResize(w,h); // -something
26399                     }
26400                 }
26401             });
26402             
26403         }
26404         this.createToolbar(this);
26405        
26406         
26407         if(!this.width && this.resizable){
26408             this.setSize(this.wrap.getSize());
26409         }
26410         if (this.resizeEl) {
26411             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26412             // should trigger onReize..
26413         }
26414         
26415     },
26416
26417     // private
26418     onResize : function(w, h)
26419     {
26420         Roo.log('resize: ' +w + ',' + h );
26421         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26422         var ew = false;
26423         var eh = false;
26424         
26425         if(this.inputEl() ){
26426             if(typeof w == 'number'){
26427                 var aw = w - this.wrap.getFrameWidth('lr');
26428                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26429                 ew = aw;
26430             }
26431             if(typeof h == 'number'){
26432                  var tbh = -11;  // fixme it needs to tool bar size!
26433                 for (var i =0; i < this.toolbars.length;i++) {
26434                     // fixme - ask toolbars for heights?
26435                     tbh += this.toolbars[i].el.getHeight();
26436                     //if (this.toolbars[i].footer) {
26437                     //    tbh += this.toolbars[i].footer.el.getHeight();
26438                     //}
26439                 }
26440               
26441                 
26442                 
26443                 
26444                 
26445                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26446                 ah -= 5; // knock a few pixes off for look..
26447                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26448                 var eh = ah;
26449             }
26450         }
26451         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26452         this.editorcore.onResize(ew,eh);
26453         
26454     },
26455
26456     /**
26457      * Toggles the editor between standard and source edit mode.
26458      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26459      */
26460     toggleSourceEdit : function(sourceEditMode)
26461     {
26462         this.editorcore.toggleSourceEdit(sourceEditMode);
26463         
26464         if(this.editorcore.sourceEditMode){
26465             Roo.log('editor - showing textarea');
26466             
26467 //            Roo.log('in');
26468 //            Roo.log(this.syncValue());
26469             this.syncValue();
26470             this.inputEl().removeClass(['hide', 'x-hidden']);
26471             this.inputEl().dom.removeAttribute('tabIndex');
26472             this.inputEl().focus();
26473         }else{
26474             Roo.log('editor - hiding textarea');
26475 //            Roo.log('out')
26476 //            Roo.log(this.pushValue()); 
26477             this.pushValue();
26478             
26479             this.inputEl().addClass(['hide', 'x-hidden']);
26480             this.inputEl().dom.setAttribute('tabIndex', -1);
26481             //this.deferFocus();
26482         }
26483          
26484         if(this.resizable){
26485             this.setSize(this.wrap.getSize());
26486         }
26487         
26488         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26489     },
26490  
26491     // private (for BoxComponent)
26492     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26493
26494     // private (for BoxComponent)
26495     getResizeEl : function(){
26496         return this.wrap;
26497     },
26498
26499     // private (for BoxComponent)
26500     getPositionEl : function(){
26501         return this.wrap;
26502     },
26503
26504     // private
26505     initEvents : function(){
26506         this.originalValue = this.getValue();
26507     },
26508
26509 //    /**
26510 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26511 //     * @method
26512 //     */
26513 //    markInvalid : Roo.emptyFn,
26514 //    /**
26515 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26516 //     * @method
26517 //     */
26518 //    clearInvalid : Roo.emptyFn,
26519
26520     setValue : function(v){
26521         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26522         this.editorcore.pushValue();
26523     },
26524
26525      
26526     // private
26527     deferFocus : function(){
26528         this.focus.defer(10, this);
26529     },
26530
26531     // doc'ed in Field
26532     focus : function(){
26533         this.editorcore.focus();
26534         
26535     },
26536       
26537
26538     // private
26539     onDestroy : function(){
26540         
26541         
26542         
26543         if(this.rendered){
26544             
26545             for (var i =0; i < this.toolbars.length;i++) {
26546                 // fixme - ask toolbars for heights?
26547                 this.toolbars[i].onDestroy();
26548             }
26549             
26550             this.wrap.dom.innerHTML = '';
26551             this.wrap.remove();
26552         }
26553     },
26554
26555     // private
26556     onFirstFocus : function(){
26557         //Roo.log("onFirstFocus");
26558         this.editorcore.onFirstFocus();
26559          for (var i =0; i < this.toolbars.length;i++) {
26560             this.toolbars[i].onFirstFocus();
26561         }
26562         
26563     },
26564     
26565     // private
26566     syncValue : function()
26567     {   
26568         this.editorcore.syncValue();
26569     },
26570     
26571     pushValue : function()
26572     {   
26573         this.editorcore.pushValue();
26574     }
26575      
26576     
26577     // hide stuff that is not compatible
26578     /**
26579      * @event blur
26580      * @hide
26581      */
26582     /**
26583      * @event change
26584      * @hide
26585      */
26586     /**
26587      * @event focus
26588      * @hide
26589      */
26590     /**
26591      * @event specialkey
26592      * @hide
26593      */
26594     /**
26595      * @cfg {String} fieldClass @hide
26596      */
26597     /**
26598      * @cfg {String} focusClass @hide
26599      */
26600     /**
26601      * @cfg {String} autoCreate @hide
26602      */
26603     /**
26604      * @cfg {String} inputType @hide
26605      */
26606      
26607     /**
26608      * @cfg {String} invalidText @hide
26609      */
26610     /**
26611      * @cfg {String} msgFx @hide
26612      */
26613     /**
26614      * @cfg {String} validateOnBlur @hide
26615      */
26616 });
26617  
26618     
26619    
26620    
26621    
26622       
26623 Roo.namespace('Roo.bootstrap.htmleditor');
26624 /**
26625  * @class Roo.bootstrap.HtmlEditorToolbar1
26626  * Basic Toolbar
26627  * 
26628  * @example
26629  * Usage:
26630  *
26631  new Roo.bootstrap.HtmlEditor({
26632     ....
26633     toolbars : [
26634         new Roo.bootstrap.HtmlEditorToolbar1({
26635             disable : { fonts: 1 , format: 1, ..., ... , ...],
26636             btns : [ .... ]
26637         })
26638     }
26639      
26640  * 
26641  * @cfg {Object} disable List of elements to disable..
26642  * @cfg {Array} btns List of additional buttons.
26643  * 
26644  * 
26645  * NEEDS Extra CSS? 
26646  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26647  */
26648  
26649 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26650 {
26651     
26652     Roo.apply(this, config);
26653     
26654     // default disabled, based on 'good practice'..
26655     this.disable = this.disable || {};
26656     Roo.applyIf(this.disable, {
26657         fontSize : true,
26658         colors : true,
26659         specialElements : true
26660     });
26661     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26662     
26663     this.editor = config.editor;
26664     this.editorcore = config.editor.editorcore;
26665     
26666     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26667     
26668     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26669     // dont call parent... till later.
26670 }
26671 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26672      
26673     bar : true,
26674     
26675     editor : false,
26676     editorcore : false,
26677     
26678     
26679     formats : [
26680         "p" ,  
26681         "h1","h2","h3","h4","h5","h6", 
26682         "pre", "code", 
26683         "abbr", "acronym", "address", "cite", "samp", "var",
26684         'div','span'
26685     ],
26686     
26687     onRender : function(ct, position)
26688     {
26689        // Roo.log("Call onRender: " + this.xtype);
26690         
26691        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26692        Roo.log(this.el);
26693        this.el.dom.style.marginBottom = '0';
26694        var _this = this;
26695        var editorcore = this.editorcore;
26696        var editor= this.editor;
26697        
26698        var children = [];
26699        var btn = function(id,cmd , toggle, handler, html){
26700        
26701             var  event = toggle ? 'toggle' : 'click';
26702        
26703             var a = {
26704                 size : 'sm',
26705                 xtype: 'Button',
26706                 xns: Roo.bootstrap,
26707                 //glyphicon : id,
26708                 fa: id,
26709                 cmd : id || cmd,
26710                 enableToggle:toggle !== false,
26711                 html : html || '',
26712                 pressed : toggle ? false : null,
26713                 listeners : {}
26714             };
26715             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26716                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26717             };
26718             children.push(a);
26719             return a;
26720        }
26721        
26722     //    var cb_box = function...
26723         
26724         var style = {
26725                 xtype: 'Button',
26726                 size : 'sm',
26727                 xns: Roo.bootstrap,
26728                 fa : 'font',
26729                 //html : 'submit'
26730                 menu : {
26731                     xtype: 'Menu',
26732                     xns: Roo.bootstrap,
26733                     items:  []
26734                 }
26735         };
26736         Roo.each(this.formats, function(f) {
26737             style.menu.items.push({
26738                 xtype :'MenuItem',
26739                 xns: Roo.bootstrap,
26740                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26741                 tagname : f,
26742                 listeners : {
26743                     click : function()
26744                     {
26745                         editorcore.insertTag(this.tagname);
26746                         editor.focus();
26747                     }
26748                 }
26749                 
26750             });
26751         });
26752         children.push(style);   
26753         
26754         btn('bold',false,true);
26755         btn('italic',false,true);
26756         btn('align-left', 'justifyleft',true);
26757         btn('align-center', 'justifycenter',true);
26758         btn('align-right' , 'justifyright',true);
26759         btn('link', false, false, function(btn) {
26760             //Roo.log("create link?");
26761             var url = prompt(this.createLinkText, this.defaultLinkValue);
26762             if(url && url != 'http:/'+'/'){
26763                 this.editorcore.relayCmd('createlink', url);
26764             }
26765         }),
26766         btn('list','insertunorderedlist',true);
26767         btn('pencil', false,true, function(btn){
26768                 Roo.log(this);
26769                 this.toggleSourceEdit(btn.pressed);
26770         });
26771         
26772         if (this.editor.btns.length > 0) {
26773             for (var i = 0; i<this.editor.btns.length; i++) {
26774                 children.push(this.editor.btns[i]);
26775             }
26776         }
26777         
26778         /*
26779         var cog = {
26780                 xtype: 'Button',
26781                 size : 'sm',
26782                 xns: Roo.bootstrap,
26783                 glyphicon : 'cog',
26784                 //html : 'submit'
26785                 menu : {
26786                     xtype: 'Menu',
26787                     xns: Roo.bootstrap,
26788                     items:  []
26789                 }
26790         };
26791         
26792         cog.menu.items.push({
26793             xtype :'MenuItem',
26794             xns: Roo.bootstrap,
26795             html : Clean styles,
26796             tagname : f,
26797             listeners : {
26798                 click : function()
26799                 {
26800                     editorcore.insertTag(this.tagname);
26801                     editor.focus();
26802                 }
26803             }
26804             
26805         });
26806        */
26807         
26808          
26809        this.xtype = 'NavSimplebar';
26810         
26811         for(var i=0;i< children.length;i++) {
26812             
26813             this.buttons.add(this.addxtypeChild(children[i]));
26814             
26815         }
26816         
26817         editor.on('editorevent', this.updateToolbar, this);
26818     },
26819     onBtnClick : function(id)
26820     {
26821        this.editorcore.relayCmd(id);
26822        this.editorcore.focus();
26823     },
26824     
26825     /**
26826      * Protected method that will not generally be called directly. It triggers
26827      * a toolbar update by reading the markup state of the current selection in the editor.
26828      */
26829     updateToolbar: function(){
26830
26831         if(!this.editorcore.activated){
26832             this.editor.onFirstFocus(); // is this neeed?
26833             return;
26834         }
26835
26836         var btns = this.buttons; 
26837         var doc = this.editorcore.doc;
26838         btns.get('bold').setActive(doc.queryCommandState('bold'));
26839         btns.get('italic').setActive(doc.queryCommandState('italic'));
26840         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26841         
26842         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26843         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26844         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26845         
26846         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26847         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26848          /*
26849         
26850         var ans = this.editorcore.getAllAncestors();
26851         if (this.formatCombo) {
26852             
26853             
26854             var store = this.formatCombo.store;
26855             this.formatCombo.setValue("");
26856             for (var i =0; i < ans.length;i++) {
26857                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26858                     // select it..
26859                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26860                     break;
26861                 }
26862             }
26863         }
26864         
26865         
26866         
26867         // hides menus... - so this cant be on a menu...
26868         Roo.bootstrap.MenuMgr.hideAll();
26869         */
26870         Roo.bootstrap.MenuMgr.hideAll();
26871         //this.editorsyncValue();
26872     },
26873     onFirstFocus: function() {
26874         this.buttons.each(function(item){
26875            item.enable();
26876         });
26877     },
26878     toggleSourceEdit : function(sourceEditMode){
26879         
26880           
26881         if(sourceEditMode){
26882             Roo.log("disabling buttons");
26883            this.buttons.each( function(item){
26884                 if(item.cmd != 'pencil'){
26885                     item.disable();
26886                 }
26887             });
26888           
26889         }else{
26890             Roo.log("enabling buttons");
26891             if(this.editorcore.initialized){
26892                 this.buttons.each( function(item){
26893                     item.enable();
26894                 });
26895             }
26896             
26897         }
26898         Roo.log("calling toggole on editor");
26899         // tell the editor that it's been pressed..
26900         this.editor.toggleSourceEdit(sourceEditMode);
26901        
26902     }
26903 });
26904
26905
26906
26907
26908  
26909 /*
26910  * - LGPL
26911  */
26912
26913 /**
26914  * @class Roo.bootstrap.Markdown
26915  * @extends Roo.bootstrap.TextArea
26916  * Bootstrap Showdown editable area
26917  * @cfg {string} content
26918  * 
26919  * @constructor
26920  * Create a new Showdown
26921  */
26922
26923 Roo.bootstrap.Markdown = function(config){
26924     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26925    
26926 };
26927
26928 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26929     
26930     editing :false,
26931     
26932     initEvents : function()
26933     {
26934         
26935         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26936         this.markdownEl = this.el.createChild({
26937             cls : 'roo-markdown-area'
26938         });
26939         this.inputEl().addClass('d-none');
26940         if (this.getValue() == '') {
26941             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26942             
26943         } else {
26944             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26945         }
26946         this.markdownEl.on('click', this.toggleTextEdit, this);
26947         this.on('blur', this.toggleTextEdit, this);
26948         this.on('specialkey', this.resizeTextArea, this);
26949     },
26950     
26951     toggleTextEdit : function()
26952     {
26953         var sh = this.markdownEl.getHeight();
26954         this.inputEl().addClass('d-none');
26955         this.markdownEl.addClass('d-none');
26956         if (!this.editing) {
26957             // show editor?
26958             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26959             this.inputEl().removeClass('d-none');
26960             this.inputEl().focus();
26961             this.editing = true;
26962             return;
26963         }
26964         // show showdown...
26965         this.updateMarkdown();
26966         this.markdownEl.removeClass('d-none');
26967         this.editing = false;
26968         return;
26969     },
26970     updateMarkdown : function()
26971     {
26972         if (this.getValue() == '') {
26973             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26974             return;
26975         }
26976  
26977         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26978     },
26979     
26980     resizeTextArea: function () {
26981         
26982         var sh = 100;
26983         Roo.log([sh, this.getValue().split("\n").length * 30]);
26984         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26985     },
26986     setValue : function(val)
26987     {
26988         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26989         if (!this.editing) {
26990             this.updateMarkdown();
26991         }
26992         
26993     },
26994     focus : function()
26995     {
26996         if (!this.editing) {
26997             this.toggleTextEdit();
26998         }
26999         
27000     }
27001
27002
27003 });
27004 /**
27005  * @class Roo.bootstrap.Table.AbstractSelectionModel
27006  * @extends Roo.util.Observable
27007  * Abstract base class for grid SelectionModels.  It provides the interface that should be
27008  * implemented by descendant classes.  This class should not be directly instantiated.
27009  * @constructor
27010  */
27011 Roo.bootstrap.Table.AbstractSelectionModel = function(){
27012     this.locked = false;
27013     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
27014 };
27015
27016
27017 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
27018     /** @ignore Called by the grid automatically. Do not call directly. */
27019     init : function(grid){
27020         this.grid = grid;
27021         this.initEvents();
27022     },
27023
27024     /**
27025      * Locks the selections.
27026      */
27027     lock : function(){
27028         this.locked = true;
27029     },
27030
27031     /**
27032      * Unlocks the selections.
27033      */
27034     unlock : function(){
27035         this.locked = false;
27036     },
27037
27038     /**
27039      * Returns true if the selections are locked.
27040      * @return {Boolean}
27041      */
27042     isLocked : function(){
27043         return this.locked;
27044     },
27045     
27046     
27047     initEvents : function ()
27048     {
27049         
27050     }
27051 });
27052 /**
27053  * @extends Roo.bootstrap.Table.AbstractSelectionModel
27054  * @class Roo.bootstrap.Table.RowSelectionModel
27055  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27056  * It supports multiple selections and keyboard selection/navigation. 
27057  * @constructor
27058  * @param {Object} config
27059  */
27060
27061 Roo.bootstrap.Table.RowSelectionModel = function(config){
27062     Roo.apply(this, config);
27063     this.selections = new Roo.util.MixedCollection(false, function(o){
27064         return o.id;
27065     });
27066
27067     this.last = false;
27068     this.lastActive = false;
27069
27070     this.addEvents({
27071         /**
27072              * @event selectionchange
27073              * Fires when the selection changes
27074              * @param {SelectionModel} this
27075              */
27076             "selectionchange" : true,
27077         /**
27078              * @event afterselectionchange
27079              * Fires after the selection changes (eg. by key press or clicking)
27080              * @param {SelectionModel} this
27081              */
27082             "afterselectionchange" : true,
27083         /**
27084              * @event beforerowselect
27085              * Fires when a row is selected being selected, return false to cancel.
27086              * @param {SelectionModel} this
27087              * @param {Number} rowIndex The selected index
27088              * @param {Boolean} keepExisting False if other selections will be cleared
27089              */
27090             "beforerowselect" : true,
27091         /**
27092              * @event rowselect
27093              * Fires when a row is selected.
27094              * @param {SelectionModel} this
27095              * @param {Number} rowIndex The selected index
27096              * @param {Roo.data.Record} r The record
27097              */
27098             "rowselect" : true,
27099         /**
27100              * @event rowdeselect
27101              * Fires when a row is deselected.
27102              * @param {SelectionModel} this
27103              * @param {Number} rowIndex The selected index
27104              */
27105         "rowdeselect" : true
27106     });
27107     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27108     this.locked = false;
27109  };
27110
27111 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27112     /**
27113      * @cfg {Boolean} singleSelect
27114      * True to allow selection of only one row at a time (defaults to false)
27115      */
27116     singleSelect : false,
27117
27118     // private
27119     initEvents : function()
27120     {
27121
27122         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27123         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27124         //}else{ // allow click to work like normal
27125          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27126         //}
27127         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27128         this.grid.on("rowclick", this.handleMouseDown, this);
27129         
27130         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27131             "up" : function(e){
27132                 if(!e.shiftKey){
27133                     this.selectPrevious(e.shiftKey);
27134                 }else if(this.last !== false && this.lastActive !== false){
27135                     var last = this.last;
27136                     this.selectRange(this.last,  this.lastActive-1);
27137                     this.grid.getView().focusRow(this.lastActive);
27138                     if(last !== false){
27139                         this.last = last;
27140                     }
27141                 }else{
27142                     this.selectFirstRow();
27143                 }
27144                 this.fireEvent("afterselectionchange", this);
27145             },
27146             "down" : function(e){
27147                 if(!e.shiftKey){
27148                     this.selectNext(e.shiftKey);
27149                 }else if(this.last !== false && this.lastActive !== false){
27150                     var last = this.last;
27151                     this.selectRange(this.last,  this.lastActive+1);
27152                     this.grid.getView().focusRow(this.lastActive);
27153                     if(last !== false){
27154                         this.last = last;
27155                     }
27156                 }else{
27157                     this.selectFirstRow();
27158                 }
27159                 this.fireEvent("afterselectionchange", this);
27160             },
27161             scope: this
27162         });
27163         this.grid.store.on('load', function(){
27164             this.selections.clear();
27165         },this);
27166         /*
27167         var view = this.grid.view;
27168         view.on("refresh", this.onRefresh, this);
27169         view.on("rowupdated", this.onRowUpdated, this);
27170         view.on("rowremoved", this.onRemove, this);
27171         */
27172     },
27173
27174     // private
27175     onRefresh : function()
27176     {
27177         var ds = this.grid.store, i, v = this.grid.view;
27178         var s = this.selections;
27179         s.each(function(r){
27180             if((i = ds.indexOfId(r.id)) != -1){
27181                 v.onRowSelect(i);
27182             }else{
27183                 s.remove(r);
27184             }
27185         });
27186     },
27187
27188     // private
27189     onRemove : function(v, index, r){
27190         this.selections.remove(r);
27191     },
27192
27193     // private
27194     onRowUpdated : function(v, index, r){
27195         if(this.isSelected(r)){
27196             v.onRowSelect(index);
27197         }
27198     },
27199
27200     /**
27201      * Select records.
27202      * @param {Array} records The records to select
27203      * @param {Boolean} keepExisting (optional) True to keep existing selections
27204      */
27205     selectRecords : function(records, keepExisting)
27206     {
27207         if(!keepExisting){
27208             this.clearSelections();
27209         }
27210             var ds = this.grid.store;
27211         for(var i = 0, len = records.length; i < len; i++){
27212             this.selectRow(ds.indexOf(records[i]), true);
27213         }
27214     },
27215
27216     /**
27217      * Gets the number of selected rows.
27218      * @return {Number}
27219      */
27220     getCount : function(){
27221         return this.selections.length;
27222     },
27223
27224     /**
27225      * Selects the first row in the grid.
27226      */
27227     selectFirstRow : function(){
27228         this.selectRow(0);
27229     },
27230
27231     /**
27232      * Select the last row.
27233      * @param {Boolean} keepExisting (optional) True to keep existing selections
27234      */
27235     selectLastRow : function(keepExisting){
27236         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27237         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27238     },
27239
27240     /**
27241      * Selects the row immediately following the last selected row.
27242      * @param {Boolean} keepExisting (optional) True to keep existing selections
27243      */
27244     selectNext : function(keepExisting)
27245     {
27246             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27247             this.selectRow(this.last+1, keepExisting);
27248             this.grid.getView().focusRow(this.last);
27249         }
27250     },
27251
27252     /**
27253      * Selects the row that precedes the last selected row.
27254      * @param {Boolean} keepExisting (optional) True to keep existing selections
27255      */
27256     selectPrevious : function(keepExisting){
27257         if(this.last){
27258             this.selectRow(this.last-1, keepExisting);
27259             this.grid.getView().focusRow(this.last);
27260         }
27261     },
27262
27263     /**
27264      * Returns the selected records
27265      * @return {Array} Array of selected records
27266      */
27267     getSelections : function(){
27268         return [].concat(this.selections.items);
27269     },
27270
27271     /**
27272      * Returns the first selected record.
27273      * @return {Record}
27274      */
27275     getSelected : function(){
27276         return this.selections.itemAt(0);
27277     },
27278
27279
27280     /**
27281      * Clears all selections.
27282      */
27283     clearSelections : function(fast)
27284     {
27285         if(this.locked) {
27286             return;
27287         }
27288         if(fast !== true){
27289                 var ds = this.grid.store;
27290             var s = this.selections;
27291             s.each(function(r){
27292                 this.deselectRow(ds.indexOfId(r.id));
27293             }, this);
27294             s.clear();
27295         }else{
27296             this.selections.clear();
27297         }
27298         this.last = false;
27299     },
27300
27301
27302     /**
27303      * Selects all rows.
27304      */
27305     selectAll : function(){
27306         if(this.locked) {
27307             return;
27308         }
27309         this.selections.clear();
27310         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27311             this.selectRow(i, true);
27312         }
27313     },
27314
27315     /**
27316      * Returns True if there is a selection.
27317      * @return {Boolean}
27318      */
27319     hasSelection : function(){
27320         return this.selections.length > 0;
27321     },
27322
27323     /**
27324      * Returns True if the specified row is selected.
27325      * @param {Number/Record} record The record or index of the record to check
27326      * @return {Boolean}
27327      */
27328     isSelected : function(index){
27329             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27330         return (r && this.selections.key(r.id) ? true : false);
27331     },
27332
27333     /**
27334      * Returns True if the specified record id is selected.
27335      * @param {String} id The id of record to check
27336      * @return {Boolean}
27337      */
27338     isIdSelected : function(id){
27339         return (this.selections.key(id) ? true : false);
27340     },
27341
27342
27343     // private
27344     handleMouseDBClick : function(e, t){
27345         
27346     },
27347     // private
27348     handleMouseDown : function(e, t)
27349     {
27350             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27351         if(this.isLocked() || rowIndex < 0 ){
27352             return;
27353         };
27354         if(e.shiftKey && this.last !== false){
27355             var last = this.last;
27356             this.selectRange(last, rowIndex, e.ctrlKey);
27357             this.last = last; // reset the last
27358             t.focus();
27359     
27360         }else{
27361             var isSelected = this.isSelected(rowIndex);
27362             //Roo.log("select row:" + rowIndex);
27363             if(isSelected){
27364                 this.deselectRow(rowIndex);
27365             } else {
27366                         this.selectRow(rowIndex, true);
27367             }
27368     
27369             /*
27370                 if(e.button !== 0 && isSelected){
27371                 alert('rowIndex 2: ' + rowIndex);
27372                     view.focusRow(rowIndex);
27373                 }else if(e.ctrlKey && isSelected){
27374                     this.deselectRow(rowIndex);
27375                 }else if(!isSelected){
27376                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27377                     view.focusRow(rowIndex);
27378                 }
27379             */
27380         }
27381         this.fireEvent("afterselectionchange", this);
27382     },
27383     // private
27384     handleDragableRowClick :  function(grid, rowIndex, e) 
27385     {
27386         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27387             this.selectRow(rowIndex, false);
27388             grid.view.focusRow(rowIndex);
27389              this.fireEvent("afterselectionchange", this);
27390         }
27391     },
27392     
27393     /**
27394      * Selects multiple rows.
27395      * @param {Array} rows Array of the indexes of the row to select
27396      * @param {Boolean} keepExisting (optional) True to keep existing selections
27397      */
27398     selectRows : function(rows, keepExisting){
27399         if(!keepExisting){
27400             this.clearSelections();
27401         }
27402         for(var i = 0, len = rows.length; i < len; i++){
27403             this.selectRow(rows[i], true);
27404         }
27405     },
27406
27407     /**
27408      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27409      * @param {Number} startRow The index of the first row in the range
27410      * @param {Number} endRow The index of the last row in the range
27411      * @param {Boolean} keepExisting (optional) True to retain existing selections
27412      */
27413     selectRange : function(startRow, endRow, keepExisting){
27414         if(this.locked) {
27415             return;
27416         }
27417         if(!keepExisting){
27418             this.clearSelections();
27419         }
27420         if(startRow <= endRow){
27421             for(var i = startRow; i <= endRow; i++){
27422                 this.selectRow(i, true);
27423             }
27424         }else{
27425             for(var i = startRow; i >= endRow; i--){
27426                 this.selectRow(i, true);
27427             }
27428         }
27429     },
27430
27431     /**
27432      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27433      * @param {Number} startRow The index of the first row in the range
27434      * @param {Number} endRow The index of the last row in the range
27435      */
27436     deselectRange : function(startRow, endRow, preventViewNotify){
27437         if(this.locked) {
27438             return;
27439         }
27440         for(var i = startRow; i <= endRow; i++){
27441             this.deselectRow(i, preventViewNotify);
27442         }
27443     },
27444
27445     /**
27446      * Selects a row.
27447      * @param {Number} row The index of the row to select
27448      * @param {Boolean} keepExisting (optional) True to keep existing selections
27449      */
27450     selectRow : function(index, keepExisting, preventViewNotify)
27451     {
27452             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27453             return;
27454         }
27455         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27456             if(!keepExisting || this.singleSelect){
27457                 this.clearSelections();
27458             }
27459             
27460             var r = this.grid.store.getAt(index);
27461             //console.log('selectRow - record id :' + r.id);
27462             
27463             this.selections.add(r);
27464             this.last = this.lastActive = index;
27465             if(!preventViewNotify){
27466                 var proxy = new Roo.Element(
27467                                 this.grid.getRowDom(index)
27468                 );
27469                 proxy.addClass('bg-info info');
27470             }
27471             this.fireEvent("rowselect", this, index, r);
27472             this.fireEvent("selectionchange", this);
27473         }
27474     },
27475
27476     /**
27477      * Deselects a row.
27478      * @param {Number} row The index of the row to deselect
27479      */
27480     deselectRow : function(index, preventViewNotify)
27481     {
27482         if(this.locked) {
27483             return;
27484         }
27485         if(this.last == index){
27486             this.last = false;
27487         }
27488         if(this.lastActive == index){
27489             this.lastActive = false;
27490         }
27491         
27492         var r = this.grid.store.getAt(index);
27493         if (!r) {
27494             return;
27495         }
27496         
27497         this.selections.remove(r);
27498         //.console.log('deselectRow - record id :' + r.id);
27499         if(!preventViewNotify){
27500         
27501             var proxy = new Roo.Element(
27502                 this.grid.getRowDom(index)
27503             );
27504             proxy.removeClass('bg-info info');
27505         }
27506         this.fireEvent("rowdeselect", this, index);
27507         this.fireEvent("selectionchange", this);
27508     },
27509
27510     // private
27511     restoreLast : function(){
27512         if(this._last){
27513             this.last = this._last;
27514         }
27515     },
27516
27517     // private
27518     acceptsNav : function(row, col, cm){
27519         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27520     },
27521
27522     // private
27523     onEditorKey : function(field, e){
27524         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27525         if(k == e.TAB){
27526             e.stopEvent();
27527             ed.completeEdit();
27528             if(e.shiftKey){
27529                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27530             }else{
27531                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27532             }
27533         }else if(k == e.ENTER && !e.ctrlKey){
27534             e.stopEvent();
27535             ed.completeEdit();
27536             if(e.shiftKey){
27537                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27538             }else{
27539                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27540             }
27541         }else if(k == e.ESC){
27542             ed.cancelEdit();
27543         }
27544         if(newCell){
27545             g.startEditing(newCell[0], newCell[1]);
27546         }
27547     }
27548 });
27549 /*
27550  * Based on:
27551  * Ext JS Library 1.1.1
27552  * Copyright(c) 2006-2007, Ext JS, LLC.
27553  *
27554  * Originally Released Under LGPL - original licence link has changed is not relivant.
27555  *
27556  * Fork - LGPL
27557  * <script type="text/javascript">
27558  */
27559  
27560 /**
27561  * @class Roo.bootstrap.PagingToolbar
27562  * @extends Roo.bootstrap.NavSimplebar
27563  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27564  * @constructor
27565  * Create a new PagingToolbar
27566  * @param {Object} config The config object
27567  * @param {Roo.data.Store} store
27568  */
27569 Roo.bootstrap.PagingToolbar = function(config)
27570 {
27571     // old args format still supported... - xtype is prefered..
27572         // created from xtype...
27573     
27574     this.ds = config.dataSource;
27575     
27576     if (config.store && !this.ds) {
27577         this.store= Roo.factory(config.store, Roo.data);
27578         this.ds = this.store;
27579         this.ds.xmodule = this.xmodule || false;
27580     }
27581     
27582     this.toolbarItems = [];
27583     if (config.items) {
27584         this.toolbarItems = config.items;
27585     }
27586     
27587     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27588     
27589     this.cursor = 0;
27590     
27591     if (this.ds) { 
27592         this.bind(this.ds);
27593     }
27594     
27595     if (Roo.bootstrap.version == 4) {
27596         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27597     } else {
27598         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27599     }
27600     
27601 };
27602
27603 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27604     /**
27605      * @cfg {Roo.data.Store} dataSource
27606      * The underlying data store providing the paged data
27607      */
27608     /**
27609      * @cfg {String/HTMLElement/Element} container
27610      * container The id or element that will contain the toolbar
27611      */
27612     /**
27613      * @cfg {Boolean} displayInfo
27614      * True to display the displayMsg (defaults to false)
27615      */
27616     /**
27617      * @cfg {Number} pageSize
27618      * The number of records to display per page (defaults to 20)
27619      */
27620     pageSize: 20,
27621     /**
27622      * @cfg {String} displayMsg
27623      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27624      */
27625     displayMsg : 'Displaying {0} - {1} of {2}',
27626     /**
27627      * @cfg {String} emptyMsg
27628      * The message to display when no records are found (defaults to "No data to display")
27629      */
27630     emptyMsg : 'No data to display',
27631     /**
27632      * Customizable piece of the default paging text (defaults to "Page")
27633      * @type String
27634      */
27635     beforePageText : "Page",
27636     /**
27637      * Customizable piece of the default paging text (defaults to "of %0")
27638      * @type String
27639      */
27640     afterPageText : "of {0}",
27641     /**
27642      * Customizable piece of the default paging text (defaults to "First Page")
27643      * @type String
27644      */
27645     firstText : "First Page",
27646     /**
27647      * Customizable piece of the default paging text (defaults to "Previous Page")
27648      * @type String
27649      */
27650     prevText : "Previous Page",
27651     /**
27652      * Customizable piece of the default paging text (defaults to "Next Page")
27653      * @type String
27654      */
27655     nextText : "Next Page",
27656     /**
27657      * Customizable piece of the default paging text (defaults to "Last Page")
27658      * @type String
27659      */
27660     lastText : "Last Page",
27661     /**
27662      * Customizable piece of the default paging text (defaults to "Refresh")
27663      * @type String
27664      */
27665     refreshText : "Refresh",
27666
27667     buttons : false,
27668     // private
27669     onRender : function(ct, position) 
27670     {
27671         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27672         this.navgroup.parentId = this.id;
27673         this.navgroup.onRender(this.el, null);
27674         // add the buttons to the navgroup
27675         
27676         if(this.displayInfo){
27677             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27678             this.displayEl = this.el.select('.x-paging-info', true).first();
27679 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27680 //            this.displayEl = navel.el.select('span',true).first();
27681         }
27682         
27683         var _this = this;
27684         
27685         if(this.buttons){
27686             Roo.each(_this.buttons, function(e){ // this might need to use render????
27687                Roo.factory(e).render(_this.el);
27688             });
27689         }
27690             
27691         Roo.each(_this.toolbarItems, function(e) {
27692             _this.navgroup.addItem(e);
27693         });
27694         
27695         
27696         this.first = this.navgroup.addItem({
27697             tooltip: this.firstText,
27698             cls: "prev btn-outline-secondary",
27699             html : ' <i class="fa fa-step-backward"></i>',
27700             disabled: true,
27701             preventDefault: true,
27702             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27703         });
27704         
27705         this.prev =  this.navgroup.addItem({
27706             tooltip: this.prevText,
27707             cls: "prev btn-outline-secondary",
27708             html : ' <i class="fa fa-backward"></i>',
27709             disabled: true,
27710             preventDefault: true,
27711             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27712         });
27713     //this.addSeparator();
27714         
27715         
27716         var field = this.navgroup.addItem( {
27717             tagtype : 'span',
27718             cls : 'x-paging-position  btn-outline-secondary',
27719              disabled: true,
27720             html : this.beforePageText  +
27721                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27722                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27723          } ); //?? escaped?
27724         
27725         this.field = field.el.select('input', true).first();
27726         this.field.on("keydown", this.onPagingKeydown, this);
27727         this.field.on("focus", function(){this.dom.select();});
27728     
27729     
27730         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27731         //this.field.setHeight(18);
27732         //this.addSeparator();
27733         this.next = this.navgroup.addItem({
27734             tooltip: this.nextText,
27735             cls: "next btn-outline-secondary",
27736             html : ' <i class="fa fa-forward"></i>',
27737             disabled: true,
27738             preventDefault: true,
27739             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27740         });
27741         this.last = this.navgroup.addItem({
27742             tooltip: this.lastText,
27743             html : ' <i class="fa fa-step-forward"></i>',
27744             cls: "next btn-outline-secondary",
27745             disabled: true,
27746             preventDefault: true,
27747             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27748         });
27749     //this.addSeparator();
27750         this.loading = this.navgroup.addItem({
27751             tooltip: this.refreshText,
27752             cls: "btn-outline-secondary",
27753             html : ' <i class="fa fa-refresh"></i>',
27754             preventDefault: true,
27755             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27756         });
27757         
27758     },
27759
27760     // private
27761     updateInfo : function(){
27762         if(this.displayEl){
27763             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27764             var msg = count == 0 ?
27765                 this.emptyMsg :
27766                 String.format(
27767                     this.displayMsg,
27768                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27769                 );
27770             this.displayEl.update(msg);
27771         }
27772     },
27773
27774     // private
27775     onLoad : function(ds, r, o)
27776     {
27777         this.cursor = o.params && o.params.start ? o.params.start : 0;
27778         
27779         var d = this.getPageData(),
27780             ap = d.activePage,
27781             ps = d.pages;
27782         
27783         
27784         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27785         this.field.dom.value = ap;
27786         this.first.setDisabled(ap == 1);
27787         this.prev.setDisabled(ap == 1);
27788         this.next.setDisabled(ap == ps);
27789         this.last.setDisabled(ap == ps);
27790         this.loading.enable();
27791         this.updateInfo();
27792     },
27793
27794     // private
27795     getPageData : function(){
27796         var total = this.ds.getTotalCount();
27797         return {
27798             total : total,
27799             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27800             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27801         };
27802     },
27803
27804     // private
27805     onLoadError : function(){
27806         this.loading.enable();
27807     },
27808
27809     // private
27810     onPagingKeydown : function(e){
27811         var k = e.getKey();
27812         var d = this.getPageData();
27813         if(k == e.RETURN){
27814             var v = this.field.dom.value, pageNum;
27815             if(!v || isNaN(pageNum = parseInt(v, 10))){
27816                 this.field.dom.value = d.activePage;
27817                 return;
27818             }
27819             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27820             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27821             e.stopEvent();
27822         }
27823         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))
27824         {
27825           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27826           this.field.dom.value = pageNum;
27827           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27828           e.stopEvent();
27829         }
27830         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27831         {
27832           var v = this.field.dom.value, pageNum; 
27833           var increment = (e.shiftKey) ? 10 : 1;
27834           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27835                 increment *= -1;
27836           }
27837           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27838             this.field.dom.value = d.activePage;
27839             return;
27840           }
27841           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27842           {
27843             this.field.dom.value = parseInt(v, 10) + increment;
27844             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27845             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27846           }
27847           e.stopEvent();
27848         }
27849     },
27850
27851     // private
27852     beforeLoad : function(){
27853         if(this.loading){
27854             this.loading.disable();
27855         }
27856     },
27857
27858     // private
27859     onClick : function(which){
27860         
27861         var ds = this.ds;
27862         if (!ds) {
27863             return;
27864         }
27865         
27866         switch(which){
27867             case "first":
27868                 ds.load({params:{start: 0, limit: this.pageSize}});
27869             break;
27870             case "prev":
27871                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27872             break;
27873             case "next":
27874                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27875             break;
27876             case "last":
27877                 var total = ds.getTotalCount();
27878                 var extra = total % this.pageSize;
27879                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27880                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27881             break;
27882             case "refresh":
27883                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27884             break;
27885         }
27886     },
27887
27888     /**
27889      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27890      * @param {Roo.data.Store} store The data store to unbind
27891      */
27892     unbind : function(ds){
27893         ds.un("beforeload", this.beforeLoad, this);
27894         ds.un("load", this.onLoad, this);
27895         ds.un("loadexception", this.onLoadError, this);
27896         ds.un("remove", this.updateInfo, this);
27897         ds.un("add", this.updateInfo, this);
27898         this.ds = undefined;
27899     },
27900
27901     /**
27902      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27903      * @param {Roo.data.Store} store The data store to bind
27904      */
27905     bind : function(ds){
27906         ds.on("beforeload", this.beforeLoad, this);
27907         ds.on("load", this.onLoad, this);
27908         ds.on("loadexception", this.onLoadError, this);
27909         ds.on("remove", this.updateInfo, this);
27910         ds.on("add", this.updateInfo, this);
27911         this.ds = ds;
27912     }
27913 });/*
27914  * - LGPL
27915  *
27916  * element
27917  * 
27918  */
27919
27920 /**
27921  * @class Roo.bootstrap.MessageBar
27922  * @extends Roo.bootstrap.Component
27923  * Bootstrap MessageBar class
27924  * @cfg {String} html contents of the MessageBar
27925  * @cfg {String} weight (info | success | warning | danger) default info
27926  * @cfg {String} beforeClass insert the bar before the given class
27927  * @cfg {Boolean} closable (true | false) default false
27928  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27929  * 
27930  * @constructor
27931  * Create a new Element
27932  * @param {Object} config The config object
27933  */
27934
27935 Roo.bootstrap.MessageBar = function(config){
27936     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27937 };
27938
27939 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27940     
27941     html: '',
27942     weight: 'info',
27943     closable: false,
27944     fixed: false,
27945     beforeClass: 'bootstrap-sticky-wrap',
27946     
27947     getAutoCreate : function(){
27948         
27949         var cfg = {
27950             tag: 'div',
27951             cls: 'alert alert-dismissable alert-' + this.weight,
27952             cn: [
27953                 {
27954                     tag: 'span',
27955                     cls: 'message',
27956                     html: this.html || ''
27957                 }
27958             ]
27959         };
27960         
27961         if(this.fixed){
27962             cfg.cls += ' alert-messages-fixed';
27963         }
27964         
27965         if(this.closable){
27966             cfg.cn.push({
27967                 tag: 'button',
27968                 cls: 'close',
27969                 html: 'x'
27970             });
27971         }
27972         
27973         return cfg;
27974     },
27975     
27976     onRender : function(ct, position)
27977     {
27978         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27979         
27980         if(!this.el){
27981             var cfg = Roo.apply({},  this.getAutoCreate());
27982             cfg.id = Roo.id();
27983             
27984             if (this.cls) {
27985                 cfg.cls += ' ' + this.cls;
27986             }
27987             if (this.style) {
27988                 cfg.style = this.style;
27989             }
27990             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27991             
27992             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27993         }
27994         
27995         this.el.select('>button.close').on('click', this.hide, this);
27996         
27997     },
27998     
27999     show : function()
28000     {
28001         if (!this.rendered) {
28002             this.render();
28003         }
28004         
28005         this.el.show();
28006         
28007         this.fireEvent('show', this);
28008         
28009     },
28010     
28011     hide : function()
28012     {
28013         if (!this.rendered) {
28014             this.render();
28015         }
28016         
28017         this.el.hide();
28018         
28019         this.fireEvent('hide', this);
28020     },
28021     
28022     update : function()
28023     {
28024 //        var e = this.el.dom.firstChild;
28025 //        
28026 //        if(this.closable){
28027 //            e = e.nextSibling;
28028 //        }
28029 //        
28030 //        e.data = this.html || '';
28031
28032         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28033     }
28034    
28035 });
28036
28037  
28038
28039      /*
28040  * - LGPL
28041  *
28042  * Graph
28043  * 
28044  */
28045
28046
28047 /**
28048  * @class Roo.bootstrap.Graph
28049  * @extends Roo.bootstrap.Component
28050  * Bootstrap Graph class
28051 > Prameters
28052  -sm {number} sm 4
28053  -md {number} md 5
28054  @cfg {String} graphtype  bar | vbar | pie
28055  @cfg {number} g_x coodinator | centre x (pie)
28056  @cfg {number} g_y coodinator | centre y (pie)
28057  @cfg {number} g_r radius (pie)
28058  @cfg {number} g_height height of the chart (respected by all elements in the set)
28059  @cfg {number} g_width width of the chart (respected by all elements in the set)
28060  @cfg {Object} title The title of the chart
28061     
28062  -{Array}  values
28063  -opts (object) options for the chart 
28064      o {
28065      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28066      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28067      o vgutter (number)
28068      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.
28069      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28070      o to
28071      o stretch (boolean)
28072      o }
28073  -opts (object) options for the pie
28074      o{
28075      o cut
28076      o startAngle (number)
28077      o endAngle (number)
28078      } 
28079  *
28080  * @constructor
28081  * Create a new Input
28082  * @param {Object} config The config object
28083  */
28084
28085 Roo.bootstrap.Graph = function(config){
28086     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28087     
28088     this.addEvents({
28089         // img events
28090         /**
28091          * @event click
28092          * The img click event for the img.
28093          * @param {Roo.EventObject} e
28094          */
28095         "click" : true
28096     });
28097 };
28098
28099 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28100     
28101     sm: 4,
28102     md: 5,
28103     graphtype: 'bar',
28104     g_height: 250,
28105     g_width: 400,
28106     g_x: 50,
28107     g_y: 50,
28108     g_r: 30,
28109     opts:{
28110         //g_colors: this.colors,
28111         g_type: 'soft',
28112         g_gutter: '20%'
28113
28114     },
28115     title : false,
28116
28117     getAutoCreate : function(){
28118         
28119         var cfg = {
28120             tag: 'div',
28121             html : null
28122         };
28123         
28124         
28125         return  cfg;
28126     },
28127
28128     onRender : function(ct,position){
28129         
28130         
28131         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28132         
28133         if (typeof(Raphael) == 'undefined') {
28134             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28135             return;
28136         }
28137         
28138         this.raphael = Raphael(this.el.dom);
28139         
28140                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28141                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28142                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28143                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28144                 /*
28145                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28146                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28147                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28148                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28149                 
28150                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28151                 r.barchart(330, 10, 300, 220, data1);
28152                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28153                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28154                 */
28155                 
28156                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28157                 // r.barchart(30, 30, 560, 250,  xdata, {
28158                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28159                 //     axis : "0 0 1 1",
28160                 //     axisxlabels :  xdata
28161                 //     //yvalues : cols,
28162                    
28163                 // });
28164 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28165 //        
28166 //        this.load(null,xdata,{
28167 //                axis : "0 0 1 1",
28168 //                axisxlabels :  xdata
28169 //                });
28170
28171     },
28172
28173     load : function(graphtype,xdata,opts)
28174     {
28175         this.raphael.clear();
28176         if(!graphtype) {
28177             graphtype = this.graphtype;
28178         }
28179         if(!opts){
28180             opts = this.opts;
28181         }
28182         var r = this.raphael,
28183             fin = function () {
28184                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28185             },
28186             fout = function () {
28187                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28188             },
28189             pfin = function() {
28190                 this.sector.stop();
28191                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28192
28193                 if (this.label) {
28194                     this.label[0].stop();
28195                     this.label[0].attr({ r: 7.5 });
28196                     this.label[1].attr({ "font-weight": 800 });
28197                 }
28198             },
28199             pfout = function() {
28200                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28201
28202                 if (this.label) {
28203                     this.label[0].animate({ r: 5 }, 500, "bounce");
28204                     this.label[1].attr({ "font-weight": 400 });
28205                 }
28206             };
28207
28208         switch(graphtype){
28209             case 'bar':
28210                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28211                 break;
28212             case 'hbar':
28213                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28214                 break;
28215             case 'pie':
28216 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28217 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28218 //            
28219                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28220                 
28221                 break;
28222
28223         }
28224         
28225         if(this.title){
28226             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28227         }
28228         
28229     },
28230     
28231     setTitle: function(o)
28232     {
28233         this.title = o;
28234     },
28235     
28236     initEvents: function() {
28237         
28238         if(!this.href){
28239             this.el.on('click', this.onClick, this);
28240         }
28241     },
28242     
28243     onClick : function(e)
28244     {
28245         Roo.log('img onclick');
28246         this.fireEvent('click', this, e);
28247     }
28248    
28249 });
28250
28251  
28252 /*
28253  * - LGPL
28254  *
28255  * numberBox
28256  * 
28257  */
28258 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28259
28260 /**
28261  * @class Roo.bootstrap.dash.NumberBox
28262  * @extends Roo.bootstrap.Component
28263  * Bootstrap NumberBox class
28264  * @cfg {String} headline Box headline
28265  * @cfg {String} content Box content
28266  * @cfg {String} icon Box icon
28267  * @cfg {String} footer Footer text
28268  * @cfg {String} fhref Footer href
28269  * 
28270  * @constructor
28271  * Create a new NumberBox
28272  * @param {Object} config The config object
28273  */
28274
28275
28276 Roo.bootstrap.dash.NumberBox = function(config){
28277     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28278     
28279 };
28280
28281 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28282     
28283     headline : '',
28284     content : '',
28285     icon : '',
28286     footer : '',
28287     fhref : '',
28288     ficon : '',
28289     
28290     getAutoCreate : function(){
28291         
28292         var cfg = {
28293             tag : 'div',
28294             cls : 'small-box ',
28295             cn : [
28296                 {
28297                     tag : 'div',
28298                     cls : 'inner',
28299                     cn :[
28300                         {
28301                             tag : 'h3',
28302                             cls : 'roo-headline',
28303                             html : this.headline
28304                         },
28305                         {
28306                             tag : 'p',
28307                             cls : 'roo-content',
28308                             html : this.content
28309                         }
28310                     ]
28311                 }
28312             ]
28313         };
28314         
28315         if(this.icon){
28316             cfg.cn.push({
28317                 tag : 'div',
28318                 cls : 'icon',
28319                 cn :[
28320                     {
28321                         tag : 'i',
28322                         cls : 'ion ' + this.icon
28323                     }
28324                 ]
28325             });
28326         }
28327         
28328         if(this.footer){
28329             var footer = {
28330                 tag : 'a',
28331                 cls : 'small-box-footer',
28332                 href : this.fhref || '#',
28333                 html : this.footer
28334             };
28335             
28336             cfg.cn.push(footer);
28337             
28338         }
28339         
28340         return  cfg;
28341     },
28342
28343     onRender : function(ct,position){
28344         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28345
28346
28347        
28348                 
28349     },
28350
28351     setHeadline: function (value)
28352     {
28353         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28354     },
28355     
28356     setFooter: function (value, href)
28357     {
28358         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28359         
28360         if(href){
28361             this.el.select('a.small-box-footer',true).first().attr('href', href);
28362         }
28363         
28364     },
28365
28366     setContent: function (value)
28367     {
28368         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28369     },
28370
28371     initEvents: function() 
28372     {   
28373         
28374     }
28375     
28376 });
28377
28378  
28379 /*
28380  * - LGPL
28381  *
28382  * TabBox
28383  * 
28384  */
28385 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28386
28387 /**
28388  * @class Roo.bootstrap.dash.TabBox
28389  * @extends Roo.bootstrap.Component
28390  * Bootstrap TabBox class
28391  * @cfg {String} title Title of the TabBox
28392  * @cfg {String} icon Icon of the TabBox
28393  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28394  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28395  * 
28396  * @constructor
28397  * Create a new TabBox
28398  * @param {Object} config The config object
28399  */
28400
28401
28402 Roo.bootstrap.dash.TabBox = function(config){
28403     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28404     this.addEvents({
28405         // raw events
28406         /**
28407          * @event addpane
28408          * When a pane is added
28409          * @param {Roo.bootstrap.dash.TabPane} pane
28410          */
28411         "addpane" : true,
28412         /**
28413          * @event activatepane
28414          * When a pane is activated
28415          * @param {Roo.bootstrap.dash.TabPane} pane
28416          */
28417         "activatepane" : true
28418         
28419          
28420     });
28421     
28422     this.panes = [];
28423 };
28424
28425 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28426
28427     title : '',
28428     icon : false,
28429     showtabs : true,
28430     tabScrollable : false,
28431     
28432     getChildContainer : function()
28433     {
28434         return this.el.select('.tab-content', true).first();
28435     },
28436     
28437     getAutoCreate : function(){
28438         
28439         var header = {
28440             tag: 'li',
28441             cls: 'pull-left header',
28442             html: this.title,
28443             cn : []
28444         };
28445         
28446         if(this.icon){
28447             header.cn.push({
28448                 tag: 'i',
28449                 cls: 'fa ' + this.icon
28450             });
28451         }
28452         
28453         var h = {
28454             tag: 'ul',
28455             cls: 'nav nav-tabs pull-right',
28456             cn: [
28457                 header
28458             ]
28459         };
28460         
28461         if(this.tabScrollable){
28462             h = {
28463                 tag: 'div',
28464                 cls: 'tab-header',
28465                 cn: [
28466                     {
28467                         tag: 'ul',
28468                         cls: 'nav nav-tabs pull-right',
28469                         cn: [
28470                             header
28471                         ]
28472                     }
28473                 ]
28474             };
28475         }
28476         
28477         var cfg = {
28478             tag: 'div',
28479             cls: 'nav-tabs-custom',
28480             cn: [
28481                 h,
28482                 {
28483                     tag: 'div',
28484                     cls: 'tab-content no-padding',
28485                     cn: []
28486                 }
28487             ]
28488         };
28489
28490         return  cfg;
28491     },
28492     initEvents : function()
28493     {
28494         //Roo.log('add add pane handler');
28495         this.on('addpane', this.onAddPane, this);
28496     },
28497      /**
28498      * Updates the box title
28499      * @param {String} html to set the title to.
28500      */
28501     setTitle : function(value)
28502     {
28503         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28504     },
28505     onAddPane : function(pane)
28506     {
28507         this.panes.push(pane);
28508         //Roo.log('addpane');
28509         //Roo.log(pane);
28510         // tabs are rendere left to right..
28511         if(!this.showtabs){
28512             return;
28513         }
28514         
28515         var ctr = this.el.select('.nav-tabs', true).first();
28516          
28517          
28518         var existing = ctr.select('.nav-tab',true);
28519         var qty = existing.getCount();;
28520         
28521         
28522         var tab = ctr.createChild({
28523             tag : 'li',
28524             cls : 'nav-tab' + (qty ? '' : ' active'),
28525             cn : [
28526                 {
28527                     tag : 'a',
28528                     href:'#',
28529                     html : pane.title
28530                 }
28531             ]
28532         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28533         pane.tab = tab;
28534         
28535         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28536         if (!qty) {
28537             pane.el.addClass('active');
28538         }
28539         
28540                 
28541     },
28542     onTabClick : function(ev,un,ob,pane)
28543     {
28544         //Roo.log('tab - prev default');
28545         ev.preventDefault();
28546         
28547         
28548         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28549         pane.tab.addClass('active');
28550         //Roo.log(pane.title);
28551         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28552         // technically we should have a deactivate event.. but maybe add later.
28553         // and it should not de-activate the selected tab...
28554         this.fireEvent('activatepane', pane);
28555         pane.el.addClass('active');
28556         pane.fireEvent('activate');
28557         
28558         
28559     },
28560     
28561     getActivePane : function()
28562     {
28563         var r = false;
28564         Roo.each(this.panes, function(p) {
28565             if(p.el.hasClass('active')){
28566                 r = p;
28567                 return false;
28568             }
28569             
28570             return;
28571         });
28572         
28573         return r;
28574     }
28575     
28576     
28577 });
28578
28579  
28580 /*
28581  * - LGPL
28582  *
28583  * Tab pane
28584  * 
28585  */
28586 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28587 /**
28588  * @class Roo.bootstrap.TabPane
28589  * @extends Roo.bootstrap.Component
28590  * Bootstrap TabPane class
28591  * @cfg {Boolean} active (false | true) Default false
28592  * @cfg {String} title title of panel
28593
28594  * 
28595  * @constructor
28596  * Create a new TabPane
28597  * @param {Object} config The config object
28598  */
28599
28600 Roo.bootstrap.dash.TabPane = function(config){
28601     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28602     
28603     this.addEvents({
28604         // raw events
28605         /**
28606          * @event activate
28607          * When a pane is activated
28608          * @param {Roo.bootstrap.dash.TabPane} pane
28609          */
28610         "activate" : true
28611          
28612     });
28613 };
28614
28615 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28616     
28617     active : false,
28618     title : '',
28619     
28620     // the tabBox that this is attached to.
28621     tab : false,
28622      
28623     getAutoCreate : function() 
28624     {
28625         var cfg = {
28626             tag: 'div',
28627             cls: 'tab-pane'
28628         };
28629         
28630         if(this.active){
28631             cfg.cls += ' active';
28632         }
28633         
28634         return cfg;
28635     },
28636     initEvents  : function()
28637     {
28638         //Roo.log('trigger add pane handler');
28639         this.parent().fireEvent('addpane', this)
28640     },
28641     
28642      /**
28643      * Updates the tab title 
28644      * @param {String} html to set the title to.
28645      */
28646     setTitle: function(str)
28647     {
28648         if (!this.tab) {
28649             return;
28650         }
28651         this.title = str;
28652         this.tab.select('a', true).first().dom.innerHTML = str;
28653         
28654     }
28655     
28656     
28657     
28658 });
28659
28660  
28661
28662
28663  /*
28664  * - LGPL
28665  *
28666  * menu
28667  * 
28668  */
28669 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28670
28671 /**
28672  * @class Roo.bootstrap.menu.Menu
28673  * @extends Roo.bootstrap.Component
28674  * Bootstrap Menu class - container for Menu
28675  * @cfg {String} html Text of the menu
28676  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28677  * @cfg {String} icon Font awesome icon
28678  * @cfg {String} pos Menu align to (top | bottom) default bottom
28679  * 
28680  * 
28681  * @constructor
28682  * Create a new Menu
28683  * @param {Object} config The config object
28684  */
28685
28686
28687 Roo.bootstrap.menu.Menu = function(config){
28688     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28689     
28690     this.addEvents({
28691         /**
28692          * @event beforeshow
28693          * Fires before this menu is displayed
28694          * @param {Roo.bootstrap.menu.Menu} this
28695          */
28696         beforeshow : true,
28697         /**
28698          * @event beforehide
28699          * Fires before this menu is hidden
28700          * @param {Roo.bootstrap.menu.Menu} this
28701          */
28702         beforehide : true,
28703         /**
28704          * @event show
28705          * Fires after this menu is displayed
28706          * @param {Roo.bootstrap.menu.Menu} this
28707          */
28708         show : true,
28709         /**
28710          * @event hide
28711          * Fires after this menu is hidden
28712          * @param {Roo.bootstrap.menu.Menu} this
28713          */
28714         hide : true,
28715         /**
28716          * @event click
28717          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28718          * @param {Roo.bootstrap.menu.Menu} this
28719          * @param {Roo.EventObject} e
28720          */
28721         click : true
28722     });
28723     
28724 };
28725
28726 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28727     
28728     submenu : false,
28729     html : '',
28730     weight : 'default',
28731     icon : false,
28732     pos : 'bottom',
28733     
28734     
28735     getChildContainer : function() {
28736         if(this.isSubMenu){
28737             return this.el;
28738         }
28739         
28740         return this.el.select('ul.dropdown-menu', true).first();  
28741     },
28742     
28743     getAutoCreate : function()
28744     {
28745         var text = [
28746             {
28747                 tag : 'span',
28748                 cls : 'roo-menu-text',
28749                 html : this.html
28750             }
28751         ];
28752         
28753         if(this.icon){
28754             text.unshift({
28755                 tag : 'i',
28756                 cls : 'fa ' + this.icon
28757             })
28758         }
28759         
28760         
28761         var cfg = {
28762             tag : 'div',
28763             cls : 'btn-group',
28764             cn : [
28765                 {
28766                     tag : 'button',
28767                     cls : 'dropdown-button btn btn-' + this.weight,
28768                     cn : text
28769                 },
28770                 {
28771                     tag : 'button',
28772                     cls : 'dropdown-toggle btn btn-' + this.weight,
28773                     cn : [
28774                         {
28775                             tag : 'span',
28776                             cls : 'caret'
28777                         }
28778                     ]
28779                 },
28780                 {
28781                     tag : 'ul',
28782                     cls : 'dropdown-menu'
28783                 }
28784             ]
28785             
28786         };
28787         
28788         if(this.pos == 'top'){
28789             cfg.cls += ' dropup';
28790         }
28791         
28792         if(this.isSubMenu){
28793             cfg = {
28794                 tag : 'ul',
28795                 cls : 'dropdown-menu'
28796             }
28797         }
28798         
28799         return cfg;
28800     },
28801     
28802     onRender : function(ct, position)
28803     {
28804         this.isSubMenu = ct.hasClass('dropdown-submenu');
28805         
28806         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28807     },
28808     
28809     initEvents : function() 
28810     {
28811         if(this.isSubMenu){
28812             return;
28813         }
28814         
28815         this.hidden = true;
28816         
28817         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28818         this.triggerEl.on('click', this.onTriggerPress, this);
28819         
28820         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28821         this.buttonEl.on('click', this.onClick, this);
28822         
28823     },
28824     
28825     list : function()
28826     {
28827         if(this.isSubMenu){
28828             return this.el;
28829         }
28830         
28831         return this.el.select('ul.dropdown-menu', true).first();
28832     },
28833     
28834     onClick : function(e)
28835     {
28836         this.fireEvent("click", this, e);
28837     },
28838     
28839     onTriggerPress  : function(e)
28840     {   
28841         if (this.isVisible()) {
28842             this.hide();
28843         } else {
28844             this.show();
28845         }
28846     },
28847     
28848     isVisible : function(){
28849         return !this.hidden;
28850     },
28851     
28852     show : function()
28853     {
28854         this.fireEvent("beforeshow", this);
28855         
28856         this.hidden = false;
28857         this.el.addClass('open');
28858         
28859         Roo.get(document).on("mouseup", this.onMouseUp, this);
28860         
28861         this.fireEvent("show", this);
28862         
28863         
28864     },
28865     
28866     hide : function()
28867     {
28868         this.fireEvent("beforehide", this);
28869         
28870         this.hidden = true;
28871         this.el.removeClass('open');
28872         
28873         Roo.get(document).un("mouseup", this.onMouseUp);
28874         
28875         this.fireEvent("hide", this);
28876     },
28877     
28878     onMouseUp : function()
28879     {
28880         this.hide();
28881     }
28882     
28883 });
28884
28885  
28886  /*
28887  * - LGPL
28888  *
28889  * menu item
28890  * 
28891  */
28892 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28893
28894 /**
28895  * @class Roo.bootstrap.menu.Item
28896  * @extends Roo.bootstrap.Component
28897  * Bootstrap MenuItem class
28898  * @cfg {Boolean} submenu (true | false) default false
28899  * @cfg {String} html text of the item
28900  * @cfg {String} href the link
28901  * @cfg {Boolean} disable (true | false) default false
28902  * @cfg {Boolean} preventDefault (true | false) default true
28903  * @cfg {String} icon Font awesome icon
28904  * @cfg {String} pos Submenu align to (left | right) default right 
28905  * 
28906  * 
28907  * @constructor
28908  * Create a new Item
28909  * @param {Object} config The config object
28910  */
28911
28912
28913 Roo.bootstrap.menu.Item = function(config){
28914     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28915     this.addEvents({
28916         /**
28917          * @event mouseover
28918          * Fires when the mouse is hovering over this menu
28919          * @param {Roo.bootstrap.menu.Item} this
28920          * @param {Roo.EventObject} e
28921          */
28922         mouseover : true,
28923         /**
28924          * @event mouseout
28925          * Fires when the mouse exits this menu
28926          * @param {Roo.bootstrap.menu.Item} this
28927          * @param {Roo.EventObject} e
28928          */
28929         mouseout : true,
28930         // raw events
28931         /**
28932          * @event click
28933          * The raw click event for the entire grid.
28934          * @param {Roo.EventObject} e
28935          */
28936         click : true
28937     });
28938 };
28939
28940 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28941     
28942     submenu : false,
28943     href : '',
28944     html : '',
28945     preventDefault: true,
28946     disable : false,
28947     icon : false,
28948     pos : 'right',
28949     
28950     getAutoCreate : function()
28951     {
28952         var text = [
28953             {
28954                 tag : 'span',
28955                 cls : 'roo-menu-item-text',
28956                 html : this.html
28957             }
28958         ];
28959         
28960         if(this.icon){
28961             text.unshift({
28962                 tag : 'i',
28963                 cls : 'fa ' + this.icon
28964             })
28965         }
28966         
28967         var cfg = {
28968             tag : 'li',
28969             cn : [
28970                 {
28971                     tag : 'a',
28972                     href : this.href || '#',
28973                     cn : text
28974                 }
28975             ]
28976         };
28977         
28978         if(this.disable){
28979             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28980         }
28981         
28982         if(this.submenu){
28983             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28984             
28985             if(this.pos == 'left'){
28986                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28987             }
28988         }
28989         
28990         return cfg;
28991     },
28992     
28993     initEvents : function() 
28994     {
28995         this.el.on('mouseover', this.onMouseOver, this);
28996         this.el.on('mouseout', this.onMouseOut, this);
28997         
28998         this.el.select('a', true).first().on('click', this.onClick, this);
28999         
29000     },
29001     
29002     onClick : function(e)
29003     {
29004         if(this.preventDefault){
29005             e.preventDefault();
29006         }
29007         
29008         this.fireEvent("click", this, e);
29009     },
29010     
29011     onMouseOver : function(e)
29012     {
29013         if(this.submenu && this.pos == 'left'){
29014             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29015         }
29016         
29017         this.fireEvent("mouseover", this, e);
29018     },
29019     
29020     onMouseOut : function(e)
29021     {
29022         this.fireEvent("mouseout", this, e);
29023     }
29024 });
29025
29026  
29027
29028  /*
29029  * - LGPL
29030  *
29031  * menu separator
29032  * 
29033  */
29034 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29035
29036 /**
29037  * @class Roo.bootstrap.menu.Separator
29038  * @extends Roo.bootstrap.Component
29039  * Bootstrap Separator class
29040  * 
29041  * @constructor
29042  * Create a new Separator
29043  * @param {Object} config The config object
29044  */
29045
29046
29047 Roo.bootstrap.menu.Separator = function(config){
29048     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29049 };
29050
29051 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29052     
29053     getAutoCreate : function(){
29054         var cfg = {
29055             tag : 'li',
29056             cls: 'dropdown-divider divider'
29057         };
29058         
29059         return cfg;
29060     }
29061    
29062 });
29063
29064  
29065
29066  /*
29067  * - LGPL
29068  *
29069  * Tooltip
29070  * 
29071  */
29072
29073 /**
29074  * @class Roo.bootstrap.Tooltip
29075  * Bootstrap Tooltip class
29076  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29077  * to determine which dom element triggers the tooltip.
29078  * 
29079  * It needs to add support for additional attributes like tooltip-position
29080  * 
29081  * @constructor
29082  * Create a new Toolti
29083  * @param {Object} config The config object
29084  */
29085
29086 Roo.bootstrap.Tooltip = function(config){
29087     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29088     
29089     this.alignment = Roo.bootstrap.Tooltip.alignment;
29090     
29091     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29092         this.alignment = config.alignment;
29093     }
29094     
29095 };
29096
29097 Roo.apply(Roo.bootstrap.Tooltip, {
29098     /**
29099      * @function init initialize tooltip monitoring.
29100      * @static
29101      */
29102     currentEl : false,
29103     currentTip : false,
29104     currentRegion : false,
29105     
29106     //  init : delay?
29107     
29108     init : function()
29109     {
29110         Roo.get(document).on('mouseover', this.enter ,this);
29111         Roo.get(document).on('mouseout', this.leave, this);
29112          
29113         
29114         this.currentTip = new Roo.bootstrap.Tooltip();
29115     },
29116     
29117     enter : function(ev)
29118     {
29119         var dom = ev.getTarget();
29120         
29121         //Roo.log(['enter',dom]);
29122         var el = Roo.fly(dom);
29123         if (this.currentEl) {
29124             //Roo.log(dom);
29125             //Roo.log(this.currentEl);
29126             //Roo.log(this.currentEl.contains(dom));
29127             if (this.currentEl == el) {
29128                 return;
29129             }
29130             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29131                 return;
29132             }
29133
29134         }
29135         
29136         if (this.currentTip.el) {
29137             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29138         }    
29139         //Roo.log(ev);
29140         
29141         if(!el || el.dom == document){
29142             return;
29143         }
29144         
29145         var bindEl = el; 
29146         var pel = false;
29147         if (!el.attr('tooltip')) {
29148             pel = el.findParent("[tooltip]");
29149             if (pel) {
29150                 bindEl = Roo.get(pel);
29151             }
29152         }
29153         
29154        
29155         
29156         // you can not look for children, as if el is the body.. then everythign is the child..
29157         if (!pel && !el.attr('tooltip')) { //
29158             if (!el.select("[tooltip]").elements.length) {
29159                 return;
29160             }
29161             // is the mouse over this child...?
29162             bindEl = el.select("[tooltip]").first();
29163             var xy = ev.getXY();
29164             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29165                 //Roo.log("not in region.");
29166                 return;
29167             }
29168             //Roo.log("child element over..");
29169             
29170         }
29171         this.currentEl = el;
29172         this.currentTip.bind(bindEl);
29173         this.currentRegion = Roo.lib.Region.getRegion(dom);
29174         this.currentTip.enter();
29175         
29176     },
29177     leave : function(ev)
29178     {
29179         var dom = ev.getTarget();
29180         //Roo.log(['leave',dom]);
29181         if (!this.currentEl) {
29182             return;
29183         }
29184         
29185         
29186         if (dom != this.currentEl.dom) {
29187             return;
29188         }
29189         var xy = ev.getXY();
29190         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29191             return;
29192         }
29193         // only activate leave if mouse cursor is outside... bounding box..
29194         
29195         
29196         
29197         
29198         if (this.currentTip) {
29199             this.currentTip.leave();
29200         }
29201         //Roo.log('clear currentEl');
29202         this.currentEl = false;
29203         
29204         
29205     },
29206     alignment : {
29207         'left' : ['r-l', [-2,0], 'right'],
29208         'right' : ['l-r', [2,0], 'left'],
29209         'bottom' : ['t-b', [0,2], 'top'],
29210         'top' : [ 'b-t', [0,-2], 'bottom']
29211     }
29212     
29213 });
29214
29215
29216 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29217     
29218     
29219     bindEl : false,
29220     
29221     delay : null, // can be { show : 300 , hide: 500}
29222     
29223     timeout : null,
29224     
29225     hoverState : null, //???
29226     
29227     placement : 'bottom', 
29228     
29229     alignment : false,
29230     
29231     getAutoCreate : function(){
29232     
29233         var cfg = {
29234            cls : 'tooltip',   
29235            role : 'tooltip',
29236            cn : [
29237                 {
29238                     cls : 'tooltip-arrow arrow'
29239                 },
29240                 {
29241                     cls : 'tooltip-inner'
29242                 }
29243            ]
29244         };
29245         
29246         return cfg;
29247     },
29248     bind : function(el)
29249     {
29250         this.bindEl = el;
29251     },
29252     
29253     initEvents : function()
29254     {
29255         this.arrowEl = this.el.select('.arrow', true).first();
29256         this.innerEl = this.el.select('.tooltip-inner', true).first();
29257     },
29258     
29259     enter : function () {
29260        
29261         if (this.timeout != null) {
29262             clearTimeout(this.timeout);
29263         }
29264         
29265         this.hoverState = 'in';
29266          //Roo.log("enter - show");
29267         if (!this.delay || !this.delay.show) {
29268             this.show();
29269             return;
29270         }
29271         var _t = this;
29272         this.timeout = setTimeout(function () {
29273             if (_t.hoverState == 'in') {
29274                 _t.show();
29275             }
29276         }, this.delay.show);
29277     },
29278     leave : function()
29279     {
29280         clearTimeout(this.timeout);
29281     
29282         this.hoverState = 'out';
29283          if (!this.delay || !this.delay.hide) {
29284             this.hide();
29285             return;
29286         }
29287        
29288         var _t = this;
29289         this.timeout = setTimeout(function () {
29290             //Roo.log("leave - timeout");
29291             
29292             if (_t.hoverState == 'out') {
29293                 _t.hide();
29294                 Roo.bootstrap.Tooltip.currentEl = false;
29295             }
29296         }, delay);
29297     },
29298     
29299     show : function (msg)
29300     {
29301         if (!this.el) {
29302             this.render(document.body);
29303         }
29304         // set content.
29305         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29306         
29307         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29308         
29309         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29310         
29311         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29312                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29313         
29314         var placement = typeof this.placement == 'function' ?
29315             this.placement.call(this, this.el, on_el) :
29316             this.placement;
29317             
29318         var autoToken = /\s?auto?\s?/i;
29319         var autoPlace = autoToken.test(placement);
29320         if (autoPlace) {
29321             placement = placement.replace(autoToken, '') || 'top';
29322         }
29323         
29324         //this.el.detach()
29325         //this.el.setXY([0,0]);
29326         this.el.show();
29327         //this.el.dom.style.display='block';
29328         
29329         //this.el.appendTo(on_el);
29330         
29331         var p = this.getPosition();
29332         var box = this.el.getBox();
29333         
29334         if (autoPlace) {
29335             // fixme..
29336         }
29337         
29338         var align = this.alignment[placement];
29339         
29340         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29341         
29342         if(placement == 'top' || placement == 'bottom'){
29343             if(xy[0] < 0){
29344                 placement = 'right';
29345             }
29346             
29347             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29348                 placement = 'left';
29349             }
29350             
29351             var scroll = Roo.select('body', true).first().getScroll();
29352             
29353             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29354                 placement = 'top';
29355             }
29356             
29357             align = this.alignment[placement];
29358             
29359             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29360             
29361         }
29362         
29363         var elems = document.getElementsByTagName('div');
29364         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29365         for (var i = 0; i < elems.length; i++) {
29366           var zindex = Number.parseInt(
29367                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29368                 10
29369           );
29370           if (zindex > highest) {
29371             highest = zindex;
29372           }
29373         }
29374         
29375         
29376         
29377         this.el.dom.style.zIndex = highest;
29378         
29379         this.el.alignTo(this.bindEl, align[0],align[1]);
29380         //var arrow = this.el.select('.arrow',true).first();
29381         //arrow.set(align[2], 
29382         
29383         this.el.addClass(placement);
29384         this.el.addClass("bs-tooltip-"+ placement);
29385         
29386         this.el.addClass('in fade show');
29387         
29388         this.hoverState = null;
29389         
29390         if (this.el.hasClass('fade')) {
29391             // fade it?
29392         }
29393         
29394         
29395         
29396         
29397         
29398     },
29399     hide : function()
29400     {
29401          
29402         if (!this.el) {
29403             return;
29404         }
29405         //this.el.setXY([0,0]);
29406         this.el.removeClass(['show', 'in']);
29407         //this.el.hide();
29408         
29409     }
29410     
29411 });
29412  
29413
29414  /*
29415  * - LGPL
29416  *
29417  * Location Picker
29418  * 
29419  */
29420
29421 /**
29422  * @class Roo.bootstrap.LocationPicker
29423  * @extends Roo.bootstrap.Component
29424  * Bootstrap LocationPicker class
29425  * @cfg {Number} latitude Position when init default 0
29426  * @cfg {Number} longitude Position when init default 0
29427  * @cfg {Number} zoom default 15
29428  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29429  * @cfg {Boolean} mapTypeControl default false
29430  * @cfg {Boolean} disableDoubleClickZoom default false
29431  * @cfg {Boolean} scrollwheel default true
29432  * @cfg {Boolean} streetViewControl default false
29433  * @cfg {Number} radius default 0
29434  * @cfg {String} locationName
29435  * @cfg {Boolean} draggable default true
29436  * @cfg {Boolean} enableAutocomplete default false
29437  * @cfg {Boolean} enableReverseGeocode default true
29438  * @cfg {String} markerTitle
29439  * 
29440  * @constructor
29441  * Create a new LocationPicker
29442  * @param {Object} config The config object
29443  */
29444
29445
29446 Roo.bootstrap.LocationPicker = function(config){
29447     
29448     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29449     
29450     this.addEvents({
29451         /**
29452          * @event initial
29453          * Fires when the picker initialized.
29454          * @param {Roo.bootstrap.LocationPicker} this
29455          * @param {Google Location} location
29456          */
29457         initial : true,
29458         /**
29459          * @event positionchanged
29460          * Fires when the picker position changed.
29461          * @param {Roo.bootstrap.LocationPicker} this
29462          * @param {Google Location} location
29463          */
29464         positionchanged : true,
29465         /**
29466          * @event resize
29467          * Fires when the map resize.
29468          * @param {Roo.bootstrap.LocationPicker} this
29469          */
29470         resize : true,
29471         /**
29472          * @event show
29473          * Fires when the map show.
29474          * @param {Roo.bootstrap.LocationPicker} this
29475          */
29476         show : true,
29477         /**
29478          * @event hide
29479          * Fires when the map hide.
29480          * @param {Roo.bootstrap.LocationPicker} this
29481          */
29482         hide : true,
29483         /**
29484          * @event mapClick
29485          * Fires when click the map.
29486          * @param {Roo.bootstrap.LocationPicker} this
29487          * @param {Map event} e
29488          */
29489         mapClick : true,
29490         /**
29491          * @event mapRightClick
29492          * Fires when right click the map.
29493          * @param {Roo.bootstrap.LocationPicker} this
29494          * @param {Map event} e
29495          */
29496         mapRightClick : true,
29497         /**
29498          * @event markerClick
29499          * Fires when click the marker.
29500          * @param {Roo.bootstrap.LocationPicker} this
29501          * @param {Map event} e
29502          */
29503         markerClick : true,
29504         /**
29505          * @event markerRightClick
29506          * Fires when right click the marker.
29507          * @param {Roo.bootstrap.LocationPicker} this
29508          * @param {Map event} e
29509          */
29510         markerRightClick : true,
29511         /**
29512          * @event OverlayViewDraw
29513          * Fires when OverlayView Draw
29514          * @param {Roo.bootstrap.LocationPicker} this
29515          */
29516         OverlayViewDraw : true,
29517         /**
29518          * @event OverlayViewOnAdd
29519          * Fires when OverlayView Draw
29520          * @param {Roo.bootstrap.LocationPicker} this
29521          */
29522         OverlayViewOnAdd : true,
29523         /**
29524          * @event OverlayViewOnRemove
29525          * Fires when OverlayView Draw
29526          * @param {Roo.bootstrap.LocationPicker} this
29527          */
29528         OverlayViewOnRemove : true,
29529         /**
29530          * @event OverlayViewShow
29531          * Fires when OverlayView Draw
29532          * @param {Roo.bootstrap.LocationPicker} this
29533          * @param {Pixel} cpx
29534          */
29535         OverlayViewShow : true,
29536         /**
29537          * @event OverlayViewHide
29538          * Fires when OverlayView Draw
29539          * @param {Roo.bootstrap.LocationPicker} this
29540          */
29541         OverlayViewHide : true,
29542         /**
29543          * @event loadexception
29544          * Fires when load google lib failed.
29545          * @param {Roo.bootstrap.LocationPicker} this
29546          */
29547         loadexception : true
29548     });
29549         
29550 };
29551
29552 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29553     
29554     gMapContext: false,
29555     
29556     latitude: 0,
29557     longitude: 0,
29558     zoom: 15,
29559     mapTypeId: false,
29560     mapTypeControl: false,
29561     disableDoubleClickZoom: false,
29562     scrollwheel: true,
29563     streetViewControl: false,
29564     radius: 0,
29565     locationName: '',
29566     draggable: true,
29567     enableAutocomplete: false,
29568     enableReverseGeocode: true,
29569     markerTitle: '',
29570     
29571     getAutoCreate: function()
29572     {
29573
29574         var cfg = {
29575             tag: 'div',
29576             cls: 'roo-location-picker'
29577         };
29578         
29579         return cfg
29580     },
29581     
29582     initEvents: function(ct, position)
29583     {       
29584         if(!this.el.getWidth() || this.isApplied()){
29585             return;
29586         }
29587         
29588         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29589         
29590         this.initial();
29591     },
29592     
29593     initial: function()
29594     {
29595         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29596             this.fireEvent('loadexception', this);
29597             return;
29598         }
29599         
29600         if(!this.mapTypeId){
29601             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29602         }
29603         
29604         this.gMapContext = this.GMapContext();
29605         
29606         this.initOverlayView();
29607         
29608         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29609         
29610         var _this = this;
29611                 
29612         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29613             _this.setPosition(_this.gMapContext.marker.position);
29614         });
29615         
29616         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29617             _this.fireEvent('mapClick', this, event);
29618             
29619         });
29620
29621         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29622             _this.fireEvent('mapRightClick', this, event);
29623             
29624         });
29625         
29626         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29627             _this.fireEvent('markerClick', this, event);
29628             
29629         });
29630
29631         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29632             _this.fireEvent('markerRightClick', this, event);
29633             
29634         });
29635         
29636         this.setPosition(this.gMapContext.location);
29637         
29638         this.fireEvent('initial', this, this.gMapContext.location);
29639     },
29640     
29641     initOverlayView: function()
29642     {
29643         var _this = this;
29644         
29645         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29646             
29647             draw: function()
29648             {
29649                 _this.fireEvent('OverlayViewDraw', _this);
29650             },
29651             
29652             onAdd: function()
29653             {
29654                 _this.fireEvent('OverlayViewOnAdd', _this);
29655             },
29656             
29657             onRemove: function()
29658             {
29659                 _this.fireEvent('OverlayViewOnRemove', _this);
29660             },
29661             
29662             show: function(cpx)
29663             {
29664                 _this.fireEvent('OverlayViewShow', _this, cpx);
29665             },
29666             
29667             hide: function()
29668             {
29669                 _this.fireEvent('OverlayViewHide', _this);
29670             }
29671             
29672         });
29673     },
29674     
29675     fromLatLngToContainerPixel: function(event)
29676     {
29677         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29678     },
29679     
29680     isApplied: function() 
29681     {
29682         return this.getGmapContext() == false ? false : true;
29683     },
29684     
29685     getGmapContext: function() 
29686     {
29687         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29688     },
29689     
29690     GMapContext: function() 
29691     {
29692         var position = new google.maps.LatLng(this.latitude, this.longitude);
29693         
29694         var _map = new google.maps.Map(this.el.dom, {
29695             center: position,
29696             zoom: this.zoom,
29697             mapTypeId: this.mapTypeId,
29698             mapTypeControl: this.mapTypeControl,
29699             disableDoubleClickZoom: this.disableDoubleClickZoom,
29700             scrollwheel: this.scrollwheel,
29701             streetViewControl: this.streetViewControl,
29702             locationName: this.locationName,
29703             draggable: this.draggable,
29704             enableAutocomplete: this.enableAutocomplete,
29705             enableReverseGeocode: this.enableReverseGeocode
29706         });
29707         
29708         var _marker = new google.maps.Marker({
29709             position: position,
29710             map: _map,
29711             title: this.markerTitle,
29712             draggable: this.draggable
29713         });
29714         
29715         return {
29716             map: _map,
29717             marker: _marker,
29718             circle: null,
29719             location: position,
29720             radius: this.radius,
29721             locationName: this.locationName,
29722             addressComponents: {
29723                 formatted_address: null,
29724                 addressLine1: null,
29725                 addressLine2: null,
29726                 streetName: null,
29727                 streetNumber: null,
29728                 city: null,
29729                 district: null,
29730                 state: null,
29731                 stateOrProvince: null
29732             },
29733             settings: this,
29734             domContainer: this.el.dom,
29735             geodecoder: new google.maps.Geocoder()
29736         };
29737     },
29738     
29739     drawCircle: function(center, radius, options) 
29740     {
29741         if (this.gMapContext.circle != null) {
29742             this.gMapContext.circle.setMap(null);
29743         }
29744         if (radius > 0) {
29745             radius *= 1;
29746             options = Roo.apply({}, options, {
29747                 strokeColor: "#0000FF",
29748                 strokeOpacity: .35,
29749                 strokeWeight: 2,
29750                 fillColor: "#0000FF",
29751                 fillOpacity: .2
29752             });
29753             
29754             options.map = this.gMapContext.map;
29755             options.radius = radius;
29756             options.center = center;
29757             this.gMapContext.circle = new google.maps.Circle(options);
29758             return this.gMapContext.circle;
29759         }
29760         
29761         return null;
29762     },
29763     
29764     setPosition: function(location) 
29765     {
29766         this.gMapContext.location = location;
29767         this.gMapContext.marker.setPosition(location);
29768         this.gMapContext.map.panTo(location);
29769         this.drawCircle(location, this.gMapContext.radius, {});
29770         
29771         var _this = this;
29772         
29773         if (this.gMapContext.settings.enableReverseGeocode) {
29774             this.gMapContext.geodecoder.geocode({
29775                 latLng: this.gMapContext.location
29776             }, function(results, status) {
29777                 
29778                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29779                     _this.gMapContext.locationName = results[0].formatted_address;
29780                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29781                     
29782                     _this.fireEvent('positionchanged', this, location);
29783                 }
29784             });
29785             
29786             return;
29787         }
29788         
29789         this.fireEvent('positionchanged', this, location);
29790     },
29791     
29792     resize: function()
29793     {
29794         google.maps.event.trigger(this.gMapContext.map, "resize");
29795         
29796         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29797         
29798         this.fireEvent('resize', this);
29799     },
29800     
29801     setPositionByLatLng: function(latitude, longitude)
29802     {
29803         this.setPosition(new google.maps.LatLng(latitude, longitude));
29804     },
29805     
29806     getCurrentPosition: function() 
29807     {
29808         return {
29809             latitude: this.gMapContext.location.lat(),
29810             longitude: this.gMapContext.location.lng()
29811         };
29812     },
29813     
29814     getAddressName: function() 
29815     {
29816         return this.gMapContext.locationName;
29817     },
29818     
29819     getAddressComponents: function() 
29820     {
29821         return this.gMapContext.addressComponents;
29822     },
29823     
29824     address_component_from_google_geocode: function(address_components) 
29825     {
29826         var result = {};
29827         
29828         for (var i = 0; i < address_components.length; i++) {
29829             var component = address_components[i];
29830             if (component.types.indexOf("postal_code") >= 0) {
29831                 result.postalCode = component.short_name;
29832             } else if (component.types.indexOf("street_number") >= 0) {
29833                 result.streetNumber = component.short_name;
29834             } else if (component.types.indexOf("route") >= 0) {
29835                 result.streetName = component.short_name;
29836             } else if (component.types.indexOf("neighborhood") >= 0) {
29837                 result.city = component.short_name;
29838             } else if (component.types.indexOf("locality") >= 0) {
29839                 result.city = component.short_name;
29840             } else if (component.types.indexOf("sublocality") >= 0) {
29841                 result.district = component.short_name;
29842             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29843                 result.stateOrProvince = component.short_name;
29844             } else if (component.types.indexOf("country") >= 0) {
29845                 result.country = component.short_name;
29846             }
29847         }
29848         
29849         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29850         result.addressLine2 = "";
29851         return result;
29852     },
29853     
29854     setZoomLevel: function(zoom)
29855     {
29856         this.gMapContext.map.setZoom(zoom);
29857     },
29858     
29859     show: function()
29860     {
29861         if(!this.el){
29862             return;
29863         }
29864         
29865         this.el.show();
29866         
29867         this.resize();
29868         
29869         this.fireEvent('show', this);
29870     },
29871     
29872     hide: function()
29873     {
29874         if(!this.el){
29875             return;
29876         }
29877         
29878         this.el.hide();
29879         
29880         this.fireEvent('hide', this);
29881     }
29882     
29883 });
29884
29885 Roo.apply(Roo.bootstrap.LocationPicker, {
29886     
29887     OverlayView : function(map, options)
29888     {
29889         options = options || {};
29890         
29891         this.setMap(map);
29892     }
29893     
29894     
29895 });/**
29896  * @class Roo.bootstrap.Alert
29897  * @extends Roo.bootstrap.Component
29898  * Bootstrap Alert class - shows an alert area box
29899  * eg
29900  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29901   Enter a valid email address
29902 </div>
29903  * @licence LGPL
29904  * @cfg {String} title The title of alert
29905  * @cfg {String} html The content of alert
29906  * @cfg {String} weight (success|info|warning|danger) Weight of the message
29907  * @cfg {String} fa font-awesomeicon
29908  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29909  * @cfg {Boolean} close true to show a x closer
29910  * 
29911  * 
29912  * @constructor
29913  * Create a new alert
29914  * @param {Object} config The config object
29915  */
29916
29917
29918 Roo.bootstrap.Alert = function(config){
29919     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29920     
29921 };
29922
29923 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29924     
29925     title: '',
29926     html: '',
29927     weight: false,
29928     fa: false,
29929     faicon: false, // BC
29930     close : false,
29931     
29932     
29933     getAutoCreate : function()
29934     {
29935         
29936         var cfg = {
29937             tag : 'div',
29938             cls : 'alert',
29939             cn : [
29940                 {
29941                     tag: 'button',
29942                     type :  "button",
29943                     cls: "close",
29944                     html : '×',
29945                     style : this.close ? '' : 'display:none'
29946                 },
29947                 {
29948                     tag : 'i',
29949                     cls : 'roo-alert-icon'
29950                     
29951                 },
29952                 {
29953                     tag : 'b',
29954                     cls : 'roo-alert-title',
29955                     html : this.title
29956                 },
29957                 {
29958                     tag : 'span',
29959                     cls : 'roo-alert-text',
29960                     html : this.html
29961                 }
29962             ]
29963         };
29964         
29965         if(this.faicon){
29966             cfg.cn[0].cls += ' fa ' + this.faicon;
29967         }
29968         if(this.fa){
29969             cfg.cn[0].cls += ' fa ' + this.fa;
29970         }
29971         
29972         if(this.weight){
29973             cfg.cls += ' alert-' + this.weight;
29974         }
29975         
29976         return cfg;
29977     },
29978     
29979     initEvents: function() 
29980     {
29981         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29982         this.titleEl =  this.el.select('.roo-alert-title',true).first();
29983         this.iconEl = this.el.select('.roo-alert-icon',true).first();
29984         this.htmlEl = this.el.select('.roo-alert-text',true).first();
29985         if (this.seconds > 0) {
29986             this.hide.defer(this.seconds, this);
29987         }
29988     },
29989     /**
29990      * Set the Title Message HTML
29991      * @param {String} html
29992      */
29993     setTitle : function(str)
29994     {
29995         this.titleEl.dom.innerHTML = str;
29996     },
29997      
29998      /**
29999      * Set the Body Message HTML
30000      * @param {String} html
30001      */
30002     setHtml : function(str)
30003     {
30004         this.htmlEl.dom.innerHTML = str;
30005     },
30006     /**
30007      * Set the Weight of the alert
30008      * @param {String} (success|info|warning|danger) weight
30009      */
30010     
30011     setWeight : function(weight)
30012     {
30013         if(this.weight){
30014             this.el.removeClass('alert-' + this.weight);
30015         }
30016         
30017         this.weight = weight;
30018         
30019         this.el.addClass('alert-' + this.weight);
30020     },
30021       /**
30022      * Set the Icon of the alert
30023      * @param {String} see fontawsome names (name without the 'fa-' bit)
30024      */
30025     setIcon : function(icon)
30026     {
30027         if(this.faicon){
30028             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30029         }
30030         
30031         this.faicon = icon;
30032         
30033         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30034     },
30035     /**
30036      * Hide the Alert
30037      */
30038     hide: function() 
30039     {
30040         this.el.hide();   
30041     },
30042     /**
30043      * Show the Alert
30044      */
30045     show: function() 
30046     {  
30047         this.el.show();   
30048     }
30049     
30050 });
30051
30052  
30053 /*
30054 * Licence: LGPL
30055 */
30056
30057 /**
30058  * @class Roo.bootstrap.UploadCropbox
30059  * @extends Roo.bootstrap.Component
30060  * Bootstrap UploadCropbox class
30061  * @cfg {String} emptyText show when image has been loaded
30062  * @cfg {String} rotateNotify show when image too small to rotate
30063  * @cfg {Number} errorTimeout default 3000
30064  * @cfg {Number} minWidth default 300
30065  * @cfg {Number} minHeight default 300
30066  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30067  * @cfg {Boolean} isDocument (true|false) default false
30068  * @cfg {String} url action url
30069  * @cfg {String} paramName default 'imageUpload'
30070  * @cfg {String} method default POST
30071  * @cfg {Boolean} loadMask (true|false) default true
30072  * @cfg {Boolean} loadingText default 'Loading...'
30073  * 
30074  * @constructor
30075  * Create a new UploadCropbox
30076  * @param {Object} config The config object
30077  */
30078
30079 Roo.bootstrap.UploadCropbox = function(config){
30080     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30081     
30082     this.addEvents({
30083         /**
30084          * @event beforeselectfile
30085          * Fire before select file
30086          * @param {Roo.bootstrap.UploadCropbox} this
30087          */
30088         "beforeselectfile" : true,
30089         /**
30090          * @event initial
30091          * Fire after initEvent
30092          * @param {Roo.bootstrap.UploadCropbox} this
30093          */
30094         "initial" : true,
30095         /**
30096          * @event crop
30097          * Fire after initEvent
30098          * @param {Roo.bootstrap.UploadCropbox} this
30099          * @param {String} data
30100          */
30101         "crop" : true,
30102         /**
30103          * @event prepare
30104          * Fire when preparing the file data
30105          * @param {Roo.bootstrap.UploadCropbox} this
30106          * @param {Object} file
30107          */
30108         "prepare" : true,
30109         /**
30110          * @event exception
30111          * Fire when get exception
30112          * @param {Roo.bootstrap.UploadCropbox} this
30113          * @param {XMLHttpRequest} xhr
30114          */
30115         "exception" : true,
30116         /**
30117          * @event beforeloadcanvas
30118          * Fire before load the canvas
30119          * @param {Roo.bootstrap.UploadCropbox} this
30120          * @param {String} src
30121          */
30122         "beforeloadcanvas" : true,
30123         /**
30124          * @event trash
30125          * Fire when trash image
30126          * @param {Roo.bootstrap.UploadCropbox} this
30127          */
30128         "trash" : true,
30129         /**
30130          * @event download
30131          * Fire when download the image
30132          * @param {Roo.bootstrap.UploadCropbox} this
30133          */
30134         "download" : true,
30135         /**
30136          * @event footerbuttonclick
30137          * Fire when footerbuttonclick
30138          * @param {Roo.bootstrap.UploadCropbox} this
30139          * @param {String} type
30140          */
30141         "footerbuttonclick" : true,
30142         /**
30143          * @event resize
30144          * Fire when resize
30145          * @param {Roo.bootstrap.UploadCropbox} this
30146          */
30147         "resize" : true,
30148         /**
30149          * @event rotate
30150          * Fire when rotate the image
30151          * @param {Roo.bootstrap.UploadCropbox} this
30152          * @param {String} pos
30153          */
30154         "rotate" : true,
30155         /**
30156          * @event inspect
30157          * Fire when inspect the file
30158          * @param {Roo.bootstrap.UploadCropbox} this
30159          * @param {Object} file
30160          */
30161         "inspect" : true,
30162         /**
30163          * @event upload
30164          * Fire when xhr upload the file
30165          * @param {Roo.bootstrap.UploadCropbox} this
30166          * @param {Object} data
30167          */
30168         "upload" : true,
30169         /**
30170          * @event arrange
30171          * Fire when arrange the file data
30172          * @param {Roo.bootstrap.UploadCropbox} this
30173          * @param {Object} formData
30174          */
30175         "arrange" : true
30176     });
30177     
30178     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30179 };
30180
30181 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30182     
30183     emptyText : 'Click to upload image',
30184     rotateNotify : 'Image is too small to rotate',
30185     errorTimeout : 3000,
30186     scale : 0,
30187     baseScale : 1,
30188     rotate : 0,
30189     dragable : false,
30190     pinching : false,
30191     mouseX : 0,
30192     mouseY : 0,
30193     cropData : false,
30194     minWidth : 300,
30195     minHeight : 300,
30196     file : false,
30197     exif : {},
30198     baseRotate : 1,
30199     cropType : 'image/jpeg',
30200     buttons : false,
30201     canvasLoaded : false,
30202     isDocument : false,
30203     method : 'POST',
30204     paramName : 'imageUpload',
30205     loadMask : true,
30206     loadingText : 'Loading...',
30207     maskEl : false,
30208     
30209     getAutoCreate : function()
30210     {
30211         var cfg = {
30212             tag : 'div',
30213             cls : 'roo-upload-cropbox',
30214             cn : [
30215                 {
30216                     tag : 'input',
30217                     cls : 'roo-upload-cropbox-selector',
30218                     type : 'file'
30219                 },
30220                 {
30221                     tag : 'div',
30222                     cls : 'roo-upload-cropbox-body',
30223                     style : 'cursor:pointer',
30224                     cn : [
30225                         {
30226                             tag : 'div',
30227                             cls : 'roo-upload-cropbox-preview'
30228                         },
30229                         {
30230                             tag : 'div',
30231                             cls : 'roo-upload-cropbox-thumb'
30232                         },
30233                         {
30234                             tag : 'div',
30235                             cls : 'roo-upload-cropbox-empty-notify',
30236                             html : this.emptyText
30237                         },
30238                         {
30239                             tag : 'div',
30240                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30241                             html : this.rotateNotify
30242                         }
30243                     ]
30244                 },
30245                 {
30246                     tag : 'div',
30247                     cls : 'roo-upload-cropbox-footer',
30248                     cn : {
30249                         tag : 'div',
30250                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30251                         cn : []
30252                     }
30253                 }
30254             ]
30255         };
30256         
30257         return cfg;
30258     },
30259     
30260     onRender : function(ct, position)
30261     {
30262         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30263         
30264         if (this.buttons.length) {
30265             
30266             Roo.each(this.buttons, function(bb) {
30267                 
30268                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30269                 
30270                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30271                 
30272             }, this);
30273         }
30274         
30275         if(this.loadMask){
30276             this.maskEl = this.el;
30277         }
30278     },
30279     
30280     initEvents : function()
30281     {
30282         this.urlAPI = (window.createObjectURL && window) || 
30283                                 (window.URL && URL.revokeObjectURL && URL) || 
30284                                 (window.webkitURL && webkitURL);
30285                         
30286         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30287         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30288         
30289         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30290         this.selectorEl.hide();
30291         
30292         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30293         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30294         
30295         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30296         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30297         this.thumbEl.hide();
30298         
30299         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30300         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30301         
30302         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30303         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30304         this.errorEl.hide();
30305         
30306         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30307         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30308         this.footerEl.hide();
30309         
30310         this.setThumbBoxSize();
30311         
30312         this.bind();
30313         
30314         this.resize();
30315         
30316         this.fireEvent('initial', this);
30317     },
30318
30319     bind : function()
30320     {
30321         var _this = this;
30322         
30323         window.addEventListener("resize", function() { _this.resize(); } );
30324         
30325         this.bodyEl.on('click', this.beforeSelectFile, this);
30326         
30327         if(Roo.isTouch){
30328             this.bodyEl.on('touchstart', this.onTouchStart, this);
30329             this.bodyEl.on('touchmove', this.onTouchMove, this);
30330             this.bodyEl.on('touchend', this.onTouchEnd, this);
30331         }
30332         
30333         if(!Roo.isTouch){
30334             this.bodyEl.on('mousedown', this.onMouseDown, this);
30335             this.bodyEl.on('mousemove', this.onMouseMove, this);
30336             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30337             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30338             Roo.get(document).on('mouseup', this.onMouseUp, this);
30339         }
30340         
30341         this.selectorEl.on('change', this.onFileSelected, this);
30342     },
30343     
30344     reset : function()
30345     {    
30346         this.scale = 0;
30347         this.baseScale = 1;
30348         this.rotate = 0;
30349         this.baseRotate = 1;
30350         this.dragable = false;
30351         this.pinching = false;
30352         this.mouseX = 0;
30353         this.mouseY = 0;
30354         this.cropData = false;
30355         this.notifyEl.dom.innerHTML = this.emptyText;
30356         
30357         this.selectorEl.dom.value = '';
30358         
30359     },
30360     
30361     resize : function()
30362     {
30363         if(this.fireEvent('resize', this) != false){
30364             this.setThumbBoxPosition();
30365             this.setCanvasPosition();
30366         }
30367     },
30368     
30369     onFooterButtonClick : function(e, el, o, type)
30370     {
30371         switch (type) {
30372             case 'rotate-left' :
30373                 this.onRotateLeft(e);
30374                 break;
30375             case 'rotate-right' :
30376                 this.onRotateRight(e);
30377                 break;
30378             case 'picture' :
30379                 this.beforeSelectFile(e);
30380                 break;
30381             case 'trash' :
30382                 this.trash(e);
30383                 break;
30384             case 'crop' :
30385                 this.crop(e);
30386                 break;
30387             case 'download' :
30388                 this.download(e);
30389                 break;
30390             default :
30391                 break;
30392         }
30393         
30394         this.fireEvent('footerbuttonclick', this, type);
30395     },
30396     
30397     beforeSelectFile : function(e)
30398     {
30399         e.preventDefault();
30400         
30401         if(this.fireEvent('beforeselectfile', this) != false){
30402             this.selectorEl.dom.click();
30403         }
30404     },
30405     
30406     onFileSelected : function(e)
30407     {
30408         e.preventDefault();
30409         
30410         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30411             return;
30412         }
30413         
30414         var file = this.selectorEl.dom.files[0];
30415         
30416         if(this.fireEvent('inspect', this, file) != false){
30417             this.prepare(file);
30418         }
30419         
30420     },
30421     
30422     trash : function(e)
30423     {
30424         this.fireEvent('trash', this);
30425     },
30426     
30427     download : function(e)
30428     {
30429         this.fireEvent('download', this);
30430     },
30431     
30432     loadCanvas : function(src)
30433     {   
30434         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30435             
30436             this.reset();
30437             
30438             this.imageEl = document.createElement('img');
30439             
30440             var _this = this;
30441             
30442             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30443             
30444             this.imageEl.src = src;
30445         }
30446     },
30447     
30448     onLoadCanvas : function()
30449     {   
30450         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30451         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30452         
30453         this.bodyEl.un('click', this.beforeSelectFile, this);
30454         
30455         this.notifyEl.hide();
30456         this.thumbEl.show();
30457         this.footerEl.show();
30458         
30459         this.baseRotateLevel();
30460         
30461         if(this.isDocument){
30462             this.setThumbBoxSize();
30463         }
30464         
30465         this.setThumbBoxPosition();
30466         
30467         this.baseScaleLevel();
30468         
30469         this.draw();
30470         
30471         this.resize();
30472         
30473         this.canvasLoaded = true;
30474         
30475         if(this.loadMask){
30476             this.maskEl.unmask();
30477         }
30478         
30479     },
30480     
30481     setCanvasPosition : function()
30482     {   
30483         if(!this.canvasEl){
30484             return;
30485         }
30486         
30487         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30488         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30489         
30490         this.previewEl.setLeft(pw);
30491         this.previewEl.setTop(ph);
30492         
30493     },
30494     
30495     onMouseDown : function(e)
30496     {   
30497         e.stopEvent();
30498         
30499         this.dragable = true;
30500         this.pinching = false;
30501         
30502         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30503             this.dragable = false;
30504             return;
30505         }
30506         
30507         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30508         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30509         
30510     },
30511     
30512     onMouseMove : function(e)
30513     {   
30514         e.stopEvent();
30515         
30516         if(!this.canvasLoaded){
30517             return;
30518         }
30519         
30520         if (!this.dragable){
30521             return;
30522         }
30523         
30524         var minX = Math.ceil(this.thumbEl.getLeft(true));
30525         var minY = Math.ceil(this.thumbEl.getTop(true));
30526         
30527         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30528         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30529         
30530         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30531         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30532         
30533         x = x - this.mouseX;
30534         y = y - this.mouseY;
30535         
30536         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30537         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30538         
30539         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30540         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30541         
30542         this.previewEl.setLeft(bgX);
30543         this.previewEl.setTop(bgY);
30544         
30545         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30546         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30547     },
30548     
30549     onMouseUp : function(e)
30550     {   
30551         e.stopEvent();
30552         
30553         this.dragable = false;
30554     },
30555     
30556     onMouseWheel : function(e)
30557     {   
30558         e.stopEvent();
30559         
30560         this.startScale = this.scale;
30561         
30562         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30563         
30564         if(!this.zoomable()){
30565             this.scale = this.startScale;
30566             return;
30567         }
30568         
30569         this.draw();
30570         
30571         return;
30572     },
30573     
30574     zoomable : function()
30575     {
30576         var minScale = this.thumbEl.getWidth() / this.minWidth;
30577         
30578         if(this.minWidth < this.minHeight){
30579             minScale = this.thumbEl.getHeight() / this.minHeight;
30580         }
30581         
30582         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30583         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30584         
30585         if(
30586                 this.isDocument &&
30587                 (this.rotate == 0 || this.rotate == 180) && 
30588                 (
30589                     width > this.imageEl.OriginWidth || 
30590                     height > this.imageEl.OriginHeight ||
30591                     (width < this.minWidth && height < this.minHeight)
30592                 )
30593         ){
30594             return false;
30595         }
30596         
30597         if(
30598                 this.isDocument &&
30599                 (this.rotate == 90 || this.rotate == 270) && 
30600                 (
30601                     width > this.imageEl.OriginWidth || 
30602                     height > this.imageEl.OriginHeight ||
30603                     (width < this.minHeight && height < this.minWidth)
30604                 )
30605         ){
30606             return false;
30607         }
30608         
30609         if(
30610                 !this.isDocument &&
30611                 (this.rotate == 0 || this.rotate == 180) && 
30612                 (
30613                     width < this.minWidth || 
30614                     width > this.imageEl.OriginWidth || 
30615                     height < this.minHeight || 
30616                     height > this.imageEl.OriginHeight
30617                 )
30618         ){
30619             return false;
30620         }
30621         
30622         if(
30623                 !this.isDocument &&
30624                 (this.rotate == 90 || this.rotate == 270) && 
30625                 (
30626                     width < this.minHeight || 
30627                     width > this.imageEl.OriginWidth || 
30628                     height < this.minWidth || 
30629                     height > this.imageEl.OriginHeight
30630                 )
30631         ){
30632             return false;
30633         }
30634         
30635         return true;
30636         
30637     },
30638     
30639     onRotateLeft : function(e)
30640     {   
30641         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30642             
30643             var minScale = this.thumbEl.getWidth() / this.minWidth;
30644             
30645             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30646             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30647             
30648             this.startScale = this.scale;
30649             
30650             while (this.getScaleLevel() < minScale){
30651             
30652                 this.scale = this.scale + 1;
30653                 
30654                 if(!this.zoomable()){
30655                     break;
30656                 }
30657                 
30658                 if(
30659                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30660                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30661                 ){
30662                     continue;
30663                 }
30664                 
30665                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30666
30667                 this.draw();
30668                 
30669                 return;
30670             }
30671             
30672             this.scale = this.startScale;
30673             
30674             this.onRotateFail();
30675             
30676             return false;
30677         }
30678         
30679         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30680
30681         if(this.isDocument){
30682             this.setThumbBoxSize();
30683             this.setThumbBoxPosition();
30684             this.setCanvasPosition();
30685         }
30686         
30687         this.draw();
30688         
30689         this.fireEvent('rotate', this, 'left');
30690         
30691     },
30692     
30693     onRotateRight : function(e)
30694     {
30695         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30696             
30697             var minScale = this.thumbEl.getWidth() / this.minWidth;
30698         
30699             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30700             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30701             
30702             this.startScale = this.scale;
30703             
30704             while (this.getScaleLevel() < minScale){
30705             
30706                 this.scale = this.scale + 1;
30707                 
30708                 if(!this.zoomable()){
30709                     break;
30710                 }
30711                 
30712                 if(
30713                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30714                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30715                 ){
30716                     continue;
30717                 }
30718                 
30719                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30720
30721                 this.draw();
30722                 
30723                 return;
30724             }
30725             
30726             this.scale = this.startScale;
30727             
30728             this.onRotateFail();
30729             
30730             return false;
30731         }
30732         
30733         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30734
30735         if(this.isDocument){
30736             this.setThumbBoxSize();
30737             this.setThumbBoxPosition();
30738             this.setCanvasPosition();
30739         }
30740         
30741         this.draw();
30742         
30743         this.fireEvent('rotate', this, 'right');
30744     },
30745     
30746     onRotateFail : function()
30747     {
30748         this.errorEl.show(true);
30749         
30750         var _this = this;
30751         
30752         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30753     },
30754     
30755     draw : function()
30756     {
30757         this.previewEl.dom.innerHTML = '';
30758         
30759         var canvasEl = document.createElement("canvas");
30760         
30761         var contextEl = canvasEl.getContext("2d");
30762         
30763         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30764         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30765         var center = this.imageEl.OriginWidth / 2;
30766         
30767         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30768             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30769             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30770             center = this.imageEl.OriginHeight / 2;
30771         }
30772         
30773         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30774         
30775         contextEl.translate(center, center);
30776         contextEl.rotate(this.rotate * Math.PI / 180);
30777
30778         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30779         
30780         this.canvasEl = document.createElement("canvas");
30781         
30782         this.contextEl = this.canvasEl.getContext("2d");
30783         
30784         switch (this.rotate) {
30785             case 0 :
30786                 
30787                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30788                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30789                 
30790                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30791                 
30792                 break;
30793             case 90 : 
30794                 
30795                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30796                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30797                 
30798                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30799                     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);
30800                     break;
30801                 }
30802                 
30803                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30804                 
30805                 break;
30806             case 180 :
30807                 
30808                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30809                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30810                 
30811                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30812                     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);
30813                     break;
30814                 }
30815                 
30816                 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);
30817                 
30818                 break;
30819             case 270 :
30820                 
30821                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30822                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30823         
30824                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30825                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30826                     break;
30827                 }
30828                 
30829                 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);
30830                 
30831                 break;
30832             default : 
30833                 break;
30834         }
30835         
30836         this.previewEl.appendChild(this.canvasEl);
30837         
30838         this.setCanvasPosition();
30839     },
30840     
30841     crop : function()
30842     {
30843         if(!this.canvasLoaded){
30844             return;
30845         }
30846         
30847         var imageCanvas = document.createElement("canvas");
30848         
30849         var imageContext = imageCanvas.getContext("2d");
30850         
30851         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30852         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30853         
30854         var center = imageCanvas.width / 2;
30855         
30856         imageContext.translate(center, center);
30857         
30858         imageContext.rotate(this.rotate * Math.PI / 180);
30859         
30860         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30861         
30862         var canvas = document.createElement("canvas");
30863         
30864         var context = canvas.getContext("2d");
30865                 
30866         canvas.width = this.minWidth;
30867         canvas.height = this.minHeight;
30868
30869         switch (this.rotate) {
30870             case 0 :
30871                 
30872                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30873                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30874                 
30875                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30876                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30877                 
30878                 var targetWidth = this.minWidth - 2 * x;
30879                 var targetHeight = this.minHeight - 2 * y;
30880                 
30881                 var scale = 1;
30882                 
30883                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30884                     scale = targetWidth / width;
30885                 }
30886                 
30887                 if(x > 0 && y == 0){
30888                     scale = targetHeight / height;
30889                 }
30890                 
30891                 if(x > 0 && y > 0){
30892                     scale = targetWidth / width;
30893                     
30894                     if(width < height){
30895                         scale = targetHeight / height;
30896                     }
30897                 }
30898                 
30899                 context.scale(scale, scale);
30900                 
30901                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30902                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30903
30904                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30905                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30906
30907                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30908                 
30909                 break;
30910             case 90 : 
30911                 
30912                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30913                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30914                 
30915                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30916                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30917                 
30918                 var targetWidth = this.minWidth - 2 * x;
30919                 var targetHeight = this.minHeight - 2 * y;
30920                 
30921                 var scale = 1;
30922                 
30923                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30924                     scale = targetWidth / width;
30925                 }
30926                 
30927                 if(x > 0 && y == 0){
30928                     scale = targetHeight / height;
30929                 }
30930                 
30931                 if(x > 0 && y > 0){
30932                     scale = targetWidth / width;
30933                     
30934                     if(width < height){
30935                         scale = targetHeight / height;
30936                     }
30937                 }
30938                 
30939                 context.scale(scale, scale);
30940                 
30941                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30942                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30943
30944                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30945                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30946                 
30947                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30948                 
30949                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30950                 
30951                 break;
30952             case 180 :
30953                 
30954                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30955                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30956                 
30957                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30958                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30959                 
30960                 var targetWidth = this.minWidth - 2 * x;
30961                 var targetHeight = this.minHeight - 2 * y;
30962                 
30963                 var scale = 1;
30964                 
30965                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30966                     scale = targetWidth / width;
30967                 }
30968                 
30969                 if(x > 0 && y == 0){
30970                     scale = targetHeight / height;
30971                 }
30972                 
30973                 if(x > 0 && y > 0){
30974                     scale = targetWidth / width;
30975                     
30976                     if(width < height){
30977                         scale = targetHeight / height;
30978                     }
30979                 }
30980                 
30981                 context.scale(scale, scale);
30982                 
30983                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30984                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30985
30986                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30987                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30988
30989                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30990                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30991                 
30992                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30993                 
30994                 break;
30995             case 270 :
30996                 
30997                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30998                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30999                 
31000                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31001                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31002                 
31003                 var targetWidth = this.minWidth - 2 * x;
31004                 var targetHeight = this.minHeight - 2 * y;
31005                 
31006                 var scale = 1;
31007                 
31008                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31009                     scale = targetWidth / width;
31010                 }
31011                 
31012                 if(x > 0 && y == 0){
31013                     scale = targetHeight / height;
31014                 }
31015                 
31016                 if(x > 0 && y > 0){
31017                     scale = targetWidth / width;
31018                     
31019                     if(width < height){
31020                         scale = targetHeight / height;
31021                     }
31022                 }
31023                 
31024                 context.scale(scale, scale);
31025                 
31026                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31027                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31028
31029                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31030                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31031                 
31032                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31033                 
31034                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31035                 
31036                 break;
31037             default : 
31038                 break;
31039         }
31040         
31041         this.cropData = canvas.toDataURL(this.cropType);
31042         
31043         if(this.fireEvent('crop', this, this.cropData) !== false){
31044             this.process(this.file, this.cropData);
31045         }
31046         
31047         return;
31048         
31049     },
31050     
31051     setThumbBoxSize : function()
31052     {
31053         var width, height;
31054         
31055         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31056             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31057             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31058             
31059             this.minWidth = width;
31060             this.minHeight = height;
31061             
31062             if(this.rotate == 90 || this.rotate == 270){
31063                 this.minWidth = height;
31064                 this.minHeight = width;
31065             }
31066         }
31067         
31068         height = 300;
31069         width = Math.ceil(this.minWidth * height / this.minHeight);
31070         
31071         if(this.minWidth > this.minHeight){
31072             width = 300;
31073             height = Math.ceil(this.minHeight * width / this.minWidth);
31074         }
31075         
31076         this.thumbEl.setStyle({
31077             width : width + 'px',
31078             height : height + 'px'
31079         });
31080
31081         return;
31082             
31083     },
31084     
31085     setThumbBoxPosition : function()
31086     {
31087         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31088         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31089         
31090         this.thumbEl.setLeft(x);
31091         this.thumbEl.setTop(y);
31092         
31093     },
31094     
31095     baseRotateLevel : function()
31096     {
31097         this.baseRotate = 1;
31098         
31099         if(
31100                 typeof(this.exif) != 'undefined' &&
31101                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31102                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31103         ){
31104             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31105         }
31106         
31107         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31108         
31109     },
31110     
31111     baseScaleLevel : function()
31112     {
31113         var width, height;
31114         
31115         if(this.isDocument){
31116             
31117             if(this.baseRotate == 6 || this.baseRotate == 8){
31118             
31119                 height = this.thumbEl.getHeight();
31120                 this.baseScale = height / this.imageEl.OriginWidth;
31121
31122                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31123                     width = this.thumbEl.getWidth();
31124                     this.baseScale = width / this.imageEl.OriginHeight;
31125                 }
31126
31127                 return;
31128             }
31129
31130             height = this.thumbEl.getHeight();
31131             this.baseScale = height / this.imageEl.OriginHeight;
31132
31133             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31134                 width = this.thumbEl.getWidth();
31135                 this.baseScale = width / this.imageEl.OriginWidth;
31136             }
31137
31138             return;
31139         }
31140         
31141         if(this.baseRotate == 6 || this.baseRotate == 8){
31142             
31143             width = this.thumbEl.getHeight();
31144             this.baseScale = width / this.imageEl.OriginHeight;
31145             
31146             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31147                 height = this.thumbEl.getWidth();
31148                 this.baseScale = height / this.imageEl.OriginHeight;
31149             }
31150             
31151             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31152                 height = this.thumbEl.getWidth();
31153                 this.baseScale = height / this.imageEl.OriginHeight;
31154                 
31155                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31156                     width = this.thumbEl.getHeight();
31157                     this.baseScale = width / this.imageEl.OriginWidth;
31158                 }
31159             }
31160             
31161             return;
31162         }
31163         
31164         width = this.thumbEl.getWidth();
31165         this.baseScale = width / this.imageEl.OriginWidth;
31166         
31167         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31168             height = this.thumbEl.getHeight();
31169             this.baseScale = height / this.imageEl.OriginHeight;
31170         }
31171         
31172         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31173             
31174             height = this.thumbEl.getHeight();
31175             this.baseScale = height / this.imageEl.OriginHeight;
31176             
31177             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31178                 width = this.thumbEl.getWidth();
31179                 this.baseScale = width / this.imageEl.OriginWidth;
31180             }
31181             
31182         }
31183         
31184         return;
31185     },
31186     
31187     getScaleLevel : function()
31188     {
31189         return this.baseScale * Math.pow(1.1, this.scale);
31190     },
31191     
31192     onTouchStart : function(e)
31193     {
31194         if(!this.canvasLoaded){
31195             this.beforeSelectFile(e);
31196             return;
31197         }
31198         
31199         var touches = e.browserEvent.touches;
31200         
31201         if(!touches){
31202             return;
31203         }
31204         
31205         if(touches.length == 1){
31206             this.onMouseDown(e);
31207             return;
31208         }
31209         
31210         if(touches.length != 2){
31211             return;
31212         }
31213         
31214         var coords = [];
31215         
31216         for(var i = 0, finger; finger = touches[i]; i++){
31217             coords.push(finger.pageX, finger.pageY);
31218         }
31219         
31220         var x = Math.pow(coords[0] - coords[2], 2);
31221         var y = Math.pow(coords[1] - coords[3], 2);
31222         
31223         this.startDistance = Math.sqrt(x + y);
31224         
31225         this.startScale = this.scale;
31226         
31227         this.pinching = true;
31228         this.dragable = false;
31229         
31230     },
31231     
31232     onTouchMove : function(e)
31233     {
31234         if(!this.pinching && !this.dragable){
31235             return;
31236         }
31237         
31238         var touches = e.browserEvent.touches;
31239         
31240         if(!touches){
31241             return;
31242         }
31243         
31244         if(this.dragable){
31245             this.onMouseMove(e);
31246             return;
31247         }
31248         
31249         var coords = [];
31250         
31251         for(var i = 0, finger; finger = touches[i]; i++){
31252             coords.push(finger.pageX, finger.pageY);
31253         }
31254         
31255         var x = Math.pow(coords[0] - coords[2], 2);
31256         var y = Math.pow(coords[1] - coords[3], 2);
31257         
31258         this.endDistance = Math.sqrt(x + y);
31259         
31260         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31261         
31262         if(!this.zoomable()){
31263             this.scale = this.startScale;
31264             return;
31265         }
31266         
31267         this.draw();
31268         
31269     },
31270     
31271     onTouchEnd : function(e)
31272     {
31273         this.pinching = false;
31274         this.dragable = false;
31275         
31276     },
31277     
31278     process : function(file, crop)
31279     {
31280         if(this.loadMask){
31281             this.maskEl.mask(this.loadingText);
31282         }
31283         
31284         this.xhr = new XMLHttpRequest();
31285         
31286         file.xhr = this.xhr;
31287
31288         this.xhr.open(this.method, this.url, true);
31289         
31290         var headers = {
31291             "Accept": "application/json",
31292             "Cache-Control": "no-cache",
31293             "X-Requested-With": "XMLHttpRequest"
31294         };
31295         
31296         for (var headerName in headers) {
31297             var headerValue = headers[headerName];
31298             if (headerValue) {
31299                 this.xhr.setRequestHeader(headerName, headerValue);
31300             }
31301         }
31302         
31303         var _this = this;
31304         
31305         this.xhr.onload = function()
31306         {
31307             _this.xhrOnLoad(_this.xhr);
31308         }
31309         
31310         this.xhr.onerror = function()
31311         {
31312             _this.xhrOnError(_this.xhr);
31313         }
31314         
31315         var formData = new FormData();
31316
31317         formData.append('returnHTML', 'NO');
31318         
31319         if(crop){
31320             formData.append('crop', crop);
31321         }
31322         
31323         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31324             formData.append(this.paramName, file, file.name);
31325         }
31326         
31327         if(typeof(file.filename) != 'undefined'){
31328             formData.append('filename', file.filename);
31329         }
31330         
31331         if(typeof(file.mimetype) != 'undefined'){
31332             formData.append('mimetype', file.mimetype);
31333         }
31334         
31335         if(this.fireEvent('arrange', this, formData) != false){
31336             this.xhr.send(formData);
31337         };
31338     },
31339     
31340     xhrOnLoad : function(xhr)
31341     {
31342         if(this.loadMask){
31343             this.maskEl.unmask();
31344         }
31345         
31346         if (xhr.readyState !== 4) {
31347             this.fireEvent('exception', this, xhr);
31348             return;
31349         }
31350
31351         var response = Roo.decode(xhr.responseText);
31352         
31353         if(!response.success){
31354             this.fireEvent('exception', this, xhr);
31355             return;
31356         }
31357         
31358         var response = Roo.decode(xhr.responseText);
31359         
31360         this.fireEvent('upload', this, response);
31361         
31362     },
31363     
31364     xhrOnError : function()
31365     {
31366         if(this.loadMask){
31367             this.maskEl.unmask();
31368         }
31369         
31370         Roo.log('xhr on error');
31371         
31372         var response = Roo.decode(xhr.responseText);
31373           
31374         Roo.log(response);
31375         
31376     },
31377     
31378     prepare : function(file)
31379     {   
31380         if(this.loadMask){
31381             this.maskEl.mask(this.loadingText);
31382         }
31383         
31384         this.file = false;
31385         this.exif = {};
31386         
31387         if(typeof(file) === 'string'){
31388             this.loadCanvas(file);
31389             return;
31390         }
31391         
31392         if(!file || !this.urlAPI){
31393             return;
31394         }
31395         
31396         this.file = file;
31397         this.cropType = file.type;
31398         
31399         var _this = this;
31400         
31401         if(this.fireEvent('prepare', this, this.file) != false){
31402             
31403             var reader = new FileReader();
31404             
31405             reader.onload = function (e) {
31406                 if (e.target.error) {
31407                     Roo.log(e.target.error);
31408                     return;
31409                 }
31410                 
31411                 var buffer = e.target.result,
31412                     dataView = new DataView(buffer),
31413                     offset = 2,
31414                     maxOffset = dataView.byteLength - 4,
31415                     markerBytes,
31416                     markerLength;
31417                 
31418                 if (dataView.getUint16(0) === 0xffd8) {
31419                     while (offset < maxOffset) {
31420                         markerBytes = dataView.getUint16(offset);
31421                         
31422                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31423                             markerLength = dataView.getUint16(offset + 2) + 2;
31424                             if (offset + markerLength > dataView.byteLength) {
31425                                 Roo.log('Invalid meta data: Invalid segment size.');
31426                                 break;
31427                             }
31428                             
31429                             if(markerBytes == 0xffe1){
31430                                 _this.parseExifData(
31431                                     dataView,
31432                                     offset,
31433                                     markerLength
31434                                 );
31435                             }
31436                             
31437                             offset += markerLength;
31438                             
31439                             continue;
31440                         }
31441                         
31442                         break;
31443                     }
31444                     
31445                 }
31446                 
31447                 var url = _this.urlAPI.createObjectURL(_this.file);
31448                 
31449                 _this.loadCanvas(url);
31450                 
31451                 return;
31452             }
31453             
31454             reader.readAsArrayBuffer(this.file);
31455             
31456         }
31457         
31458     },
31459     
31460     parseExifData : function(dataView, offset, length)
31461     {
31462         var tiffOffset = offset + 10,
31463             littleEndian,
31464             dirOffset;
31465     
31466         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31467             // No Exif data, might be XMP data instead
31468             return;
31469         }
31470         
31471         // Check for the ASCII code for "Exif" (0x45786966):
31472         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31473             // No Exif data, might be XMP data instead
31474             return;
31475         }
31476         if (tiffOffset + 8 > dataView.byteLength) {
31477             Roo.log('Invalid Exif data: Invalid segment size.');
31478             return;
31479         }
31480         // Check for the two null bytes:
31481         if (dataView.getUint16(offset + 8) !== 0x0000) {
31482             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31483             return;
31484         }
31485         // Check the byte alignment:
31486         switch (dataView.getUint16(tiffOffset)) {
31487         case 0x4949:
31488             littleEndian = true;
31489             break;
31490         case 0x4D4D:
31491             littleEndian = false;
31492             break;
31493         default:
31494             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31495             return;
31496         }
31497         // Check for the TIFF tag marker (0x002A):
31498         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31499             Roo.log('Invalid Exif data: Missing TIFF marker.');
31500             return;
31501         }
31502         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31503         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31504         
31505         this.parseExifTags(
31506             dataView,
31507             tiffOffset,
31508             tiffOffset + dirOffset,
31509             littleEndian
31510         );
31511     },
31512     
31513     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31514     {
31515         var tagsNumber,
31516             dirEndOffset,
31517             i;
31518         if (dirOffset + 6 > dataView.byteLength) {
31519             Roo.log('Invalid Exif data: Invalid directory offset.');
31520             return;
31521         }
31522         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31523         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31524         if (dirEndOffset + 4 > dataView.byteLength) {
31525             Roo.log('Invalid Exif data: Invalid directory size.');
31526             return;
31527         }
31528         for (i = 0; i < tagsNumber; i += 1) {
31529             this.parseExifTag(
31530                 dataView,
31531                 tiffOffset,
31532                 dirOffset + 2 + 12 * i, // tag offset
31533                 littleEndian
31534             );
31535         }
31536         // Return the offset to the next directory:
31537         return dataView.getUint32(dirEndOffset, littleEndian);
31538     },
31539     
31540     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31541     {
31542         var tag = dataView.getUint16(offset, littleEndian);
31543         
31544         this.exif[tag] = this.getExifValue(
31545             dataView,
31546             tiffOffset,
31547             offset,
31548             dataView.getUint16(offset + 2, littleEndian), // tag type
31549             dataView.getUint32(offset + 4, littleEndian), // tag length
31550             littleEndian
31551         );
31552     },
31553     
31554     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31555     {
31556         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31557             tagSize,
31558             dataOffset,
31559             values,
31560             i,
31561             str,
31562             c;
31563     
31564         if (!tagType) {
31565             Roo.log('Invalid Exif data: Invalid tag type.');
31566             return;
31567         }
31568         
31569         tagSize = tagType.size * length;
31570         // Determine if the value is contained in the dataOffset bytes,
31571         // or if the value at the dataOffset is a pointer to the actual data:
31572         dataOffset = tagSize > 4 ?
31573                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31574         if (dataOffset + tagSize > dataView.byteLength) {
31575             Roo.log('Invalid Exif data: Invalid data offset.');
31576             return;
31577         }
31578         if (length === 1) {
31579             return tagType.getValue(dataView, dataOffset, littleEndian);
31580         }
31581         values = [];
31582         for (i = 0; i < length; i += 1) {
31583             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31584         }
31585         
31586         if (tagType.ascii) {
31587             str = '';
31588             // Concatenate the chars:
31589             for (i = 0; i < values.length; i += 1) {
31590                 c = values[i];
31591                 // Ignore the terminating NULL byte(s):
31592                 if (c === '\u0000') {
31593                     break;
31594                 }
31595                 str += c;
31596             }
31597             return str;
31598         }
31599         return values;
31600     }
31601     
31602 });
31603
31604 Roo.apply(Roo.bootstrap.UploadCropbox, {
31605     tags : {
31606         'Orientation': 0x0112
31607     },
31608     
31609     Orientation: {
31610             1: 0, //'top-left',
31611 //            2: 'top-right',
31612             3: 180, //'bottom-right',
31613 //            4: 'bottom-left',
31614 //            5: 'left-top',
31615             6: 90, //'right-top',
31616 //            7: 'right-bottom',
31617             8: 270 //'left-bottom'
31618     },
31619     
31620     exifTagTypes : {
31621         // byte, 8-bit unsigned int:
31622         1: {
31623             getValue: function (dataView, dataOffset) {
31624                 return dataView.getUint8(dataOffset);
31625             },
31626             size: 1
31627         },
31628         // ascii, 8-bit byte:
31629         2: {
31630             getValue: function (dataView, dataOffset) {
31631                 return String.fromCharCode(dataView.getUint8(dataOffset));
31632             },
31633             size: 1,
31634             ascii: true
31635         },
31636         // short, 16 bit int:
31637         3: {
31638             getValue: function (dataView, dataOffset, littleEndian) {
31639                 return dataView.getUint16(dataOffset, littleEndian);
31640             },
31641             size: 2
31642         },
31643         // long, 32 bit int:
31644         4: {
31645             getValue: function (dataView, dataOffset, littleEndian) {
31646                 return dataView.getUint32(dataOffset, littleEndian);
31647             },
31648             size: 4
31649         },
31650         // rational = two long values, first is numerator, second is denominator:
31651         5: {
31652             getValue: function (dataView, dataOffset, littleEndian) {
31653                 return dataView.getUint32(dataOffset, littleEndian) /
31654                     dataView.getUint32(dataOffset + 4, littleEndian);
31655             },
31656             size: 8
31657         },
31658         // slong, 32 bit signed int:
31659         9: {
31660             getValue: function (dataView, dataOffset, littleEndian) {
31661                 return dataView.getInt32(dataOffset, littleEndian);
31662             },
31663             size: 4
31664         },
31665         // srational, two slongs, first is numerator, second is denominator:
31666         10: {
31667             getValue: function (dataView, dataOffset, littleEndian) {
31668                 return dataView.getInt32(dataOffset, littleEndian) /
31669                     dataView.getInt32(dataOffset + 4, littleEndian);
31670             },
31671             size: 8
31672         }
31673     },
31674     
31675     footer : {
31676         STANDARD : [
31677             {
31678                 tag : 'div',
31679                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31680                 action : 'rotate-left',
31681                 cn : [
31682                     {
31683                         tag : 'button',
31684                         cls : 'btn btn-default',
31685                         html : '<i class="fa fa-undo"></i>'
31686                     }
31687                 ]
31688             },
31689             {
31690                 tag : 'div',
31691                 cls : 'btn-group roo-upload-cropbox-picture',
31692                 action : 'picture',
31693                 cn : [
31694                     {
31695                         tag : 'button',
31696                         cls : 'btn btn-default',
31697                         html : '<i class="fa fa-picture-o"></i>'
31698                     }
31699                 ]
31700             },
31701             {
31702                 tag : 'div',
31703                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31704                 action : 'rotate-right',
31705                 cn : [
31706                     {
31707                         tag : 'button',
31708                         cls : 'btn btn-default',
31709                         html : '<i class="fa fa-repeat"></i>'
31710                     }
31711                 ]
31712             }
31713         ],
31714         DOCUMENT : [
31715             {
31716                 tag : 'div',
31717                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31718                 action : 'rotate-left',
31719                 cn : [
31720                     {
31721                         tag : 'button',
31722                         cls : 'btn btn-default',
31723                         html : '<i class="fa fa-undo"></i>'
31724                     }
31725                 ]
31726             },
31727             {
31728                 tag : 'div',
31729                 cls : 'btn-group roo-upload-cropbox-download',
31730                 action : 'download',
31731                 cn : [
31732                     {
31733                         tag : 'button',
31734                         cls : 'btn btn-default',
31735                         html : '<i class="fa fa-download"></i>'
31736                     }
31737                 ]
31738             },
31739             {
31740                 tag : 'div',
31741                 cls : 'btn-group roo-upload-cropbox-crop',
31742                 action : 'crop',
31743                 cn : [
31744                     {
31745                         tag : 'button',
31746                         cls : 'btn btn-default',
31747                         html : '<i class="fa fa-crop"></i>'
31748                     }
31749                 ]
31750             },
31751             {
31752                 tag : 'div',
31753                 cls : 'btn-group roo-upload-cropbox-trash',
31754                 action : 'trash',
31755                 cn : [
31756                     {
31757                         tag : 'button',
31758                         cls : 'btn btn-default',
31759                         html : '<i class="fa fa-trash"></i>'
31760                     }
31761                 ]
31762             },
31763             {
31764                 tag : 'div',
31765                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31766                 action : 'rotate-right',
31767                 cn : [
31768                     {
31769                         tag : 'button',
31770                         cls : 'btn btn-default',
31771                         html : '<i class="fa fa-repeat"></i>'
31772                     }
31773                 ]
31774             }
31775         ],
31776         ROTATOR : [
31777             {
31778                 tag : 'div',
31779                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31780                 action : 'rotate-left',
31781                 cn : [
31782                     {
31783                         tag : 'button',
31784                         cls : 'btn btn-default',
31785                         html : '<i class="fa fa-undo"></i>'
31786                     }
31787                 ]
31788             },
31789             {
31790                 tag : 'div',
31791                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31792                 action : 'rotate-right',
31793                 cn : [
31794                     {
31795                         tag : 'button',
31796                         cls : 'btn btn-default',
31797                         html : '<i class="fa fa-repeat"></i>'
31798                     }
31799                 ]
31800             }
31801         ]
31802     }
31803 });
31804
31805 /*
31806 * Licence: LGPL
31807 */
31808
31809 /**
31810  * @class Roo.bootstrap.DocumentManager
31811  * @extends Roo.bootstrap.Component
31812  * Bootstrap DocumentManager class
31813  * @cfg {String} paramName default 'imageUpload'
31814  * @cfg {String} toolTipName default 'filename'
31815  * @cfg {String} method default POST
31816  * @cfg {String} url action url
31817  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31818  * @cfg {Boolean} multiple multiple upload default true
31819  * @cfg {Number} thumbSize default 300
31820  * @cfg {String} fieldLabel
31821  * @cfg {Number} labelWidth default 4
31822  * @cfg {String} labelAlign (left|top) default left
31823  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31824 * @cfg {Number} labellg set the width of label (1-12)
31825  * @cfg {Number} labelmd set the width of label (1-12)
31826  * @cfg {Number} labelsm set the width of label (1-12)
31827  * @cfg {Number} labelxs set the width of label (1-12)
31828  * 
31829  * @constructor
31830  * Create a new DocumentManager
31831  * @param {Object} config The config object
31832  */
31833
31834 Roo.bootstrap.DocumentManager = function(config){
31835     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31836     
31837     this.files = [];
31838     this.delegates = [];
31839     
31840     this.addEvents({
31841         /**
31842          * @event initial
31843          * Fire when initial the DocumentManager
31844          * @param {Roo.bootstrap.DocumentManager} this
31845          */
31846         "initial" : true,
31847         /**
31848          * @event inspect
31849          * inspect selected file
31850          * @param {Roo.bootstrap.DocumentManager} this
31851          * @param {File} file
31852          */
31853         "inspect" : true,
31854         /**
31855          * @event exception
31856          * Fire when xhr load exception
31857          * @param {Roo.bootstrap.DocumentManager} this
31858          * @param {XMLHttpRequest} xhr
31859          */
31860         "exception" : true,
31861         /**
31862          * @event afterupload
31863          * Fire when xhr load exception
31864          * @param {Roo.bootstrap.DocumentManager} this
31865          * @param {XMLHttpRequest} xhr
31866          */
31867         "afterupload" : true,
31868         /**
31869          * @event prepare
31870          * prepare the form data
31871          * @param {Roo.bootstrap.DocumentManager} this
31872          * @param {Object} formData
31873          */
31874         "prepare" : true,
31875         /**
31876          * @event remove
31877          * Fire when remove the file
31878          * @param {Roo.bootstrap.DocumentManager} this
31879          * @param {Object} file
31880          */
31881         "remove" : true,
31882         /**
31883          * @event refresh
31884          * Fire after refresh the file
31885          * @param {Roo.bootstrap.DocumentManager} this
31886          */
31887         "refresh" : true,
31888         /**
31889          * @event click
31890          * Fire after click the image
31891          * @param {Roo.bootstrap.DocumentManager} this
31892          * @param {Object} file
31893          */
31894         "click" : true,
31895         /**
31896          * @event edit
31897          * Fire when upload a image and editable set to true
31898          * @param {Roo.bootstrap.DocumentManager} this
31899          * @param {Object} file
31900          */
31901         "edit" : true,
31902         /**
31903          * @event beforeselectfile
31904          * Fire before select file
31905          * @param {Roo.bootstrap.DocumentManager} this
31906          */
31907         "beforeselectfile" : true,
31908         /**
31909          * @event process
31910          * Fire before process file
31911          * @param {Roo.bootstrap.DocumentManager} this
31912          * @param {Object} file
31913          */
31914         "process" : true,
31915         /**
31916          * @event previewrendered
31917          * Fire when preview rendered
31918          * @param {Roo.bootstrap.DocumentManager} this
31919          * @param {Object} file
31920          */
31921         "previewrendered" : true,
31922         /**
31923          */
31924         "previewResize" : true
31925         
31926     });
31927 };
31928
31929 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31930     
31931     boxes : 0,
31932     inputName : '',
31933     thumbSize : 300,
31934     multiple : true,
31935     files : false,
31936     method : 'POST',
31937     url : '',
31938     paramName : 'imageUpload',
31939     toolTipName : 'filename',
31940     fieldLabel : '',
31941     labelWidth : 4,
31942     labelAlign : 'left',
31943     editable : true,
31944     delegates : false,
31945     xhr : false, 
31946     
31947     labellg : 0,
31948     labelmd : 0,
31949     labelsm : 0,
31950     labelxs : 0,
31951     
31952     getAutoCreate : function()
31953     {   
31954         var managerWidget = {
31955             tag : 'div',
31956             cls : 'roo-document-manager',
31957             cn : [
31958                 {
31959                     tag : 'input',
31960                     cls : 'roo-document-manager-selector',
31961                     type : 'file'
31962                 },
31963                 {
31964                     tag : 'div',
31965                     cls : 'roo-document-manager-uploader',
31966                     cn : [
31967                         {
31968                             tag : 'div',
31969                             cls : 'roo-document-manager-upload-btn',
31970                             html : '<i class="fa fa-plus"></i>'
31971                         }
31972                     ]
31973                     
31974                 }
31975             ]
31976         };
31977         
31978         var content = [
31979             {
31980                 tag : 'div',
31981                 cls : 'column col-md-12',
31982                 cn : managerWidget
31983             }
31984         ];
31985         
31986         if(this.fieldLabel.length){
31987             
31988             content = [
31989                 {
31990                     tag : 'div',
31991                     cls : 'column col-md-12',
31992                     html : this.fieldLabel
31993                 },
31994                 {
31995                     tag : 'div',
31996                     cls : 'column col-md-12',
31997                     cn : managerWidget
31998                 }
31999             ];
32000
32001             if(this.labelAlign == 'left'){
32002                 content = [
32003                     {
32004                         tag : 'div',
32005                         cls : 'column',
32006                         html : this.fieldLabel
32007                     },
32008                     {
32009                         tag : 'div',
32010                         cls : 'column',
32011                         cn : managerWidget
32012                     }
32013                 ];
32014                 
32015                 if(this.labelWidth > 12){
32016                     content[0].style = "width: " + this.labelWidth + 'px';
32017                 }
32018
32019                 if(this.labelWidth < 13 && this.labelmd == 0){
32020                     this.labelmd = this.labelWidth;
32021                 }
32022
32023                 if(this.labellg > 0){
32024                     content[0].cls += ' col-lg-' + this.labellg;
32025                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32026                 }
32027
32028                 if(this.labelmd > 0){
32029                     content[0].cls += ' col-md-' + this.labelmd;
32030                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32031                 }
32032
32033                 if(this.labelsm > 0){
32034                     content[0].cls += ' col-sm-' + this.labelsm;
32035                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32036                 }
32037
32038                 if(this.labelxs > 0){
32039                     content[0].cls += ' col-xs-' + this.labelxs;
32040                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32041                 }
32042                 
32043             }
32044         }
32045         
32046         var cfg = {
32047             tag : 'div',
32048             cls : 'row clearfix',
32049             cn : content
32050         };
32051         
32052         return cfg;
32053         
32054     },
32055     
32056     initEvents : function()
32057     {
32058         this.managerEl = this.el.select('.roo-document-manager', true).first();
32059         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32060         
32061         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32062         this.selectorEl.hide();
32063         
32064         if(this.multiple){
32065             this.selectorEl.attr('multiple', 'multiple');
32066         }
32067         
32068         this.selectorEl.on('change', this.onFileSelected, this);
32069         
32070         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32071         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32072         
32073         this.uploader.on('click', this.onUploaderClick, this);
32074         
32075         this.renderProgressDialog();
32076         
32077         var _this = this;
32078         
32079         window.addEventListener("resize", function() { _this.refresh(); } );
32080         
32081         this.fireEvent('initial', this);
32082     },
32083     
32084     renderProgressDialog : function()
32085     {
32086         var _this = this;
32087         
32088         this.progressDialog = new Roo.bootstrap.Modal({
32089             cls : 'roo-document-manager-progress-dialog',
32090             allow_close : false,
32091             animate : false,
32092             title : '',
32093             buttons : [
32094                 {
32095                     name  :'cancel',
32096                     weight : 'danger',
32097                     html : 'Cancel'
32098                 }
32099             ], 
32100             listeners : { 
32101                 btnclick : function() {
32102                     _this.uploadCancel();
32103                     this.hide();
32104                 }
32105             }
32106         });
32107          
32108         this.progressDialog.render(Roo.get(document.body));
32109          
32110         this.progress = new Roo.bootstrap.Progress({
32111             cls : 'roo-document-manager-progress',
32112             active : true,
32113             striped : true
32114         });
32115         
32116         this.progress.render(this.progressDialog.getChildContainer());
32117         
32118         this.progressBar = new Roo.bootstrap.ProgressBar({
32119             cls : 'roo-document-manager-progress-bar',
32120             aria_valuenow : 0,
32121             aria_valuemin : 0,
32122             aria_valuemax : 12,
32123             panel : 'success'
32124         });
32125         
32126         this.progressBar.render(this.progress.getChildContainer());
32127     },
32128     
32129     onUploaderClick : function(e)
32130     {
32131         e.preventDefault();
32132      
32133         if(this.fireEvent('beforeselectfile', this) != false){
32134             this.selectorEl.dom.click();
32135         }
32136         
32137     },
32138     
32139     onFileSelected : function(e)
32140     {
32141         e.preventDefault();
32142         
32143         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32144             return;
32145         }
32146         
32147         Roo.each(this.selectorEl.dom.files, function(file){
32148             if(this.fireEvent('inspect', this, file) != false){
32149                 this.files.push(file);
32150             }
32151         }, this);
32152         
32153         this.queue();
32154         
32155     },
32156     
32157     queue : function()
32158     {
32159         this.selectorEl.dom.value = '';
32160         
32161         if(!this.files || !this.files.length){
32162             return;
32163         }
32164         
32165         if(this.boxes > 0 && this.files.length > this.boxes){
32166             this.files = this.files.slice(0, this.boxes);
32167         }
32168         
32169         this.uploader.show();
32170         
32171         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32172             this.uploader.hide();
32173         }
32174         
32175         var _this = this;
32176         
32177         var files = [];
32178         
32179         var docs = [];
32180         
32181         Roo.each(this.files, function(file){
32182             
32183             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32184                 var f = this.renderPreview(file);
32185                 files.push(f);
32186                 return;
32187             }
32188             
32189             if(file.type.indexOf('image') != -1){
32190                 this.delegates.push(
32191                     (function(){
32192                         _this.process(file);
32193                     }).createDelegate(this)
32194                 );
32195         
32196                 return;
32197             }
32198             
32199             docs.push(
32200                 (function(){
32201                     _this.process(file);
32202                 }).createDelegate(this)
32203             );
32204             
32205         }, this);
32206         
32207         this.files = files;
32208         
32209         this.delegates = this.delegates.concat(docs);
32210         
32211         if(!this.delegates.length){
32212             this.refresh();
32213             return;
32214         }
32215         
32216         this.progressBar.aria_valuemax = this.delegates.length;
32217         
32218         this.arrange();
32219         
32220         return;
32221     },
32222     
32223     arrange : function()
32224     {
32225         if(!this.delegates.length){
32226             this.progressDialog.hide();
32227             this.refresh();
32228             return;
32229         }
32230         
32231         var delegate = this.delegates.shift();
32232         
32233         this.progressDialog.show();
32234         
32235         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32236         
32237         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32238         
32239         delegate();
32240     },
32241     
32242     refresh : function()
32243     {
32244         this.uploader.show();
32245         
32246         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32247             this.uploader.hide();
32248         }
32249         
32250         Roo.isTouch ? this.closable(false) : this.closable(true);
32251         
32252         this.fireEvent('refresh', this);
32253     },
32254     
32255     onRemove : function(e, el, o)
32256     {
32257         e.preventDefault();
32258         
32259         this.fireEvent('remove', this, o);
32260         
32261     },
32262     
32263     remove : function(o)
32264     {
32265         var files = [];
32266         
32267         Roo.each(this.files, function(file){
32268             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32269                 files.push(file);
32270                 return;
32271             }
32272
32273             o.target.remove();
32274
32275         }, this);
32276         
32277         this.files = files;
32278         
32279         this.refresh();
32280     },
32281     
32282     clear : function()
32283     {
32284         Roo.each(this.files, function(file){
32285             if(!file.target){
32286                 return;
32287             }
32288             
32289             file.target.remove();
32290
32291         }, this);
32292         
32293         this.files = [];
32294         
32295         this.refresh();
32296     },
32297     
32298     onClick : function(e, el, o)
32299     {
32300         e.preventDefault();
32301         
32302         this.fireEvent('click', this, o);
32303         
32304     },
32305     
32306     closable : function(closable)
32307     {
32308         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32309             
32310             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32311             
32312             if(closable){
32313                 el.show();
32314                 return;
32315             }
32316             
32317             el.hide();
32318             
32319         }, this);
32320     },
32321     
32322     xhrOnLoad : function(xhr)
32323     {
32324         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32325             el.remove();
32326         }, this);
32327         
32328         if (xhr.readyState !== 4) {
32329             this.arrange();
32330             this.fireEvent('exception', this, xhr);
32331             return;
32332         }
32333
32334         var response = Roo.decode(xhr.responseText);
32335         
32336         if(!response.success){
32337             this.arrange();
32338             this.fireEvent('exception', this, xhr);
32339             return;
32340         }
32341         
32342         var file = this.renderPreview(response.data);
32343         
32344         this.files.push(file);
32345         
32346         this.arrange();
32347         
32348         this.fireEvent('afterupload', this, xhr);
32349         
32350     },
32351     
32352     xhrOnError : function(xhr)
32353     {
32354         Roo.log('xhr on error');
32355         
32356         var response = Roo.decode(xhr.responseText);
32357           
32358         Roo.log(response);
32359         
32360         this.arrange();
32361     },
32362     
32363     process : function(file)
32364     {
32365         if(this.fireEvent('process', this, file) !== false){
32366             if(this.editable && file.type.indexOf('image') != -1){
32367                 this.fireEvent('edit', this, file);
32368                 return;
32369             }
32370
32371             this.uploadStart(file, false);
32372
32373             return;
32374         }
32375         
32376     },
32377     
32378     uploadStart : function(file, crop)
32379     {
32380         this.xhr = new XMLHttpRequest();
32381         
32382         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32383             this.arrange();
32384             return;
32385         }
32386         
32387         file.xhr = this.xhr;
32388             
32389         this.managerEl.createChild({
32390             tag : 'div',
32391             cls : 'roo-document-manager-loading',
32392             cn : [
32393                 {
32394                     tag : 'div',
32395                     tooltip : file.name,
32396                     cls : 'roo-document-manager-thumb',
32397                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32398                 }
32399             ]
32400
32401         });
32402
32403         this.xhr.open(this.method, this.url, true);
32404         
32405         var headers = {
32406             "Accept": "application/json",
32407             "Cache-Control": "no-cache",
32408             "X-Requested-With": "XMLHttpRequest"
32409         };
32410         
32411         for (var headerName in headers) {
32412             var headerValue = headers[headerName];
32413             if (headerValue) {
32414                 this.xhr.setRequestHeader(headerName, headerValue);
32415             }
32416         }
32417         
32418         var _this = this;
32419         
32420         this.xhr.onload = function()
32421         {
32422             _this.xhrOnLoad(_this.xhr);
32423         }
32424         
32425         this.xhr.onerror = function()
32426         {
32427             _this.xhrOnError(_this.xhr);
32428         }
32429         
32430         var formData = new FormData();
32431
32432         formData.append('returnHTML', 'NO');
32433         
32434         if(crop){
32435             formData.append('crop', crop);
32436         }
32437         
32438         formData.append(this.paramName, file, file.name);
32439         
32440         var options = {
32441             file : file, 
32442             manually : false
32443         };
32444         
32445         if(this.fireEvent('prepare', this, formData, options) != false){
32446             
32447             if(options.manually){
32448                 return;
32449             }
32450             
32451             this.xhr.send(formData);
32452             return;
32453         };
32454         
32455         this.uploadCancel();
32456     },
32457     
32458     uploadCancel : function()
32459     {
32460         if (this.xhr) {
32461             this.xhr.abort();
32462         }
32463         
32464         this.delegates = [];
32465         
32466         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32467             el.remove();
32468         }, this);
32469         
32470         this.arrange();
32471     },
32472     
32473     renderPreview : function(file)
32474     {
32475         if(typeof(file.target) != 'undefined' && file.target){
32476             return file;
32477         }
32478         
32479         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32480         
32481         var previewEl = this.managerEl.createChild({
32482             tag : 'div',
32483             cls : 'roo-document-manager-preview',
32484             cn : [
32485                 {
32486                     tag : 'div',
32487                     tooltip : file[this.toolTipName],
32488                     cls : 'roo-document-manager-thumb',
32489                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32490                 },
32491                 {
32492                     tag : 'button',
32493                     cls : 'close',
32494                     html : '<i class="fa fa-times-circle"></i>'
32495                 }
32496             ]
32497         });
32498
32499         var close = previewEl.select('button.close', true).first();
32500
32501         close.on('click', this.onRemove, this, file);
32502
32503         file.target = previewEl;
32504
32505         var image = previewEl.select('img', true).first();
32506         
32507         var _this = this;
32508         
32509         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32510         
32511         image.on('click', this.onClick, this, file);
32512         
32513         this.fireEvent('previewrendered', this, file);
32514         
32515         return file;
32516         
32517     },
32518     
32519     onPreviewLoad : function(file, image)
32520     {
32521         if(typeof(file.target) == 'undefined' || !file.target){
32522             return;
32523         }
32524         
32525         var width = image.dom.naturalWidth || image.dom.width;
32526         var height = image.dom.naturalHeight || image.dom.height;
32527         
32528         if(!this.previewResize) {
32529             return;
32530         }
32531         
32532         if(width > height){
32533             file.target.addClass('wide');
32534             return;
32535         }
32536         
32537         file.target.addClass('tall');
32538         return;
32539         
32540     },
32541     
32542     uploadFromSource : function(file, crop)
32543     {
32544         this.xhr = new XMLHttpRequest();
32545         
32546         this.managerEl.createChild({
32547             tag : 'div',
32548             cls : 'roo-document-manager-loading',
32549             cn : [
32550                 {
32551                     tag : 'div',
32552                     tooltip : file.name,
32553                     cls : 'roo-document-manager-thumb',
32554                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32555                 }
32556             ]
32557
32558         });
32559
32560         this.xhr.open(this.method, this.url, true);
32561         
32562         var headers = {
32563             "Accept": "application/json",
32564             "Cache-Control": "no-cache",
32565             "X-Requested-With": "XMLHttpRequest"
32566         };
32567         
32568         for (var headerName in headers) {
32569             var headerValue = headers[headerName];
32570             if (headerValue) {
32571                 this.xhr.setRequestHeader(headerName, headerValue);
32572             }
32573         }
32574         
32575         var _this = this;
32576         
32577         this.xhr.onload = function()
32578         {
32579             _this.xhrOnLoad(_this.xhr);
32580         }
32581         
32582         this.xhr.onerror = function()
32583         {
32584             _this.xhrOnError(_this.xhr);
32585         }
32586         
32587         var formData = new FormData();
32588
32589         formData.append('returnHTML', 'NO');
32590         
32591         formData.append('crop', crop);
32592         
32593         if(typeof(file.filename) != 'undefined'){
32594             formData.append('filename', file.filename);
32595         }
32596         
32597         if(typeof(file.mimetype) != 'undefined'){
32598             formData.append('mimetype', file.mimetype);
32599         }
32600         
32601         Roo.log(formData);
32602         
32603         if(this.fireEvent('prepare', this, formData) != false){
32604             this.xhr.send(formData);
32605         };
32606     }
32607 });
32608
32609 /*
32610 * Licence: LGPL
32611 */
32612
32613 /**
32614  * @class Roo.bootstrap.DocumentViewer
32615  * @extends Roo.bootstrap.Component
32616  * Bootstrap DocumentViewer class
32617  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32618  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32619  * 
32620  * @constructor
32621  * Create a new DocumentViewer
32622  * @param {Object} config The config object
32623  */
32624
32625 Roo.bootstrap.DocumentViewer = function(config){
32626     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32627     
32628     this.addEvents({
32629         /**
32630          * @event initial
32631          * Fire after initEvent
32632          * @param {Roo.bootstrap.DocumentViewer} this
32633          */
32634         "initial" : true,
32635         /**
32636          * @event click
32637          * Fire after click
32638          * @param {Roo.bootstrap.DocumentViewer} this
32639          */
32640         "click" : true,
32641         /**
32642          * @event download
32643          * Fire after download button
32644          * @param {Roo.bootstrap.DocumentViewer} this
32645          */
32646         "download" : true,
32647         /**
32648          * @event trash
32649          * Fire after trash button
32650          * @param {Roo.bootstrap.DocumentViewer} this
32651          */
32652         "trash" : true
32653         
32654     });
32655 };
32656
32657 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32658     
32659     showDownload : true,
32660     
32661     showTrash : true,
32662     
32663     getAutoCreate : function()
32664     {
32665         var cfg = {
32666             tag : 'div',
32667             cls : 'roo-document-viewer',
32668             cn : [
32669                 {
32670                     tag : 'div',
32671                     cls : 'roo-document-viewer-body',
32672                     cn : [
32673                         {
32674                             tag : 'div',
32675                             cls : 'roo-document-viewer-thumb',
32676                             cn : [
32677                                 {
32678                                     tag : 'img',
32679                                     cls : 'roo-document-viewer-image'
32680                                 }
32681                             ]
32682                         }
32683                     ]
32684                 },
32685                 {
32686                     tag : 'div',
32687                     cls : 'roo-document-viewer-footer',
32688                     cn : {
32689                         tag : 'div',
32690                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32691                         cn : [
32692                             {
32693                                 tag : 'div',
32694                                 cls : 'btn-group roo-document-viewer-download',
32695                                 cn : [
32696                                     {
32697                                         tag : 'button',
32698                                         cls : 'btn btn-default',
32699                                         html : '<i class="fa fa-download"></i>'
32700                                     }
32701                                 ]
32702                             },
32703                             {
32704                                 tag : 'div',
32705                                 cls : 'btn-group roo-document-viewer-trash',
32706                                 cn : [
32707                                     {
32708                                         tag : 'button',
32709                                         cls : 'btn btn-default',
32710                                         html : '<i class="fa fa-trash"></i>'
32711                                     }
32712                                 ]
32713                             }
32714                         ]
32715                     }
32716                 }
32717             ]
32718         };
32719         
32720         return cfg;
32721     },
32722     
32723     initEvents : function()
32724     {
32725         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32726         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32727         
32728         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32729         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32730         
32731         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32732         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32733         
32734         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32735         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32736         
32737         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32738         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32739         
32740         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32741         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32742         
32743         this.bodyEl.on('click', this.onClick, this);
32744         this.downloadBtn.on('click', this.onDownload, this);
32745         this.trashBtn.on('click', this.onTrash, this);
32746         
32747         this.downloadBtn.hide();
32748         this.trashBtn.hide();
32749         
32750         if(this.showDownload){
32751             this.downloadBtn.show();
32752         }
32753         
32754         if(this.showTrash){
32755             this.trashBtn.show();
32756         }
32757         
32758         if(!this.showDownload && !this.showTrash) {
32759             this.footerEl.hide();
32760         }
32761         
32762     },
32763     
32764     initial : function()
32765     {
32766         this.fireEvent('initial', this);
32767         
32768     },
32769     
32770     onClick : function(e)
32771     {
32772         e.preventDefault();
32773         
32774         this.fireEvent('click', this);
32775     },
32776     
32777     onDownload : function(e)
32778     {
32779         e.preventDefault();
32780         
32781         this.fireEvent('download', this);
32782     },
32783     
32784     onTrash : function(e)
32785     {
32786         e.preventDefault();
32787         
32788         this.fireEvent('trash', this);
32789     }
32790     
32791 });
32792 /*
32793  * - LGPL
32794  *
32795  * nav progress bar
32796  * 
32797  */
32798
32799 /**
32800  * @class Roo.bootstrap.NavProgressBar
32801  * @extends Roo.bootstrap.Component
32802  * Bootstrap NavProgressBar class
32803  * 
32804  * @constructor
32805  * Create a new nav progress bar
32806  * @param {Object} config The config object
32807  */
32808
32809 Roo.bootstrap.NavProgressBar = function(config){
32810     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32811
32812     this.bullets = this.bullets || [];
32813    
32814 //    Roo.bootstrap.NavProgressBar.register(this);
32815      this.addEvents({
32816         /**
32817              * @event changed
32818              * Fires when the active item changes
32819              * @param {Roo.bootstrap.NavProgressBar} this
32820              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32821              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32822          */
32823         'changed': true
32824      });
32825     
32826 };
32827
32828 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32829     
32830     bullets : [],
32831     barItems : [],
32832     
32833     getAutoCreate : function()
32834     {
32835         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32836         
32837         cfg = {
32838             tag : 'div',
32839             cls : 'roo-navigation-bar-group',
32840             cn : [
32841                 {
32842                     tag : 'div',
32843                     cls : 'roo-navigation-top-bar'
32844                 },
32845                 {
32846                     tag : 'div',
32847                     cls : 'roo-navigation-bullets-bar',
32848                     cn : [
32849                         {
32850                             tag : 'ul',
32851                             cls : 'roo-navigation-bar'
32852                         }
32853                     ]
32854                 },
32855                 
32856                 {
32857                     tag : 'div',
32858                     cls : 'roo-navigation-bottom-bar'
32859                 }
32860             ]
32861             
32862         };
32863         
32864         return cfg;
32865         
32866     },
32867     
32868     initEvents: function() 
32869     {
32870         
32871     },
32872     
32873     onRender : function(ct, position) 
32874     {
32875         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32876         
32877         if(this.bullets.length){
32878             Roo.each(this.bullets, function(b){
32879                this.addItem(b);
32880             }, this);
32881         }
32882         
32883         this.format();
32884         
32885     },
32886     
32887     addItem : function(cfg)
32888     {
32889         var item = new Roo.bootstrap.NavProgressItem(cfg);
32890         
32891         item.parentId = this.id;
32892         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32893         
32894         if(cfg.html){
32895             var top = new Roo.bootstrap.Element({
32896                 tag : 'div',
32897                 cls : 'roo-navigation-bar-text'
32898             });
32899             
32900             var bottom = new Roo.bootstrap.Element({
32901                 tag : 'div',
32902                 cls : 'roo-navigation-bar-text'
32903             });
32904             
32905             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32906             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32907             
32908             var topText = new Roo.bootstrap.Element({
32909                 tag : 'span',
32910                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32911             });
32912             
32913             var bottomText = new Roo.bootstrap.Element({
32914                 tag : 'span',
32915                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32916             });
32917             
32918             topText.onRender(top.el, null);
32919             bottomText.onRender(bottom.el, null);
32920             
32921             item.topEl = top;
32922             item.bottomEl = bottom;
32923         }
32924         
32925         this.barItems.push(item);
32926         
32927         return item;
32928     },
32929     
32930     getActive : function()
32931     {
32932         var active = false;
32933         
32934         Roo.each(this.barItems, function(v){
32935             
32936             if (!v.isActive()) {
32937                 return;
32938             }
32939             
32940             active = v;
32941             return false;
32942             
32943         });
32944         
32945         return active;
32946     },
32947     
32948     setActiveItem : function(item)
32949     {
32950         var prev = false;
32951         
32952         Roo.each(this.barItems, function(v){
32953             if (v.rid == item.rid) {
32954                 return ;
32955             }
32956             
32957             if (v.isActive()) {
32958                 v.setActive(false);
32959                 prev = v;
32960             }
32961         });
32962
32963         item.setActive(true);
32964         
32965         this.fireEvent('changed', this, item, prev);
32966     },
32967     
32968     getBarItem: function(rid)
32969     {
32970         var ret = false;
32971         
32972         Roo.each(this.barItems, function(e) {
32973             if (e.rid != rid) {
32974                 return;
32975             }
32976             
32977             ret =  e;
32978             return false;
32979         });
32980         
32981         return ret;
32982     },
32983     
32984     indexOfItem : function(item)
32985     {
32986         var index = false;
32987         
32988         Roo.each(this.barItems, function(v, i){
32989             
32990             if (v.rid != item.rid) {
32991                 return;
32992             }
32993             
32994             index = i;
32995             return false
32996         });
32997         
32998         return index;
32999     },
33000     
33001     setActiveNext : function()
33002     {
33003         var i = this.indexOfItem(this.getActive());
33004         
33005         if (i > this.barItems.length) {
33006             return;
33007         }
33008         
33009         this.setActiveItem(this.barItems[i+1]);
33010     },
33011     
33012     setActivePrev : function()
33013     {
33014         var i = this.indexOfItem(this.getActive());
33015         
33016         if (i  < 1) {
33017             return;
33018         }
33019         
33020         this.setActiveItem(this.barItems[i-1]);
33021     },
33022     
33023     format : function()
33024     {
33025         if(!this.barItems.length){
33026             return;
33027         }
33028      
33029         var width = 100 / this.barItems.length;
33030         
33031         Roo.each(this.barItems, function(i){
33032             i.el.setStyle('width', width + '%');
33033             i.topEl.el.setStyle('width', width + '%');
33034             i.bottomEl.el.setStyle('width', width + '%');
33035         }, this);
33036         
33037     }
33038     
33039 });
33040 /*
33041  * - LGPL
33042  *
33043  * Nav Progress Item
33044  * 
33045  */
33046
33047 /**
33048  * @class Roo.bootstrap.NavProgressItem
33049  * @extends Roo.bootstrap.Component
33050  * Bootstrap NavProgressItem class
33051  * @cfg {String} rid the reference id
33052  * @cfg {Boolean} active (true|false) Is item active default false
33053  * @cfg {Boolean} disabled (true|false) Is item active default false
33054  * @cfg {String} html
33055  * @cfg {String} position (top|bottom) text position default bottom
33056  * @cfg {String} icon show icon instead of number
33057  * 
33058  * @constructor
33059  * Create a new NavProgressItem
33060  * @param {Object} config The config object
33061  */
33062 Roo.bootstrap.NavProgressItem = function(config){
33063     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33064     this.addEvents({
33065         // raw events
33066         /**
33067          * @event click
33068          * The raw click event for the entire grid.
33069          * @param {Roo.bootstrap.NavProgressItem} this
33070          * @param {Roo.EventObject} e
33071          */
33072         "click" : true
33073     });
33074    
33075 };
33076
33077 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33078     
33079     rid : '',
33080     active : false,
33081     disabled : false,
33082     html : '',
33083     position : 'bottom',
33084     icon : false,
33085     
33086     getAutoCreate : function()
33087     {
33088         var iconCls = 'roo-navigation-bar-item-icon';
33089         
33090         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33091         
33092         var cfg = {
33093             tag: 'li',
33094             cls: 'roo-navigation-bar-item',
33095             cn : [
33096                 {
33097                     tag : 'i',
33098                     cls : iconCls
33099                 }
33100             ]
33101         };
33102         
33103         if(this.active){
33104             cfg.cls += ' active';
33105         }
33106         if(this.disabled){
33107             cfg.cls += ' disabled';
33108         }
33109         
33110         return cfg;
33111     },
33112     
33113     disable : function()
33114     {
33115         this.setDisabled(true);
33116     },
33117     
33118     enable : function()
33119     {
33120         this.setDisabled(false);
33121     },
33122     
33123     initEvents: function() 
33124     {
33125         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33126         
33127         this.iconEl.on('click', this.onClick, this);
33128     },
33129     
33130     onClick : function(e)
33131     {
33132         e.preventDefault();
33133         
33134         if(this.disabled){
33135             return;
33136         }
33137         
33138         if(this.fireEvent('click', this, e) === false){
33139             return;
33140         };
33141         
33142         this.parent().setActiveItem(this);
33143     },
33144     
33145     isActive: function () 
33146     {
33147         return this.active;
33148     },
33149     
33150     setActive : function(state)
33151     {
33152         if(this.active == state){
33153             return;
33154         }
33155         
33156         this.active = state;
33157         
33158         if (state) {
33159             this.el.addClass('active');
33160             return;
33161         }
33162         
33163         this.el.removeClass('active');
33164         
33165         return;
33166     },
33167     
33168     setDisabled : function(state)
33169     {
33170         if(this.disabled == state){
33171             return;
33172         }
33173         
33174         this.disabled = state;
33175         
33176         if (state) {
33177             this.el.addClass('disabled');
33178             return;
33179         }
33180         
33181         this.el.removeClass('disabled');
33182     },
33183     
33184     tooltipEl : function()
33185     {
33186         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33187     }
33188 });
33189  
33190
33191  /*
33192  * - LGPL
33193  *
33194  * FieldLabel
33195  * 
33196  */
33197
33198 /**
33199  * @class Roo.bootstrap.FieldLabel
33200  * @extends Roo.bootstrap.Component
33201  * Bootstrap FieldLabel class
33202  * @cfg {String} html contents of the element
33203  * @cfg {String} tag tag of the element default label
33204  * @cfg {String} cls class of the element
33205  * @cfg {String} target label target 
33206  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33207  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33208  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33209  * @cfg {String} iconTooltip default "This field is required"
33210  * @cfg {String} indicatorpos (left|right) default left
33211  * 
33212  * @constructor
33213  * Create a new FieldLabel
33214  * @param {Object} config The config object
33215  */
33216
33217 Roo.bootstrap.FieldLabel = function(config){
33218     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33219     
33220     this.addEvents({
33221             /**
33222              * @event invalid
33223              * Fires after the field has been marked as invalid.
33224              * @param {Roo.form.FieldLabel} this
33225              * @param {String} msg The validation message
33226              */
33227             invalid : true,
33228             /**
33229              * @event valid
33230              * Fires after the field has been validated with no errors.
33231              * @param {Roo.form.FieldLabel} this
33232              */
33233             valid : true
33234         });
33235 };
33236
33237 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33238     
33239     tag: 'label',
33240     cls: '',
33241     html: '',
33242     target: '',
33243     allowBlank : true,
33244     invalidClass : 'has-warning',
33245     validClass : 'has-success',
33246     iconTooltip : 'This field is required',
33247     indicatorpos : 'left',
33248     
33249     getAutoCreate : function(){
33250         
33251         var cls = "";
33252         if (!this.allowBlank) {
33253             cls  = "visible";
33254         }
33255         
33256         var cfg = {
33257             tag : this.tag,
33258             cls : 'roo-bootstrap-field-label ' + this.cls,
33259             for : this.target,
33260             cn : [
33261                 {
33262                     tag : 'i',
33263                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33264                     tooltip : this.iconTooltip
33265                 },
33266                 {
33267                     tag : 'span',
33268                     html : this.html
33269                 }
33270             ] 
33271         };
33272         
33273         if(this.indicatorpos == 'right'){
33274             var cfg = {
33275                 tag : this.tag,
33276                 cls : 'roo-bootstrap-field-label ' + this.cls,
33277                 for : this.target,
33278                 cn : [
33279                     {
33280                         tag : 'span',
33281                         html : this.html
33282                     },
33283                     {
33284                         tag : 'i',
33285                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33286                         tooltip : this.iconTooltip
33287                     }
33288                 ] 
33289             };
33290         }
33291         
33292         return cfg;
33293     },
33294     
33295     initEvents: function() 
33296     {
33297         Roo.bootstrap.Element.superclass.initEvents.call(this);
33298         
33299         this.indicator = this.indicatorEl();
33300         
33301         if(this.indicator){
33302             this.indicator.removeClass('visible');
33303             this.indicator.addClass('invisible');
33304         }
33305         
33306         Roo.bootstrap.FieldLabel.register(this);
33307     },
33308     
33309     indicatorEl : function()
33310     {
33311         var indicator = this.el.select('i.roo-required-indicator',true).first();
33312         
33313         if(!indicator){
33314             return false;
33315         }
33316         
33317         return indicator;
33318         
33319     },
33320     
33321     /**
33322      * Mark this field as valid
33323      */
33324     markValid : function()
33325     {
33326         if(this.indicator){
33327             this.indicator.removeClass('visible');
33328             this.indicator.addClass('invisible');
33329         }
33330         if (Roo.bootstrap.version == 3) {
33331             this.el.removeClass(this.invalidClass);
33332             this.el.addClass(this.validClass);
33333         } else {
33334             this.el.removeClass('is-invalid');
33335             this.el.addClass('is-valid');
33336         }
33337         
33338         
33339         this.fireEvent('valid', this);
33340     },
33341     
33342     /**
33343      * Mark this field as invalid
33344      * @param {String} msg The validation message
33345      */
33346     markInvalid : function(msg)
33347     {
33348         if(this.indicator){
33349             this.indicator.removeClass('invisible');
33350             this.indicator.addClass('visible');
33351         }
33352           if (Roo.bootstrap.version == 3) {
33353             this.el.removeClass(this.validClass);
33354             this.el.addClass(this.invalidClass);
33355         } else {
33356             this.el.removeClass('is-valid');
33357             this.el.addClass('is-invalid');
33358         }
33359         
33360         
33361         this.fireEvent('invalid', this, msg);
33362     }
33363     
33364    
33365 });
33366
33367 Roo.apply(Roo.bootstrap.FieldLabel, {
33368     
33369     groups: {},
33370     
33371      /**
33372     * register a FieldLabel Group
33373     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33374     */
33375     register : function(label)
33376     {
33377         if(this.groups.hasOwnProperty(label.target)){
33378             return;
33379         }
33380      
33381         this.groups[label.target] = label;
33382         
33383     },
33384     /**
33385     * fetch a FieldLabel Group based on the target
33386     * @param {string} target
33387     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33388     */
33389     get: function(target) {
33390         if (typeof(this.groups[target]) == 'undefined') {
33391             return false;
33392         }
33393         
33394         return this.groups[target] ;
33395     }
33396 });
33397
33398  
33399
33400  /*
33401  * - LGPL
33402  *
33403  * page DateSplitField.
33404  * 
33405  */
33406
33407
33408 /**
33409  * @class Roo.bootstrap.DateSplitField
33410  * @extends Roo.bootstrap.Component
33411  * Bootstrap DateSplitField class
33412  * @cfg {string} fieldLabel - the label associated
33413  * @cfg {Number} labelWidth set the width of label (0-12)
33414  * @cfg {String} labelAlign (top|left)
33415  * @cfg {Boolean} dayAllowBlank (true|false) default false
33416  * @cfg {Boolean} monthAllowBlank (true|false) default false
33417  * @cfg {Boolean} yearAllowBlank (true|false) default false
33418  * @cfg {string} dayPlaceholder 
33419  * @cfg {string} monthPlaceholder
33420  * @cfg {string} yearPlaceholder
33421  * @cfg {string} dayFormat default 'd'
33422  * @cfg {string} monthFormat default 'm'
33423  * @cfg {string} yearFormat default 'Y'
33424  * @cfg {Number} labellg set the width of label (1-12)
33425  * @cfg {Number} labelmd set the width of label (1-12)
33426  * @cfg {Number} labelsm set the width of label (1-12)
33427  * @cfg {Number} labelxs set the width of label (1-12)
33428
33429  *     
33430  * @constructor
33431  * Create a new DateSplitField
33432  * @param {Object} config The config object
33433  */
33434
33435 Roo.bootstrap.DateSplitField = function(config){
33436     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33437     
33438     this.addEvents({
33439         // raw events
33440          /**
33441          * @event years
33442          * getting the data of years
33443          * @param {Roo.bootstrap.DateSplitField} this
33444          * @param {Object} years
33445          */
33446         "years" : true,
33447         /**
33448          * @event days
33449          * getting the data of days
33450          * @param {Roo.bootstrap.DateSplitField} this
33451          * @param {Object} days
33452          */
33453         "days" : true,
33454         /**
33455          * @event invalid
33456          * Fires after the field has been marked as invalid.
33457          * @param {Roo.form.Field} this
33458          * @param {String} msg The validation message
33459          */
33460         invalid : true,
33461        /**
33462          * @event valid
33463          * Fires after the field has been validated with no errors.
33464          * @param {Roo.form.Field} this
33465          */
33466         valid : true
33467     });
33468 };
33469
33470 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33471     
33472     fieldLabel : '',
33473     labelAlign : 'top',
33474     labelWidth : 3,
33475     dayAllowBlank : false,
33476     monthAllowBlank : false,
33477     yearAllowBlank : false,
33478     dayPlaceholder : '',
33479     monthPlaceholder : '',
33480     yearPlaceholder : '',
33481     dayFormat : 'd',
33482     monthFormat : 'm',
33483     yearFormat : 'Y',
33484     isFormField : true,
33485     labellg : 0,
33486     labelmd : 0,
33487     labelsm : 0,
33488     labelxs : 0,
33489     
33490     getAutoCreate : function()
33491     {
33492         var cfg = {
33493             tag : 'div',
33494             cls : 'row roo-date-split-field-group',
33495             cn : [
33496                 {
33497                     tag : 'input',
33498                     type : 'hidden',
33499                     cls : 'form-hidden-field roo-date-split-field-group-value',
33500                     name : this.name
33501                 }
33502             ]
33503         };
33504         
33505         var labelCls = 'col-md-12';
33506         var contentCls = 'col-md-4';
33507         
33508         if(this.fieldLabel){
33509             
33510             var label = {
33511                 tag : 'div',
33512                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33513                 cn : [
33514                     {
33515                         tag : 'label',
33516                         html : this.fieldLabel
33517                     }
33518                 ]
33519             };
33520             
33521             if(this.labelAlign == 'left'){
33522             
33523                 if(this.labelWidth > 12){
33524                     label.style = "width: " + this.labelWidth + 'px';
33525                 }
33526
33527                 if(this.labelWidth < 13 && this.labelmd == 0){
33528                     this.labelmd = this.labelWidth;
33529                 }
33530
33531                 if(this.labellg > 0){
33532                     labelCls = ' col-lg-' + this.labellg;
33533                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33534                 }
33535
33536                 if(this.labelmd > 0){
33537                     labelCls = ' col-md-' + this.labelmd;
33538                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33539                 }
33540
33541                 if(this.labelsm > 0){
33542                     labelCls = ' col-sm-' + this.labelsm;
33543                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33544                 }
33545
33546                 if(this.labelxs > 0){
33547                     labelCls = ' col-xs-' + this.labelxs;
33548                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33549                 }
33550             }
33551             
33552             label.cls += ' ' + labelCls;
33553             
33554             cfg.cn.push(label);
33555         }
33556         
33557         Roo.each(['day', 'month', 'year'], function(t){
33558             cfg.cn.push({
33559                 tag : 'div',
33560                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33561             });
33562         }, this);
33563         
33564         return cfg;
33565     },
33566     
33567     inputEl: function ()
33568     {
33569         return this.el.select('.roo-date-split-field-group-value', true).first();
33570     },
33571     
33572     onRender : function(ct, position) 
33573     {
33574         var _this = this;
33575         
33576         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33577         
33578         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33579         
33580         this.dayField = new Roo.bootstrap.ComboBox({
33581             allowBlank : this.dayAllowBlank,
33582             alwaysQuery : true,
33583             displayField : 'value',
33584             editable : false,
33585             fieldLabel : '',
33586             forceSelection : true,
33587             mode : 'local',
33588             placeholder : this.dayPlaceholder,
33589             selectOnFocus : true,
33590             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33591             triggerAction : 'all',
33592             typeAhead : true,
33593             valueField : 'value',
33594             store : new Roo.data.SimpleStore({
33595                 data : (function() {    
33596                     var days = [];
33597                     _this.fireEvent('days', _this, days);
33598                     return days;
33599                 })(),
33600                 fields : [ 'value' ]
33601             }),
33602             listeners : {
33603                 select : function (_self, record, index)
33604                 {
33605                     _this.setValue(_this.getValue());
33606                 }
33607             }
33608         });
33609
33610         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33611         
33612         this.monthField = new Roo.bootstrap.MonthField({
33613             after : '<i class=\"fa fa-calendar\"></i>',
33614             allowBlank : this.monthAllowBlank,
33615             placeholder : this.monthPlaceholder,
33616             readOnly : true,
33617             listeners : {
33618                 render : function (_self)
33619                 {
33620                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33621                         e.preventDefault();
33622                         _self.focus();
33623                     });
33624                 },
33625                 select : function (_self, oldvalue, newvalue)
33626                 {
33627                     _this.setValue(_this.getValue());
33628                 }
33629             }
33630         });
33631         
33632         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33633         
33634         this.yearField = new Roo.bootstrap.ComboBox({
33635             allowBlank : this.yearAllowBlank,
33636             alwaysQuery : true,
33637             displayField : 'value',
33638             editable : false,
33639             fieldLabel : '',
33640             forceSelection : true,
33641             mode : 'local',
33642             placeholder : this.yearPlaceholder,
33643             selectOnFocus : true,
33644             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33645             triggerAction : 'all',
33646             typeAhead : true,
33647             valueField : 'value',
33648             store : new Roo.data.SimpleStore({
33649                 data : (function() {
33650                     var years = [];
33651                     _this.fireEvent('years', _this, years);
33652                     return years;
33653                 })(),
33654                 fields : [ 'value' ]
33655             }),
33656             listeners : {
33657                 select : function (_self, record, index)
33658                 {
33659                     _this.setValue(_this.getValue());
33660                 }
33661             }
33662         });
33663
33664         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33665     },
33666     
33667     setValue : function(v, format)
33668     {
33669         this.inputEl.dom.value = v;
33670         
33671         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33672         
33673         var d = Date.parseDate(v, f);
33674         
33675         if(!d){
33676             this.validate();
33677             return;
33678         }
33679         
33680         this.setDay(d.format(this.dayFormat));
33681         this.setMonth(d.format(this.monthFormat));
33682         this.setYear(d.format(this.yearFormat));
33683         
33684         this.validate();
33685         
33686         return;
33687     },
33688     
33689     setDay : function(v)
33690     {
33691         this.dayField.setValue(v);
33692         this.inputEl.dom.value = this.getValue();
33693         this.validate();
33694         return;
33695     },
33696     
33697     setMonth : function(v)
33698     {
33699         this.monthField.setValue(v, true);
33700         this.inputEl.dom.value = this.getValue();
33701         this.validate();
33702         return;
33703     },
33704     
33705     setYear : function(v)
33706     {
33707         this.yearField.setValue(v);
33708         this.inputEl.dom.value = this.getValue();
33709         this.validate();
33710         return;
33711     },
33712     
33713     getDay : function()
33714     {
33715         return this.dayField.getValue();
33716     },
33717     
33718     getMonth : function()
33719     {
33720         return this.monthField.getValue();
33721     },
33722     
33723     getYear : function()
33724     {
33725         return this.yearField.getValue();
33726     },
33727     
33728     getValue : function()
33729     {
33730         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33731         
33732         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33733         
33734         return date;
33735     },
33736     
33737     reset : function()
33738     {
33739         this.setDay('');
33740         this.setMonth('');
33741         this.setYear('');
33742         this.inputEl.dom.value = '';
33743         this.validate();
33744         return;
33745     },
33746     
33747     validate : function()
33748     {
33749         var d = this.dayField.validate();
33750         var m = this.monthField.validate();
33751         var y = this.yearField.validate();
33752         
33753         var valid = true;
33754         
33755         if(
33756                 (!this.dayAllowBlank && !d) ||
33757                 (!this.monthAllowBlank && !m) ||
33758                 (!this.yearAllowBlank && !y)
33759         ){
33760             valid = false;
33761         }
33762         
33763         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33764             return valid;
33765         }
33766         
33767         if(valid){
33768             this.markValid();
33769             return valid;
33770         }
33771         
33772         this.markInvalid();
33773         
33774         return valid;
33775     },
33776     
33777     markValid : function()
33778     {
33779         
33780         var label = this.el.select('label', true).first();
33781         var icon = this.el.select('i.fa-star', true).first();
33782
33783         if(label && icon){
33784             icon.remove();
33785         }
33786         
33787         this.fireEvent('valid', this);
33788     },
33789     
33790      /**
33791      * Mark this field as invalid
33792      * @param {String} msg The validation message
33793      */
33794     markInvalid : function(msg)
33795     {
33796         
33797         var label = this.el.select('label', true).first();
33798         var icon = this.el.select('i.fa-star', true).first();
33799
33800         if(label && !icon){
33801             this.el.select('.roo-date-split-field-label', true).createChild({
33802                 tag : 'i',
33803                 cls : 'text-danger fa fa-lg fa-star',
33804                 tooltip : 'This field is required',
33805                 style : 'margin-right:5px;'
33806             }, label, true);
33807         }
33808         
33809         this.fireEvent('invalid', this, msg);
33810     },
33811     
33812     clearInvalid : function()
33813     {
33814         var label = this.el.select('label', true).first();
33815         var icon = this.el.select('i.fa-star', true).first();
33816
33817         if(label && icon){
33818             icon.remove();
33819         }
33820         
33821         this.fireEvent('valid', this);
33822     },
33823     
33824     getName: function()
33825     {
33826         return this.name;
33827     }
33828     
33829 });
33830
33831  /**
33832  *
33833  * This is based on 
33834  * http://masonry.desandro.com
33835  *
33836  * The idea is to render all the bricks based on vertical width...
33837  *
33838  * The original code extends 'outlayer' - we might need to use that....
33839  * 
33840  */
33841
33842
33843 /**
33844  * @class Roo.bootstrap.LayoutMasonry
33845  * @extends Roo.bootstrap.Component
33846  * Bootstrap Layout Masonry class
33847  * 
33848  * @constructor
33849  * Create a new Element
33850  * @param {Object} config The config object
33851  */
33852
33853 Roo.bootstrap.LayoutMasonry = function(config){
33854     
33855     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33856     
33857     this.bricks = [];
33858     
33859     Roo.bootstrap.LayoutMasonry.register(this);
33860     
33861     this.addEvents({
33862         // raw events
33863         /**
33864          * @event layout
33865          * Fire after layout the items
33866          * @param {Roo.bootstrap.LayoutMasonry} this
33867          * @param {Roo.EventObject} e
33868          */
33869         "layout" : true
33870     });
33871     
33872 };
33873
33874 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33875     
33876     /**
33877      * @cfg {Boolean} isLayoutInstant = no animation?
33878      */   
33879     isLayoutInstant : false, // needed?
33880    
33881     /**
33882      * @cfg {Number} boxWidth  width of the columns
33883      */   
33884     boxWidth : 450,
33885     
33886       /**
33887      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33888      */   
33889     boxHeight : 0,
33890     
33891     /**
33892      * @cfg {Number} padWidth padding below box..
33893      */   
33894     padWidth : 10, 
33895     
33896     /**
33897      * @cfg {Number} gutter gutter width..
33898      */   
33899     gutter : 10,
33900     
33901      /**
33902      * @cfg {Number} maxCols maximum number of columns
33903      */   
33904     
33905     maxCols: 0,
33906     
33907     /**
33908      * @cfg {Boolean} isAutoInitial defalut true
33909      */   
33910     isAutoInitial : true, 
33911     
33912     containerWidth: 0,
33913     
33914     /**
33915      * @cfg {Boolean} isHorizontal defalut false
33916      */   
33917     isHorizontal : false, 
33918
33919     currentSize : null,
33920     
33921     tag: 'div',
33922     
33923     cls: '',
33924     
33925     bricks: null, //CompositeElement
33926     
33927     cols : 1,
33928     
33929     _isLayoutInited : false,
33930     
33931 //    isAlternative : false, // only use for vertical layout...
33932     
33933     /**
33934      * @cfg {Number} alternativePadWidth padding below box..
33935      */   
33936     alternativePadWidth : 50,
33937     
33938     selectedBrick : [],
33939     
33940     getAutoCreate : function(){
33941         
33942         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33943         
33944         var cfg = {
33945             tag: this.tag,
33946             cls: 'blog-masonary-wrapper ' + this.cls,
33947             cn : {
33948                 cls : 'mas-boxes masonary'
33949             }
33950         };
33951         
33952         return cfg;
33953     },
33954     
33955     getChildContainer: function( )
33956     {
33957         if (this.boxesEl) {
33958             return this.boxesEl;
33959         }
33960         
33961         this.boxesEl = this.el.select('.mas-boxes').first();
33962         
33963         return this.boxesEl;
33964     },
33965     
33966     
33967     initEvents : function()
33968     {
33969         var _this = this;
33970         
33971         if(this.isAutoInitial){
33972             Roo.log('hook children rendered');
33973             this.on('childrenrendered', function() {
33974                 Roo.log('children rendered');
33975                 _this.initial();
33976             } ,this);
33977         }
33978     },
33979     
33980     initial : function()
33981     {
33982         this.selectedBrick = [];
33983         
33984         this.currentSize = this.el.getBox(true);
33985         
33986         Roo.EventManager.onWindowResize(this.resize, this); 
33987
33988         if(!this.isAutoInitial){
33989             this.layout();
33990             return;
33991         }
33992         
33993         this.layout();
33994         
33995         return;
33996         //this.layout.defer(500,this);
33997         
33998     },
33999     
34000     resize : function()
34001     {
34002         var cs = this.el.getBox(true);
34003         
34004         if (
34005                 this.currentSize.width == cs.width && 
34006                 this.currentSize.x == cs.x && 
34007                 this.currentSize.height == cs.height && 
34008                 this.currentSize.y == cs.y 
34009         ) {
34010             Roo.log("no change in with or X or Y");
34011             return;
34012         }
34013         
34014         this.currentSize = cs;
34015         
34016         this.layout();
34017         
34018     },
34019     
34020     layout : function()
34021     {   
34022         this._resetLayout();
34023         
34024         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34025         
34026         this.layoutItems( isInstant );
34027       
34028         this._isLayoutInited = true;
34029         
34030         this.fireEvent('layout', this);
34031         
34032     },
34033     
34034     _resetLayout : function()
34035     {
34036         if(this.isHorizontal){
34037             this.horizontalMeasureColumns();
34038             return;
34039         }
34040         
34041         this.verticalMeasureColumns();
34042         
34043     },
34044     
34045     verticalMeasureColumns : function()
34046     {
34047         this.getContainerWidth();
34048         
34049 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34050 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34051 //            return;
34052 //        }
34053         
34054         var boxWidth = this.boxWidth + this.padWidth;
34055         
34056         if(this.containerWidth < this.boxWidth){
34057             boxWidth = this.containerWidth
34058         }
34059         
34060         var containerWidth = this.containerWidth;
34061         
34062         var cols = Math.floor(containerWidth / boxWidth);
34063         
34064         this.cols = Math.max( cols, 1 );
34065         
34066         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34067         
34068         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34069         
34070         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34071         
34072         this.colWidth = boxWidth + avail - this.padWidth;
34073         
34074         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34075         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34076     },
34077     
34078     horizontalMeasureColumns : function()
34079     {
34080         this.getContainerWidth();
34081         
34082         var boxWidth = this.boxWidth;
34083         
34084         if(this.containerWidth < boxWidth){
34085             boxWidth = this.containerWidth;
34086         }
34087         
34088         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34089         
34090         this.el.setHeight(boxWidth);
34091         
34092     },
34093     
34094     getContainerWidth : function()
34095     {
34096         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34097     },
34098     
34099     layoutItems : function( isInstant )
34100     {
34101         Roo.log(this.bricks);
34102         
34103         var items = Roo.apply([], this.bricks);
34104         
34105         if(this.isHorizontal){
34106             this._horizontalLayoutItems( items , isInstant );
34107             return;
34108         }
34109         
34110 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34111 //            this._verticalAlternativeLayoutItems( items , isInstant );
34112 //            return;
34113 //        }
34114         
34115         this._verticalLayoutItems( items , isInstant );
34116         
34117     },
34118     
34119     _verticalLayoutItems : function ( items , isInstant)
34120     {
34121         if ( !items || !items.length ) {
34122             return;
34123         }
34124         
34125         var standard = [
34126             ['xs', 'xs', 'xs', 'tall'],
34127             ['xs', 'xs', 'tall'],
34128             ['xs', 'xs', 'sm'],
34129             ['xs', 'xs', 'xs'],
34130             ['xs', 'tall'],
34131             ['xs', 'sm'],
34132             ['xs', 'xs'],
34133             ['xs'],
34134             
34135             ['sm', 'xs', 'xs'],
34136             ['sm', 'xs'],
34137             ['sm'],
34138             
34139             ['tall', 'xs', 'xs', 'xs'],
34140             ['tall', 'xs', 'xs'],
34141             ['tall', 'xs'],
34142             ['tall']
34143             
34144         ];
34145         
34146         var queue = [];
34147         
34148         var boxes = [];
34149         
34150         var box = [];
34151         
34152         Roo.each(items, function(item, k){
34153             
34154             switch (item.size) {
34155                 // these layouts take up a full box,
34156                 case 'md' :
34157                 case 'md-left' :
34158                 case 'md-right' :
34159                 case 'wide' :
34160                     
34161                     if(box.length){
34162                         boxes.push(box);
34163                         box = [];
34164                     }
34165                     
34166                     boxes.push([item]);
34167                     
34168                     break;
34169                     
34170                 case 'xs' :
34171                 case 'sm' :
34172                 case 'tall' :
34173                     
34174                     box.push(item);
34175                     
34176                     break;
34177                 default :
34178                     break;
34179                     
34180             }
34181             
34182         }, this);
34183         
34184         if(box.length){
34185             boxes.push(box);
34186             box = [];
34187         }
34188         
34189         var filterPattern = function(box, length)
34190         {
34191             if(!box.length){
34192                 return;
34193             }
34194             
34195             var match = false;
34196             
34197             var pattern = box.slice(0, length);
34198             
34199             var format = [];
34200             
34201             Roo.each(pattern, function(i){
34202                 format.push(i.size);
34203             }, this);
34204             
34205             Roo.each(standard, function(s){
34206                 
34207                 if(String(s) != String(format)){
34208                     return;
34209                 }
34210                 
34211                 match = true;
34212                 return false;
34213                 
34214             }, this);
34215             
34216             if(!match && length == 1){
34217                 return;
34218             }
34219             
34220             if(!match){
34221                 filterPattern(box, length - 1);
34222                 return;
34223             }
34224                 
34225             queue.push(pattern);
34226
34227             box = box.slice(length, box.length);
34228
34229             filterPattern(box, 4);
34230
34231             return;
34232             
34233         }
34234         
34235         Roo.each(boxes, function(box, k){
34236             
34237             if(!box.length){
34238                 return;
34239             }
34240             
34241             if(box.length == 1){
34242                 queue.push(box);
34243                 return;
34244             }
34245             
34246             filterPattern(box, 4);
34247             
34248         }, this);
34249         
34250         this._processVerticalLayoutQueue( queue, isInstant );
34251         
34252     },
34253     
34254 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34255 //    {
34256 //        if ( !items || !items.length ) {
34257 //            return;
34258 //        }
34259 //
34260 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34261 //        
34262 //    },
34263     
34264     _horizontalLayoutItems : function ( items , isInstant)
34265     {
34266         if ( !items || !items.length || items.length < 3) {
34267             return;
34268         }
34269         
34270         items.reverse();
34271         
34272         var eItems = items.slice(0, 3);
34273         
34274         items = items.slice(3, items.length);
34275         
34276         var standard = [
34277             ['xs', 'xs', 'xs', 'wide'],
34278             ['xs', 'xs', 'wide'],
34279             ['xs', 'xs', 'sm'],
34280             ['xs', 'xs', 'xs'],
34281             ['xs', 'wide'],
34282             ['xs', 'sm'],
34283             ['xs', 'xs'],
34284             ['xs'],
34285             
34286             ['sm', 'xs', 'xs'],
34287             ['sm', 'xs'],
34288             ['sm'],
34289             
34290             ['wide', 'xs', 'xs', 'xs'],
34291             ['wide', 'xs', 'xs'],
34292             ['wide', 'xs'],
34293             ['wide'],
34294             
34295             ['wide-thin']
34296         ];
34297         
34298         var queue = [];
34299         
34300         var boxes = [];
34301         
34302         var box = [];
34303         
34304         Roo.each(items, function(item, k){
34305             
34306             switch (item.size) {
34307                 case 'md' :
34308                 case 'md-left' :
34309                 case 'md-right' :
34310                 case 'tall' :
34311                     
34312                     if(box.length){
34313                         boxes.push(box);
34314                         box = [];
34315                     }
34316                     
34317                     boxes.push([item]);
34318                     
34319                     break;
34320                     
34321                 case 'xs' :
34322                 case 'sm' :
34323                 case 'wide' :
34324                 case 'wide-thin' :
34325                     
34326                     box.push(item);
34327                     
34328                     break;
34329                 default :
34330                     break;
34331                     
34332             }
34333             
34334         }, this);
34335         
34336         if(box.length){
34337             boxes.push(box);
34338             box = [];
34339         }
34340         
34341         var filterPattern = function(box, length)
34342         {
34343             if(!box.length){
34344                 return;
34345             }
34346             
34347             var match = false;
34348             
34349             var pattern = box.slice(0, length);
34350             
34351             var format = [];
34352             
34353             Roo.each(pattern, function(i){
34354                 format.push(i.size);
34355             }, this);
34356             
34357             Roo.each(standard, function(s){
34358                 
34359                 if(String(s) != String(format)){
34360                     return;
34361                 }
34362                 
34363                 match = true;
34364                 return false;
34365                 
34366             }, this);
34367             
34368             if(!match && length == 1){
34369                 return;
34370             }
34371             
34372             if(!match){
34373                 filterPattern(box, length - 1);
34374                 return;
34375             }
34376                 
34377             queue.push(pattern);
34378
34379             box = box.slice(length, box.length);
34380
34381             filterPattern(box, 4);
34382
34383             return;
34384             
34385         }
34386         
34387         Roo.each(boxes, function(box, k){
34388             
34389             if(!box.length){
34390                 return;
34391             }
34392             
34393             if(box.length == 1){
34394                 queue.push(box);
34395                 return;
34396             }
34397             
34398             filterPattern(box, 4);
34399             
34400         }, this);
34401         
34402         
34403         var prune = [];
34404         
34405         var pos = this.el.getBox(true);
34406         
34407         var minX = pos.x;
34408         
34409         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34410         
34411         var hit_end = false;
34412         
34413         Roo.each(queue, function(box){
34414             
34415             if(hit_end){
34416                 
34417                 Roo.each(box, function(b){
34418                 
34419                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34420                     b.el.hide();
34421
34422                 }, this);
34423
34424                 return;
34425             }
34426             
34427             var mx = 0;
34428             
34429             Roo.each(box, function(b){
34430                 
34431                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34432                 b.el.show();
34433
34434                 mx = Math.max(mx, b.x);
34435                 
34436             }, this);
34437             
34438             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34439             
34440             if(maxX < minX){
34441                 
34442                 Roo.each(box, function(b){
34443                 
34444                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34445                     b.el.hide();
34446                     
34447                 }, this);
34448                 
34449                 hit_end = true;
34450                 
34451                 return;
34452             }
34453             
34454             prune.push(box);
34455             
34456         }, this);
34457         
34458         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34459     },
34460     
34461     /** Sets position of item in DOM
34462     * @param {Element} item
34463     * @param {Number} x - horizontal position
34464     * @param {Number} y - vertical position
34465     * @param {Boolean} isInstant - disables transitions
34466     */
34467     _processVerticalLayoutQueue : function( queue, isInstant )
34468     {
34469         var pos = this.el.getBox(true);
34470         var x = pos.x;
34471         var y = pos.y;
34472         var maxY = [];
34473         
34474         for (var i = 0; i < this.cols; i++){
34475             maxY[i] = pos.y;
34476         }
34477         
34478         Roo.each(queue, function(box, k){
34479             
34480             var col = k % this.cols;
34481             
34482             Roo.each(box, function(b,kk){
34483                 
34484                 b.el.position('absolute');
34485                 
34486                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34487                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34488                 
34489                 if(b.size == 'md-left' || b.size == 'md-right'){
34490                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34491                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34492                 }
34493                 
34494                 b.el.setWidth(width);
34495                 b.el.setHeight(height);
34496                 // iframe?
34497                 b.el.select('iframe',true).setSize(width,height);
34498                 
34499             }, this);
34500             
34501             for (var i = 0; i < this.cols; i++){
34502                 
34503                 if(maxY[i] < maxY[col]){
34504                     col = i;
34505                     continue;
34506                 }
34507                 
34508                 col = Math.min(col, i);
34509                 
34510             }
34511             
34512             x = pos.x + col * (this.colWidth + this.padWidth);
34513             
34514             y = maxY[col];
34515             
34516             var positions = [];
34517             
34518             switch (box.length){
34519                 case 1 :
34520                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34521                     break;
34522                 case 2 :
34523                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34524                     break;
34525                 case 3 :
34526                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34527                     break;
34528                 case 4 :
34529                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34530                     break;
34531                 default :
34532                     break;
34533             }
34534             
34535             Roo.each(box, function(b,kk){
34536                 
34537                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34538                 
34539                 var sz = b.el.getSize();
34540                 
34541                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34542                 
34543             }, this);
34544             
34545         }, this);
34546         
34547         var mY = 0;
34548         
34549         for (var i = 0; i < this.cols; i++){
34550             mY = Math.max(mY, maxY[i]);
34551         }
34552         
34553         this.el.setHeight(mY - pos.y);
34554         
34555     },
34556     
34557 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34558 //    {
34559 //        var pos = this.el.getBox(true);
34560 //        var x = pos.x;
34561 //        var y = pos.y;
34562 //        var maxX = pos.right;
34563 //        
34564 //        var maxHeight = 0;
34565 //        
34566 //        Roo.each(items, function(item, k){
34567 //            
34568 //            var c = k % 2;
34569 //            
34570 //            item.el.position('absolute');
34571 //                
34572 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34573 //
34574 //            item.el.setWidth(width);
34575 //
34576 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34577 //
34578 //            item.el.setHeight(height);
34579 //            
34580 //            if(c == 0){
34581 //                item.el.setXY([x, y], isInstant ? false : true);
34582 //            } else {
34583 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34584 //            }
34585 //            
34586 //            y = y + height + this.alternativePadWidth;
34587 //            
34588 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34589 //            
34590 //        }, this);
34591 //        
34592 //        this.el.setHeight(maxHeight);
34593 //        
34594 //    },
34595     
34596     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34597     {
34598         var pos = this.el.getBox(true);
34599         
34600         var minX = pos.x;
34601         var minY = pos.y;
34602         
34603         var maxX = pos.right;
34604         
34605         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34606         
34607         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34608         
34609         Roo.each(queue, function(box, k){
34610             
34611             Roo.each(box, function(b, kk){
34612                 
34613                 b.el.position('absolute');
34614                 
34615                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34616                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34617                 
34618                 if(b.size == 'md-left' || b.size == 'md-right'){
34619                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34620                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34621                 }
34622                 
34623                 b.el.setWidth(width);
34624                 b.el.setHeight(height);
34625                 
34626             }, this);
34627             
34628             if(!box.length){
34629                 return;
34630             }
34631             
34632             var positions = [];
34633             
34634             switch (box.length){
34635                 case 1 :
34636                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34637                     break;
34638                 case 2 :
34639                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34640                     break;
34641                 case 3 :
34642                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34643                     break;
34644                 case 4 :
34645                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34646                     break;
34647                 default :
34648                     break;
34649             }
34650             
34651             Roo.each(box, function(b,kk){
34652                 
34653                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34654                 
34655                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34656                 
34657             }, this);
34658             
34659         }, this);
34660         
34661     },
34662     
34663     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34664     {
34665         Roo.each(eItems, function(b,k){
34666             
34667             b.size = (k == 0) ? 'sm' : 'xs';
34668             b.x = (k == 0) ? 2 : 1;
34669             b.y = (k == 0) ? 2 : 1;
34670             
34671             b.el.position('absolute');
34672             
34673             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34674                 
34675             b.el.setWidth(width);
34676             
34677             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34678             
34679             b.el.setHeight(height);
34680             
34681         }, this);
34682
34683         var positions = [];
34684         
34685         positions.push({
34686             x : maxX - this.unitWidth * 2 - this.gutter,
34687             y : minY
34688         });
34689         
34690         positions.push({
34691             x : maxX - this.unitWidth,
34692             y : minY + (this.unitWidth + this.gutter) * 2
34693         });
34694         
34695         positions.push({
34696             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34697             y : minY
34698         });
34699         
34700         Roo.each(eItems, function(b,k){
34701             
34702             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34703
34704         }, this);
34705         
34706     },
34707     
34708     getVerticalOneBoxColPositions : function(x, y, box)
34709     {
34710         var pos = [];
34711         
34712         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34713         
34714         if(box[0].size == 'md-left'){
34715             rand = 0;
34716         }
34717         
34718         if(box[0].size == 'md-right'){
34719             rand = 1;
34720         }
34721         
34722         pos.push({
34723             x : x + (this.unitWidth + this.gutter) * rand,
34724             y : y
34725         });
34726         
34727         return pos;
34728     },
34729     
34730     getVerticalTwoBoxColPositions : function(x, y, box)
34731     {
34732         var pos = [];
34733         
34734         if(box[0].size == 'xs'){
34735             
34736             pos.push({
34737                 x : x,
34738                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34739             });
34740
34741             pos.push({
34742                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34743                 y : y
34744             });
34745             
34746             return pos;
34747             
34748         }
34749         
34750         pos.push({
34751             x : x,
34752             y : y
34753         });
34754
34755         pos.push({
34756             x : x + (this.unitWidth + this.gutter) * 2,
34757             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34758         });
34759         
34760         return pos;
34761         
34762     },
34763     
34764     getVerticalThreeBoxColPositions : function(x, y, box)
34765     {
34766         var pos = [];
34767         
34768         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34769             
34770             pos.push({
34771                 x : x,
34772                 y : y
34773             });
34774
34775             pos.push({
34776                 x : x + (this.unitWidth + this.gutter) * 1,
34777                 y : y
34778             });
34779             
34780             pos.push({
34781                 x : x + (this.unitWidth + this.gutter) * 2,
34782                 y : y
34783             });
34784             
34785             return pos;
34786             
34787         }
34788         
34789         if(box[0].size == 'xs' && box[1].size == 'xs'){
34790             
34791             pos.push({
34792                 x : x,
34793                 y : y
34794             });
34795
34796             pos.push({
34797                 x : x,
34798                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34799             });
34800             
34801             pos.push({
34802                 x : x + (this.unitWidth + this.gutter) * 1,
34803                 y : y
34804             });
34805             
34806             return pos;
34807             
34808         }
34809         
34810         pos.push({
34811             x : x,
34812             y : y
34813         });
34814
34815         pos.push({
34816             x : x + (this.unitWidth + this.gutter) * 2,
34817             y : y
34818         });
34819
34820         pos.push({
34821             x : x + (this.unitWidth + this.gutter) * 2,
34822             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34823         });
34824             
34825         return pos;
34826         
34827     },
34828     
34829     getVerticalFourBoxColPositions : function(x, y, box)
34830     {
34831         var pos = [];
34832         
34833         if(box[0].size == 'xs'){
34834             
34835             pos.push({
34836                 x : x,
34837                 y : y
34838             });
34839
34840             pos.push({
34841                 x : x,
34842                 y : y + (this.unitHeight + this.gutter) * 1
34843             });
34844             
34845             pos.push({
34846                 x : x,
34847                 y : y + (this.unitHeight + this.gutter) * 2
34848             });
34849             
34850             pos.push({
34851                 x : x + (this.unitWidth + this.gutter) * 1,
34852                 y : y
34853             });
34854             
34855             return pos;
34856             
34857         }
34858         
34859         pos.push({
34860             x : x,
34861             y : y
34862         });
34863
34864         pos.push({
34865             x : x + (this.unitWidth + this.gutter) * 2,
34866             y : y
34867         });
34868
34869         pos.push({
34870             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34871             y : y + (this.unitHeight + this.gutter) * 1
34872         });
34873
34874         pos.push({
34875             x : x + (this.unitWidth + this.gutter) * 2,
34876             y : y + (this.unitWidth + this.gutter) * 2
34877         });
34878
34879         return pos;
34880         
34881     },
34882     
34883     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34884     {
34885         var pos = [];
34886         
34887         if(box[0].size == 'md-left'){
34888             pos.push({
34889                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34890                 y : minY
34891             });
34892             
34893             return pos;
34894         }
34895         
34896         if(box[0].size == 'md-right'){
34897             pos.push({
34898                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34899                 y : minY + (this.unitWidth + this.gutter) * 1
34900             });
34901             
34902             return pos;
34903         }
34904         
34905         var rand = Math.floor(Math.random() * (4 - box[0].y));
34906         
34907         pos.push({
34908             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34909             y : minY + (this.unitWidth + this.gutter) * rand
34910         });
34911         
34912         return pos;
34913         
34914     },
34915     
34916     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34917     {
34918         var pos = [];
34919         
34920         if(box[0].size == 'xs'){
34921             
34922             pos.push({
34923                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34924                 y : minY
34925             });
34926
34927             pos.push({
34928                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34929                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34930             });
34931             
34932             return pos;
34933             
34934         }
34935         
34936         pos.push({
34937             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34938             y : minY
34939         });
34940
34941         pos.push({
34942             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34943             y : minY + (this.unitWidth + this.gutter) * 2
34944         });
34945         
34946         return pos;
34947         
34948     },
34949     
34950     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34951     {
34952         var pos = [];
34953         
34954         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34955             
34956             pos.push({
34957                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34958                 y : minY
34959             });
34960
34961             pos.push({
34962                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34963                 y : minY + (this.unitWidth + this.gutter) * 1
34964             });
34965             
34966             pos.push({
34967                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34968                 y : minY + (this.unitWidth + this.gutter) * 2
34969             });
34970             
34971             return pos;
34972             
34973         }
34974         
34975         if(box[0].size == 'xs' && box[1].size == 'xs'){
34976             
34977             pos.push({
34978                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34979                 y : minY
34980             });
34981
34982             pos.push({
34983                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34984                 y : minY
34985             });
34986             
34987             pos.push({
34988                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34989                 y : minY + (this.unitWidth + this.gutter) * 1
34990             });
34991             
34992             return pos;
34993             
34994         }
34995         
34996         pos.push({
34997             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34998             y : minY
34999         });
35000
35001         pos.push({
35002             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35003             y : minY + (this.unitWidth + this.gutter) * 2
35004         });
35005
35006         pos.push({
35007             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35008             y : minY + (this.unitWidth + this.gutter) * 2
35009         });
35010             
35011         return pos;
35012         
35013     },
35014     
35015     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35016     {
35017         var pos = [];
35018         
35019         if(box[0].size == 'xs'){
35020             
35021             pos.push({
35022                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35023                 y : minY
35024             });
35025
35026             pos.push({
35027                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35028                 y : minY
35029             });
35030             
35031             pos.push({
35032                 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),
35033                 y : minY
35034             });
35035             
35036             pos.push({
35037                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35038                 y : minY + (this.unitWidth + this.gutter) * 1
35039             });
35040             
35041             return pos;
35042             
35043         }
35044         
35045         pos.push({
35046             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35047             y : minY
35048         });
35049         
35050         pos.push({
35051             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35052             y : minY + (this.unitWidth + this.gutter) * 2
35053         });
35054         
35055         pos.push({
35056             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35057             y : minY + (this.unitWidth + this.gutter) * 2
35058         });
35059         
35060         pos.push({
35061             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),
35062             y : minY + (this.unitWidth + this.gutter) * 2
35063         });
35064
35065         return pos;
35066         
35067     },
35068     
35069     /**
35070     * remove a Masonry Brick
35071     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35072     */
35073     removeBrick : function(brick_id)
35074     {
35075         if (!brick_id) {
35076             return;
35077         }
35078         
35079         for (var i = 0; i<this.bricks.length; i++) {
35080             if (this.bricks[i].id == brick_id) {
35081                 this.bricks.splice(i,1);
35082                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35083                 this.initial();
35084             }
35085         }
35086     },
35087     
35088     /**
35089     * adds a Masonry Brick
35090     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35091     */
35092     addBrick : function(cfg)
35093     {
35094         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35095         //this.register(cn);
35096         cn.parentId = this.id;
35097         cn.render(this.el);
35098         return cn;
35099     },
35100     
35101     /**
35102     * register a Masonry Brick
35103     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35104     */
35105     
35106     register : function(brick)
35107     {
35108         this.bricks.push(brick);
35109         brick.masonryId = this.id;
35110     },
35111     
35112     /**
35113     * clear all the Masonry Brick
35114     */
35115     clearAll : function()
35116     {
35117         this.bricks = [];
35118         //this.getChildContainer().dom.innerHTML = "";
35119         this.el.dom.innerHTML = '';
35120     },
35121     
35122     getSelected : function()
35123     {
35124         if (!this.selectedBrick) {
35125             return false;
35126         }
35127         
35128         return this.selectedBrick;
35129     }
35130 });
35131
35132 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35133     
35134     groups: {},
35135      /**
35136     * register a Masonry Layout
35137     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35138     */
35139     
35140     register : function(layout)
35141     {
35142         this.groups[layout.id] = layout;
35143     },
35144     /**
35145     * fetch a  Masonry Layout based on the masonry layout ID
35146     * @param {string} the masonry layout to add
35147     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35148     */
35149     
35150     get: function(layout_id) {
35151         if (typeof(this.groups[layout_id]) == 'undefined') {
35152             return false;
35153         }
35154         return this.groups[layout_id] ;
35155     }
35156     
35157     
35158     
35159 });
35160
35161  
35162
35163  /**
35164  *
35165  * This is based on 
35166  * http://masonry.desandro.com
35167  *
35168  * The idea is to render all the bricks based on vertical width...
35169  *
35170  * The original code extends 'outlayer' - we might need to use that....
35171  * 
35172  */
35173
35174
35175 /**
35176  * @class Roo.bootstrap.LayoutMasonryAuto
35177  * @extends Roo.bootstrap.Component
35178  * Bootstrap Layout Masonry class
35179  * 
35180  * @constructor
35181  * Create a new Element
35182  * @param {Object} config The config object
35183  */
35184
35185 Roo.bootstrap.LayoutMasonryAuto = function(config){
35186     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35187 };
35188
35189 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35190     
35191       /**
35192      * @cfg {Boolean} isFitWidth  - resize the width..
35193      */   
35194     isFitWidth : false,  // options..
35195     /**
35196      * @cfg {Boolean} isOriginLeft = left align?
35197      */   
35198     isOriginLeft : true,
35199     /**
35200      * @cfg {Boolean} isOriginTop = top align?
35201      */   
35202     isOriginTop : false,
35203     /**
35204      * @cfg {Boolean} isLayoutInstant = no animation?
35205      */   
35206     isLayoutInstant : false, // needed?
35207     /**
35208      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35209      */   
35210     isResizingContainer : true,
35211     /**
35212      * @cfg {Number} columnWidth  width of the columns 
35213      */   
35214     
35215     columnWidth : 0,
35216     
35217     /**
35218      * @cfg {Number} maxCols maximum number of columns
35219      */   
35220     
35221     maxCols: 0,
35222     /**
35223      * @cfg {Number} padHeight padding below box..
35224      */   
35225     
35226     padHeight : 10, 
35227     
35228     /**
35229      * @cfg {Boolean} isAutoInitial defalut true
35230      */   
35231     
35232     isAutoInitial : true, 
35233     
35234     // private?
35235     gutter : 0,
35236     
35237     containerWidth: 0,
35238     initialColumnWidth : 0,
35239     currentSize : null,
35240     
35241     colYs : null, // array.
35242     maxY : 0,
35243     padWidth: 10,
35244     
35245     
35246     tag: 'div',
35247     cls: '',
35248     bricks: null, //CompositeElement
35249     cols : 0, // array?
35250     // element : null, // wrapped now this.el
35251     _isLayoutInited : null, 
35252     
35253     
35254     getAutoCreate : function(){
35255         
35256         var cfg = {
35257             tag: this.tag,
35258             cls: 'blog-masonary-wrapper ' + this.cls,
35259             cn : {
35260                 cls : 'mas-boxes masonary'
35261             }
35262         };
35263         
35264         return cfg;
35265     },
35266     
35267     getChildContainer: function( )
35268     {
35269         if (this.boxesEl) {
35270             return this.boxesEl;
35271         }
35272         
35273         this.boxesEl = this.el.select('.mas-boxes').first();
35274         
35275         return this.boxesEl;
35276     },
35277     
35278     
35279     initEvents : function()
35280     {
35281         var _this = this;
35282         
35283         if(this.isAutoInitial){
35284             Roo.log('hook children rendered');
35285             this.on('childrenrendered', function() {
35286                 Roo.log('children rendered');
35287                 _this.initial();
35288             } ,this);
35289         }
35290         
35291     },
35292     
35293     initial : function()
35294     {
35295         this.reloadItems();
35296
35297         this.currentSize = this.el.getBox(true);
35298
35299         /// was window resize... - let's see if this works..
35300         Roo.EventManager.onWindowResize(this.resize, this); 
35301
35302         if(!this.isAutoInitial){
35303             this.layout();
35304             return;
35305         }
35306         
35307         this.layout.defer(500,this);
35308     },
35309     
35310     reloadItems: function()
35311     {
35312         this.bricks = this.el.select('.masonry-brick', true);
35313         
35314         this.bricks.each(function(b) {
35315             //Roo.log(b.getSize());
35316             if (!b.attr('originalwidth')) {
35317                 b.attr('originalwidth',  b.getSize().width);
35318             }
35319             
35320         });
35321         
35322         Roo.log(this.bricks.elements.length);
35323     },
35324     
35325     resize : function()
35326     {
35327         Roo.log('resize');
35328         var cs = this.el.getBox(true);
35329         
35330         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35331             Roo.log("no change in with or X");
35332             return;
35333         }
35334         this.currentSize = cs;
35335         this.layout();
35336     },
35337     
35338     layout : function()
35339     {
35340          Roo.log('layout');
35341         this._resetLayout();
35342         //this._manageStamps();
35343       
35344         // don't animate first layout
35345         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35346         this.layoutItems( isInstant );
35347       
35348         // flag for initalized
35349         this._isLayoutInited = true;
35350     },
35351     
35352     layoutItems : function( isInstant )
35353     {
35354         //var items = this._getItemsForLayout( this.items );
35355         // original code supports filtering layout items.. we just ignore it..
35356         
35357         this._layoutItems( this.bricks , isInstant );
35358       
35359         this._postLayout();
35360     },
35361     _layoutItems : function ( items , isInstant)
35362     {
35363        //this.fireEvent( 'layout', this, items );
35364     
35365
35366         if ( !items || !items.elements.length ) {
35367           // no items, emit event with empty array
35368             return;
35369         }
35370
35371         var queue = [];
35372         items.each(function(item) {
35373             Roo.log("layout item");
35374             Roo.log(item);
35375             // get x/y object from method
35376             var position = this._getItemLayoutPosition( item );
35377             // enqueue
35378             position.item = item;
35379             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35380             queue.push( position );
35381         }, this);
35382       
35383         this._processLayoutQueue( queue );
35384     },
35385     /** Sets position of item in DOM
35386     * @param {Element} item
35387     * @param {Number} x - horizontal position
35388     * @param {Number} y - vertical position
35389     * @param {Boolean} isInstant - disables transitions
35390     */
35391     _processLayoutQueue : function( queue )
35392     {
35393         for ( var i=0, len = queue.length; i < len; i++ ) {
35394             var obj = queue[i];
35395             obj.item.position('absolute');
35396             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35397         }
35398     },
35399       
35400     
35401     /**
35402     * Any logic you want to do after each layout,
35403     * i.e. size the container
35404     */
35405     _postLayout : function()
35406     {
35407         this.resizeContainer();
35408     },
35409     
35410     resizeContainer : function()
35411     {
35412         if ( !this.isResizingContainer ) {
35413             return;
35414         }
35415         var size = this._getContainerSize();
35416         if ( size ) {
35417             this.el.setSize(size.width,size.height);
35418             this.boxesEl.setSize(size.width,size.height);
35419         }
35420     },
35421     
35422     
35423     
35424     _resetLayout : function()
35425     {
35426         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35427         this.colWidth = this.el.getWidth();
35428         //this.gutter = this.el.getWidth(); 
35429         
35430         this.measureColumns();
35431
35432         // reset column Y
35433         var i = this.cols;
35434         this.colYs = [];
35435         while (i--) {
35436             this.colYs.push( 0 );
35437         }
35438     
35439         this.maxY = 0;
35440     },
35441
35442     measureColumns : function()
35443     {
35444         this.getContainerWidth();
35445       // if columnWidth is 0, default to outerWidth of first item
35446         if ( !this.columnWidth ) {
35447             var firstItem = this.bricks.first();
35448             Roo.log(firstItem);
35449             this.columnWidth  = this.containerWidth;
35450             if (firstItem && firstItem.attr('originalwidth') ) {
35451                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35452             }
35453             // columnWidth fall back to item of first element
35454             Roo.log("set column width?");
35455                         this.initialColumnWidth = this.columnWidth  ;
35456
35457             // if first elem has no width, default to size of container
35458             
35459         }
35460         
35461         
35462         if (this.initialColumnWidth) {
35463             this.columnWidth = this.initialColumnWidth;
35464         }
35465         
35466         
35467             
35468         // column width is fixed at the top - however if container width get's smaller we should
35469         // reduce it...
35470         
35471         // this bit calcs how man columns..
35472             
35473         var columnWidth = this.columnWidth += this.gutter;
35474       
35475         // calculate columns
35476         var containerWidth = this.containerWidth + this.gutter;
35477         
35478         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35479         // fix rounding errors, typically with gutters
35480         var excess = columnWidth - containerWidth % columnWidth;
35481         
35482         
35483         // if overshoot is less than a pixel, round up, otherwise floor it
35484         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35485         cols = Math[ mathMethod ]( cols );
35486         this.cols = Math.max( cols, 1 );
35487         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35488         
35489          // padding positioning..
35490         var totalColWidth = this.cols * this.columnWidth;
35491         var padavail = this.containerWidth - totalColWidth;
35492         // so for 2 columns - we need 3 'pads'
35493         
35494         var padNeeded = (1+this.cols) * this.padWidth;
35495         
35496         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35497         
35498         this.columnWidth += padExtra
35499         //this.padWidth = Math.floor(padavail /  ( this.cols));
35500         
35501         // adjust colum width so that padding is fixed??
35502         
35503         // we have 3 columns ... total = width * 3
35504         // we have X left over... that should be used by 
35505         
35506         //if (this.expandC) {
35507             
35508         //}
35509         
35510         
35511         
35512     },
35513     
35514     getContainerWidth : function()
35515     {
35516        /* // container is parent if fit width
35517         var container = this.isFitWidth ? this.element.parentNode : this.element;
35518         // check that this.size and size are there
35519         // IE8 triggers resize on body size change, so they might not be
35520         
35521         var size = getSize( container );  //FIXME
35522         this.containerWidth = size && size.innerWidth; //FIXME
35523         */
35524          
35525         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35526         
35527     },
35528     
35529     _getItemLayoutPosition : function( item )  // what is item?
35530     {
35531         // we resize the item to our columnWidth..
35532       
35533         item.setWidth(this.columnWidth);
35534         item.autoBoxAdjust  = false;
35535         
35536         var sz = item.getSize();
35537  
35538         // how many columns does this brick span
35539         var remainder = this.containerWidth % this.columnWidth;
35540         
35541         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35542         // round if off by 1 pixel, otherwise use ceil
35543         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35544         colSpan = Math.min( colSpan, this.cols );
35545         
35546         // normally this should be '1' as we dont' currently allow multi width columns..
35547         
35548         var colGroup = this._getColGroup( colSpan );
35549         // get the minimum Y value from the columns
35550         var minimumY = Math.min.apply( Math, colGroup );
35551         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35552         
35553         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35554          
35555         // position the brick
35556         var position = {
35557             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35558             y: this.currentSize.y + minimumY + this.padHeight
35559         };
35560         
35561         Roo.log(position);
35562         // apply setHeight to necessary columns
35563         var setHeight = minimumY + sz.height + this.padHeight;
35564         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35565         
35566         var setSpan = this.cols + 1 - colGroup.length;
35567         for ( var i = 0; i < setSpan; i++ ) {
35568           this.colYs[ shortColIndex + i ] = setHeight ;
35569         }
35570       
35571         return position;
35572     },
35573     
35574     /**
35575      * @param {Number} colSpan - number of columns the element spans
35576      * @returns {Array} colGroup
35577      */
35578     _getColGroup : function( colSpan )
35579     {
35580         if ( colSpan < 2 ) {
35581           // if brick spans only one column, use all the column Ys
35582           return this.colYs;
35583         }
35584       
35585         var colGroup = [];
35586         // how many different places could this brick fit horizontally
35587         var groupCount = this.cols + 1 - colSpan;
35588         // for each group potential horizontal position
35589         for ( var i = 0; i < groupCount; i++ ) {
35590           // make an array of colY values for that one group
35591           var groupColYs = this.colYs.slice( i, i + colSpan );
35592           // and get the max value of the array
35593           colGroup[i] = Math.max.apply( Math, groupColYs );
35594         }
35595         return colGroup;
35596     },
35597     /*
35598     _manageStamp : function( stamp )
35599     {
35600         var stampSize =  stamp.getSize();
35601         var offset = stamp.getBox();
35602         // get the columns that this stamp affects
35603         var firstX = this.isOriginLeft ? offset.x : offset.right;
35604         var lastX = firstX + stampSize.width;
35605         var firstCol = Math.floor( firstX / this.columnWidth );
35606         firstCol = Math.max( 0, firstCol );
35607         
35608         var lastCol = Math.floor( lastX / this.columnWidth );
35609         // lastCol should not go over if multiple of columnWidth #425
35610         lastCol -= lastX % this.columnWidth ? 0 : 1;
35611         lastCol = Math.min( this.cols - 1, lastCol );
35612         
35613         // set colYs to bottom of the stamp
35614         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35615             stampSize.height;
35616             
35617         for ( var i = firstCol; i <= lastCol; i++ ) {
35618           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35619         }
35620     },
35621     */
35622     
35623     _getContainerSize : function()
35624     {
35625         this.maxY = Math.max.apply( Math, this.colYs );
35626         var size = {
35627             height: this.maxY
35628         };
35629       
35630         if ( this.isFitWidth ) {
35631             size.width = this._getContainerFitWidth();
35632         }
35633       
35634         return size;
35635     },
35636     
35637     _getContainerFitWidth : function()
35638     {
35639         var unusedCols = 0;
35640         // count unused columns
35641         var i = this.cols;
35642         while ( --i ) {
35643           if ( this.colYs[i] !== 0 ) {
35644             break;
35645           }
35646           unusedCols++;
35647         }
35648         // fit container to columns that have been used
35649         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35650     },
35651     
35652     needsResizeLayout : function()
35653     {
35654         var previousWidth = this.containerWidth;
35655         this.getContainerWidth();
35656         return previousWidth !== this.containerWidth;
35657     }
35658  
35659 });
35660
35661  
35662
35663  /*
35664  * - LGPL
35665  *
35666  * element
35667  * 
35668  */
35669
35670 /**
35671  * @class Roo.bootstrap.MasonryBrick
35672  * @extends Roo.bootstrap.Component
35673  * Bootstrap MasonryBrick class
35674  * 
35675  * @constructor
35676  * Create a new MasonryBrick
35677  * @param {Object} config The config object
35678  */
35679
35680 Roo.bootstrap.MasonryBrick = function(config){
35681     
35682     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35683     
35684     Roo.bootstrap.MasonryBrick.register(this);
35685     
35686     this.addEvents({
35687         // raw events
35688         /**
35689          * @event click
35690          * When a MasonryBrick is clcik
35691          * @param {Roo.bootstrap.MasonryBrick} this
35692          * @param {Roo.EventObject} e
35693          */
35694         "click" : true
35695     });
35696 };
35697
35698 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35699     
35700     /**
35701      * @cfg {String} title
35702      */   
35703     title : '',
35704     /**
35705      * @cfg {String} html
35706      */   
35707     html : '',
35708     /**
35709      * @cfg {String} bgimage
35710      */   
35711     bgimage : '',
35712     /**
35713      * @cfg {String} videourl
35714      */   
35715     videourl : '',
35716     /**
35717      * @cfg {String} cls
35718      */   
35719     cls : '',
35720     /**
35721      * @cfg {String} href
35722      */   
35723     href : '',
35724     /**
35725      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35726      */   
35727     size : 'xs',
35728     
35729     /**
35730      * @cfg {String} placetitle (center|bottom)
35731      */   
35732     placetitle : '',
35733     
35734     /**
35735      * @cfg {Boolean} isFitContainer defalut true
35736      */   
35737     isFitContainer : true, 
35738     
35739     /**
35740      * @cfg {Boolean} preventDefault defalut false
35741      */   
35742     preventDefault : false, 
35743     
35744     /**
35745      * @cfg {Boolean} inverse defalut false
35746      */   
35747     maskInverse : false, 
35748     
35749     getAutoCreate : function()
35750     {
35751         if(!this.isFitContainer){
35752             return this.getSplitAutoCreate();
35753         }
35754         
35755         var cls = 'masonry-brick masonry-brick-full';
35756         
35757         if(this.href.length){
35758             cls += ' masonry-brick-link';
35759         }
35760         
35761         if(this.bgimage.length){
35762             cls += ' masonry-brick-image';
35763         }
35764         
35765         if(this.maskInverse){
35766             cls += ' mask-inverse';
35767         }
35768         
35769         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35770             cls += ' enable-mask';
35771         }
35772         
35773         if(this.size){
35774             cls += ' masonry-' + this.size + '-brick';
35775         }
35776         
35777         if(this.placetitle.length){
35778             
35779             switch (this.placetitle) {
35780                 case 'center' :
35781                     cls += ' masonry-center-title';
35782                     break;
35783                 case 'bottom' :
35784                     cls += ' masonry-bottom-title';
35785                     break;
35786                 default:
35787                     break;
35788             }
35789             
35790         } else {
35791             if(!this.html.length && !this.bgimage.length){
35792                 cls += ' masonry-center-title';
35793             }
35794
35795             if(!this.html.length && this.bgimage.length){
35796                 cls += ' masonry-bottom-title';
35797             }
35798         }
35799         
35800         if(this.cls){
35801             cls += ' ' + this.cls;
35802         }
35803         
35804         var cfg = {
35805             tag: (this.href.length) ? 'a' : 'div',
35806             cls: cls,
35807             cn: [
35808                 {
35809                     tag: 'div',
35810                     cls: 'masonry-brick-mask'
35811                 },
35812                 {
35813                     tag: 'div',
35814                     cls: 'masonry-brick-paragraph',
35815                     cn: []
35816                 }
35817             ]
35818         };
35819         
35820         if(this.href.length){
35821             cfg.href = this.href;
35822         }
35823         
35824         var cn = cfg.cn[1].cn;
35825         
35826         if(this.title.length){
35827             cn.push({
35828                 tag: 'h4',
35829                 cls: 'masonry-brick-title',
35830                 html: this.title
35831             });
35832         }
35833         
35834         if(this.html.length){
35835             cn.push({
35836                 tag: 'p',
35837                 cls: 'masonry-brick-text',
35838                 html: this.html
35839             });
35840         }
35841         
35842         if (!this.title.length && !this.html.length) {
35843             cfg.cn[1].cls += ' hide';
35844         }
35845         
35846         if(this.bgimage.length){
35847             cfg.cn.push({
35848                 tag: 'img',
35849                 cls: 'masonry-brick-image-view',
35850                 src: this.bgimage
35851             });
35852         }
35853         
35854         if(this.videourl.length){
35855             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35856             // youtube support only?
35857             cfg.cn.push({
35858                 tag: 'iframe',
35859                 cls: 'masonry-brick-image-view',
35860                 src: vurl,
35861                 frameborder : 0,
35862                 allowfullscreen : true
35863             });
35864         }
35865         
35866         return cfg;
35867         
35868     },
35869     
35870     getSplitAutoCreate : function()
35871     {
35872         var cls = 'masonry-brick masonry-brick-split';
35873         
35874         if(this.href.length){
35875             cls += ' masonry-brick-link';
35876         }
35877         
35878         if(this.bgimage.length){
35879             cls += ' masonry-brick-image';
35880         }
35881         
35882         if(this.size){
35883             cls += ' masonry-' + this.size + '-brick';
35884         }
35885         
35886         switch (this.placetitle) {
35887             case 'center' :
35888                 cls += ' masonry-center-title';
35889                 break;
35890             case 'bottom' :
35891                 cls += ' masonry-bottom-title';
35892                 break;
35893             default:
35894                 if(!this.bgimage.length){
35895                     cls += ' masonry-center-title';
35896                 }
35897
35898                 if(this.bgimage.length){
35899                     cls += ' masonry-bottom-title';
35900                 }
35901                 break;
35902         }
35903         
35904         if(this.cls){
35905             cls += ' ' + this.cls;
35906         }
35907         
35908         var cfg = {
35909             tag: (this.href.length) ? 'a' : 'div',
35910             cls: cls,
35911             cn: [
35912                 {
35913                     tag: 'div',
35914                     cls: 'masonry-brick-split-head',
35915                     cn: [
35916                         {
35917                             tag: 'div',
35918                             cls: 'masonry-brick-paragraph',
35919                             cn: []
35920                         }
35921                     ]
35922                 },
35923                 {
35924                     tag: 'div',
35925                     cls: 'masonry-brick-split-body',
35926                     cn: []
35927                 }
35928             ]
35929         };
35930         
35931         if(this.href.length){
35932             cfg.href = this.href;
35933         }
35934         
35935         if(this.title.length){
35936             cfg.cn[0].cn[0].cn.push({
35937                 tag: 'h4',
35938                 cls: 'masonry-brick-title',
35939                 html: this.title
35940             });
35941         }
35942         
35943         if(this.html.length){
35944             cfg.cn[1].cn.push({
35945                 tag: 'p',
35946                 cls: 'masonry-brick-text',
35947                 html: this.html
35948             });
35949         }
35950
35951         if(this.bgimage.length){
35952             cfg.cn[0].cn.push({
35953                 tag: 'img',
35954                 cls: 'masonry-brick-image-view',
35955                 src: this.bgimage
35956             });
35957         }
35958         
35959         if(this.videourl.length){
35960             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35961             // youtube support only?
35962             cfg.cn[0].cn.cn.push({
35963                 tag: 'iframe',
35964                 cls: 'masonry-brick-image-view',
35965                 src: vurl,
35966                 frameborder : 0,
35967                 allowfullscreen : true
35968             });
35969         }
35970         
35971         return cfg;
35972     },
35973     
35974     initEvents: function() 
35975     {
35976         switch (this.size) {
35977             case 'xs' :
35978                 this.x = 1;
35979                 this.y = 1;
35980                 break;
35981             case 'sm' :
35982                 this.x = 2;
35983                 this.y = 2;
35984                 break;
35985             case 'md' :
35986             case 'md-left' :
35987             case 'md-right' :
35988                 this.x = 3;
35989                 this.y = 3;
35990                 break;
35991             case 'tall' :
35992                 this.x = 2;
35993                 this.y = 3;
35994                 break;
35995             case 'wide' :
35996                 this.x = 3;
35997                 this.y = 2;
35998                 break;
35999             case 'wide-thin' :
36000                 this.x = 3;
36001                 this.y = 1;
36002                 break;
36003                         
36004             default :
36005                 break;
36006         }
36007         
36008         if(Roo.isTouch){
36009             this.el.on('touchstart', this.onTouchStart, this);
36010             this.el.on('touchmove', this.onTouchMove, this);
36011             this.el.on('touchend', this.onTouchEnd, this);
36012             this.el.on('contextmenu', this.onContextMenu, this);
36013         } else {
36014             this.el.on('mouseenter'  ,this.enter, this);
36015             this.el.on('mouseleave', this.leave, this);
36016             this.el.on('click', this.onClick, this);
36017         }
36018         
36019         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36020             this.parent().bricks.push(this);   
36021         }
36022         
36023     },
36024     
36025     onClick: function(e, el)
36026     {
36027         var time = this.endTimer - this.startTimer;
36028         // Roo.log(e.preventDefault());
36029         if(Roo.isTouch){
36030             if(time > 1000){
36031                 e.preventDefault();
36032                 return;
36033             }
36034         }
36035         
36036         if(!this.preventDefault){
36037             return;
36038         }
36039         
36040         e.preventDefault();
36041         
36042         if (this.activeClass != '') {
36043             this.selectBrick();
36044         }
36045         
36046         this.fireEvent('click', this, e);
36047     },
36048     
36049     enter: function(e, el)
36050     {
36051         e.preventDefault();
36052         
36053         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36054             return;
36055         }
36056         
36057         if(this.bgimage.length && this.html.length){
36058             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36059         }
36060     },
36061     
36062     leave: function(e, el)
36063     {
36064         e.preventDefault();
36065         
36066         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36067             return;
36068         }
36069         
36070         if(this.bgimage.length && this.html.length){
36071             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36072         }
36073     },
36074     
36075     onTouchStart: function(e, el)
36076     {
36077 //        e.preventDefault();
36078         
36079         this.touchmoved = false;
36080         
36081         if(!this.isFitContainer){
36082             return;
36083         }
36084         
36085         if(!this.bgimage.length || !this.html.length){
36086             return;
36087         }
36088         
36089         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36090         
36091         this.timer = new Date().getTime();
36092         
36093     },
36094     
36095     onTouchMove: function(e, el)
36096     {
36097         this.touchmoved = true;
36098     },
36099     
36100     onContextMenu : function(e,el)
36101     {
36102         e.preventDefault();
36103         e.stopPropagation();
36104         return false;
36105     },
36106     
36107     onTouchEnd: function(e, el)
36108     {
36109 //        e.preventDefault();
36110         
36111         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36112         
36113             this.leave(e,el);
36114             
36115             return;
36116         }
36117         
36118         if(!this.bgimage.length || !this.html.length){
36119             
36120             if(this.href.length){
36121                 window.location.href = this.href;
36122             }
36123             
36124             return;
36125         }
36126         
36127         if(!this.isFitContainer){
36128             return;
36129         }
36130         
36131         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36132         
36133         window.location.href = this.href;
36134     },
36135     
36136     //selection on single brick only
36137     selectBrick : function() {
36138         
36139         if (!this.parentId) {
36140             return;
36141         }
36142         
36143         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36144         var index = m.selectedBrick.indexOf(this.id);
36145         
36146         if ( index > -1) {
36147             m.selectedBrick.splice(index,1);
36148             this.el.removeClass(this.activeClass);
36149             return;
36150         }
36151         
36152         for(var i = 0; i < m.selectedBrick.length; i++) {
36153             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36154             b.el.removeClass(b.activeClass);
36155         }
36156         
36157         m.selectedBrick = [];
36158         
36159         m.selectedBrick.push(this.id);
36160         this.el.addClass(this.activeClass);
36161         return;
36162     },
36163     
36164     isSelected : function(){
36165         return this.el.hasClass(this.activeClass);
36166         
36167     }
36168 });
36169
36170 Roo.apply(Roo.bootstrap.MasonryBrick, {
36171     
36172     //groups: {},
36173     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36174      /**
36175     * register a Masonry Brick
36176     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36177     */
36178     
36179     register : function(brick)
36180     {
36181         //this.groups[brick.id] = brick;
36182         this.groups.add(brick.id, brick);
36183     },
36184     /**
36185     * fetch a  masonry brick based on the masonry brick ID
36186     * @param {string} the masonry brick to add
36187     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36188     */
36189     
36190     get: function(brick_id) 
36191     {
36192         // if (typeof(this.groups[brick_id]) == 'undefined') {
36193         //     return false;
36194         // }
36195         // return this.groups[brick_id] ;
36196         
36197         if(this.groups.key(brick_id)) {
36198             return this.groups.key(brick_id);
36199         }
36200         
36201         return false;
36202     }
36203     
36204     
36205     
36206 });
36207
36208  /*
36209  * - LGPL
36210  *
36211  * element
36212  * 
36213  */
36214
36215 /**
36216  * @class Roo.bootstrap.Brick
36217  * @extends Roo.bootstrap.Component
36218  * Bootstrap Brick class
36219  * 
36220  * @constructor
36221  * Create a new Brick
36222  * @param {Object} config The config object
36223  */
36224
36225 Roo.bootstrap.Brick = function(config){
36226     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36227     
36228     this.addEvents({
36229         // raw events
36230         /**
36231          * @event click
36232          * When a Brick is click
36233          * @param {Roo.bootstrap.Brick} this
36234          * @param {Roo.EventObject} e
36235          */
36236         "click" : true
36237     });
36238 };
36239
36240 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36241     
36242     /**
36243      * @cfg {String} title
36244      */   
36245     title : '',
36246     /**
36247      * @cfg {String} html
36248      */   
36249     html : '',
36250     /**
36251      * @cfg {String} bgimage
36252      */   
36253     bgimage : '',
36254     /**
36255      * @cfg {String} cls
36256      */   
36257     cls : '',
36258     /**
36259      * @cfg {String} href
36260      */   
36261     href : '',
36262     /**
36263      * @cfg {String} video
36264      */   
36265     video : '',
36266     /**
36267      * @cfg {Boolean} square
36268      */   
36269     square : true,
36270     
36271     getAutoCreate : function()
36272     {
36273         var cls = 'roo-brick';
36274         
36275         if(this.href.length){
36276             cls += ' roo-brick-link';
36277         }
36278         
36279         if(this.bgimage.length){
36280             cls += ' roo-brick-image';
36281         }
36282         
36283         if(!this.html.length && !this.bgimage.length){
36284             cls += ' roo-brick-center-title';
36285         }
36286         
36287         if(!this.html.length && this.bgimage.length){
36288             cls += ' roo-brick-bottom-title';
36289         }
36290         
36291         if(this.cls){
36292             cls += ' ' + this.cls;
36293         }
36294         
36295         var cfg = {
36296             tag: (this.href.length) ? 'a' : 'div',
36297             cls: cls,
36298             cn: [
36299                 {
36300                     tag: 'div',
36301                     cls: 'roo-brick-paragraph',
36302                     cn: []
36303                 }
36304             ]
36305         };
36306         
36307         if(this.href.length){
36308             cfg.href = this.href;
36309         }
36310         
36311         var cn = cfg.cn[0].cn;
36312         
36313         if(this.title.length){
36314             cn.push({
36315                 tag: 'h4',
36316                 cls: 'roo-brick-title',
36317                 html: this.title
36318             });
36319         }
36320         
36321         if(this.html.length){
36322             cn.push({
36323                 tag: 'p',
36324                 cls: 'roo-brick-text',
36325                 html: this.html
36326             });
36327         } else {
36328             cn.cls += ' hide';
36329         }
36330         
36331         if(this.bgimage.length){
36332             cfg.cn.push({
36333                 tag: 'img',
36334                 cls: 'roo-brick-image-view',
36335                 src: this.bgimage
36336             });
36337         }
36338         
36339         return cfg;
36340     },
36341     
36342     initEvents: function() 
36343     {
36344         if(this.title.length || this.html.length){
36345             this.el.on('mouseenter'  ,this.enter, this);
36346             this.el.on('mouseleave', this.leave, this);
36347         }
36348         
36349         Roo.EventManager.onWindowResize(this.resize, this); 
36350         
36351         if(this.bgimage.length){
36352             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36353             this.imageEl.on('load', this.onImageLoad, this);
36354             return;
36355         }
36356         
36357         this.resize();
36358     },
36359     
36360     onImageLoad : function()
36361     {
36362         this.resize();
36363     },
36364     
36365     resize : function()
36366     {
36367         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36368         
36369         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36370         
36371         if(this.bgimage.length){
36372             var image = this.el.select('.roo-brick-image-view', true).first();
36373             
36374             image.setWidth(paragraph.getWidth());
36375             
36376             if(this.square){
36377                 image.setHeight(paragraph.getWidth());
36378             }
36379             
36380             this.el.setHeight(image.getHeight());
36381             paragraph.setHeight(image.getHeight());
36382             
36383         }
36384         
36385     },
36386     
36387     enter: function(e, el)
36388     {
36389         e.preventDefault();
36390         
36391         if(this.bgimage.length){
36392             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36393             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36394         }
36395     },
36396     
36397     leave: function(e, el)
36398     {
36399         e.preventDefault();
36400         
36401         if(this.bgimage.length){
36402             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36403             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36404         }
36405     }
36406     
36407 });
36408
36409  
36410
36411  /*
36412  * - LGPL
36413  *
36414  * Number field 
36415  */
36416
36417 /**
36418  * @class Roo.bootstrap.NumberField
36419  * @extends Roo.bootstrap.Input
36420  * Bootstrap NumberField class
36421  * 
36422  * 
36423  * 
36424  * 
36425  * @constructor
36426  * Create a new NumberField
36427  * @param {Object} config The config object
36428  */
36429
36430 Roo.bootstrap.NumberField = function(config){
36431     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36432 };
36433
36434 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36435     
36436     /**
36437      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36438      */
36439     allowDecimals : true,
36440     /**
36441      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36442      */
36443     decimalSeparator : ".",
36444     /**
36445      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36446      */
36447     decimalPrecision : 2,
36448     /**
36449      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36450      */
36451     allowNegative : true,
36452     
36453     /**
36454      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36455      */
36456     allowZero: true,
36457     /**
36458      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36459      */
36460     minValue : Number.NEGATIVE_INFINITY,
36461     /**
36462      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36463      */
36464     maxValue : Number.MAX_VALUE,
36465     /**
36466      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36467      */
36468     minText : "The minimum value for this field is {0}",
36469     /**
36470      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36471      */
36472     maxText : "The maximum value for this field is {0}",
36473     /**
36474      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36475      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36476      */
36477     nanText : "{0} is not a valid number",
36478     /**
36479      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36480      */
36481     thousandsDelimiter : false,
36482     /**
36483      * @cfg {String} valueAlign alignment of value
36484      */
36485     valueAlign : "left",
36486
36487     getAutoCreate : function()
36488     {
36489         var hiddenInput = {
36490             tag: 'input',
36491             type: 'hidden',
36492             id: Roo.id(),
36493             cls: 'hidden-number-input'
36494         };
36495         
36496         if (this.name) {
36497             hiddenInput.name = this.name;
36498         }
36499         
36500         this.name = '';
36501         
36502         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36503         
36504         this.name = hiddenInput.name;
36505         
36506         if(cfg.cn.length > 0) {
36507             cfg.cn.push(hiddenInput);
36508         }
36509         
36510         return cfg;
36511     },
36512
36513     // private
36514     initEvents : function()
36515     {   
36516         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36517         
36518         var allowed = "0123456789";
36519         
36520         if(this.allowDecimals){
36521             allowed += this.decimalSeparator;
36522         }
36523         
36524         if(this.allowNegative){
36525             allowed += "-";
36526         }
36527         
36528         if(this.thousandsDelimiter) {
36529             allowed += ",";
36530         }
36531         
36532         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36533         
36534         var keyPress = function(e){
36535             
36536             var k = e.getKey();
36537             
36538             var c = e.getCharCode();
36539             
36540             if(
36541                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36542                     allowed.indexOf(String.fromCharCode(c)) === -1
36543             ){
36544                 e.stopEvent();
36545                 return;
36546             }
36547             
36548             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36549                 return;
36550             }
36551             
36552             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36553                 e.stopEvent();
36554             }
36555         };
36556         
36557         this.el.on("keypress", keyPress, this);
36558     },
36559     
36560     validateValue : function(value)
36561     {
36562         
36563         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36564             return false;
36565         }
36566         
36567         var num = this.parseValue(value);
36568         
36569         if(isNaN(num)){
36570             this.markInvalid(String.format(this.nanText, value));
36571             return false;
36572         }
36573         
36574         if(num < this.minValue){
36575             this.markInvalid(String.format(this.minText, this.minValue));
36576             return false;
36577         }
36578         
36579         if(num > this.maxValue){
36580             this.markInvalid(String.format(this.maxText, this.maxValue));
36581             return false;
36582         }
36583         
36584         return true;
36585     },
36586
36587     getValue : function()
36588     {
36589         var v = this.hiddenEl().getValue();
36590         
36591         return this.fixPrecision(this.parseValue(v));
36592     },
36593
36594     parseValue : function(value)
36595     {
36596         if(this.thousandsDelimiter) {
36597             value += "";
36598             r = new RegExp(",", "g");
36599             value = value.replace(r, "");
36600         }
36601         
36602         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36603         return isNaN(value) ? '' : value;
36604     },
36605
36606     fixPrecision : function(value)
36607     {
36608         if(this.thousandsDelimiter) {
36609             value += "";
36610             r = new RegExp(",", "g");
36611             value = value.replace(r, "");
36612         }
36613         
36614         var nan = isNaN(value);
36615         
36616         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36617             return nan ? '' : value;
36618         }
36619         return parseFloat(value).toFixed(this.decimalPrecision);
36620     },
36621
36622     setValue : function(v)
36623     {
36624         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36625         
36626         this.value = v;
36627         
36628         if(this.rendered){
36629             
36630             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36631             
36632             this.inputEl().dom.value = (v == '') ? '' :
36633                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36634             
36635             if(!this.allowZero && v === '0') {
36636                 this.hiddenEl().dom.value = '';
36637                 this.inputEl().dom.value = '';
36638             }
36639             
36640             this.validate();
36641         }
36642     },
36643
36644     decimalPrecisionFcn : function(v)
36645     {
36646         return Math.floor(v);
36647     },
36648
36649     beforeBlur : function()
36650     {
36651         var v = this.parseValue(this.getRawValue());
36652         
36653         if(v || v === 0 || v === ''){
36654             this.setValue(v);
36655         }
36656     },
36657     
36658     hiddenEl : function()
36659     {
36660         return this.el.select('input.hidden-number-input',true).first();
36661     }
36662     
36663 });
36664
36665  
36666
36667 /*
36668 * Licence: LGPL
36669 */
36670
36671 /**
36672  * @class Roo.bootstrap.DocumentSlider
36673  * @extends Roo.bootstrap.Component
36674  * Bootstrap DocumentSlider class
36675  * 
36676  * @constructor
36677  * Create a new DocumentViewer
36678  * @param {Object} config The config object
36679  */
36680
36681 Roo.bootstrap.DocumentSlider = function(config){
36682     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36683     
36684     this.files = [];
36685     
36686     this.addEvents({
36687         /**
36688          * @event initial
36689          * Fire after initEvent
36690          * @param {Roo.bootstrap.DocumentSlider} this
36691          */
36692         "initial" : true,
36693         /**
36694          * @event update
36695          * Fire after update
36696          * @param {Roo.bootstrap.DocumentSlider} this
36697          */
36698         "update" : true,
36699         /**
36700          * @event click
36701          * Fire after click
36702          * @param {Roo.bootstrap.DocumentSlider} this
36703          */
36704         "click" : true
36705     });
36706 };
36707
36708 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36709     
36710     files : false,
36711     
36712     indicator : 0,
36713     
36714     getAutoCreate : function()
36715     {
36716         var cfg = {
36717             tag : 'div',
36718             cls : 'roo-document-slider',
36719             cn : [
36720                 {
36721                     tag : 'div',
36722                     cls : 'roo-document-slider-header',
36723                     cn : [
36724                         {
36725                             tag : 'div',
36726                             cls : 'roo-document-slider-header-title'
36727                         }
36728                     ]
36729                 },
36730                 {
36731                     tag : 'div',
36732                     cls : 'roo-document-slider-body',
36733                     cn : [
36734                         {
36735                             tag : 'div',
36736                             cls : 'roo-document-slider-prev',
36737                             cn : [
36738                                 {
36739                                     tag : 'i',
36740                                     cls : 'fa fa-chevron-left'
36741                                 }
36742                             ]
36743                         },
36744                         {
36745                             tag : 'div',
36746                             cls : 'roo-document-slider-thumb',
36747                             cn : [
36748                                 {
36749                                     tag : 'img',
36750                                     cls : 'roo-document-slider-image'
36751                                 }
36752                             ]
36753                         },
36754                         {
36755                             tag : 'div',
36756                             cls : 'roo-document-slider-next',
36757                             cn : [
36758                                 {
36759                                     tag : 'i',
36760                                     cls : 'fa fa-chevron-right'
36761                                 }
36762                             ]
36763                         }
36764                     ]
36765                 }
36766             ]
36767         };
36768         
36769         return cfg;
36770     },
36771     
36772     initEvents : function()
36773     {
36774         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36775         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36776         
36777         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36778         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36779         
36780         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36781         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36782         
36783         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36784         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36785         
36786         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36787         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36788         
36789         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36790         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36791         
36792         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36793         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36794         
36795         this.thumbEl.on('click', this.onClick, this);
36796         
36797         this.prevIndicator.on('click', this.prev, this);
36798         
36799         this.nextIndicator.on('click', this.next, this);
36800         
36801     },
36802     
36803     initial : function()
36804     {
36805         if(this.files.length){
36806             this.indicator = 1;
36807             this.update()
36808         }
36809         
36810         this.fireEvent('initial', this);
36811     },
36812     
36813     update : function()
36814     {
36815         this.imageEl.attr('src', this.files[this.indicator - 1]);
36816         
36817         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36818         
36819         this.prevIndicator.show();
36820         
36821         if(this.indicator == 1){
36822             this.prevIndicator.hide();
36823         }
36824         
36825         this.nextIndicator.show();
36826         
36827         if(this.indicator == this.files.length){
36828             this.nextIndicator.hide();
36829         }
36830         
36831         this.thumbEl.scrollTo('top');
36832         
36833         this.fireEvent('update', this);
36834     },
36835     
36836     onClick : function(e)
36837     {
36838         e.preventDefault();
36839         
36840         this.fireEvent('click', this);
36841     },
36842     
36843     prev : function(e)
36844     {
36845         e.preventDefault();
36846         
36847         this.indicator = Math.max(1, this.indicator - 1);
36848         
36849         this.update();
36850     },
36851     
36852     next : function(e)
36853     {
36854         e.preventDefault();
36855         
36856         this.indicator = Math.min(this.files.length, this.indicator + 1);
36857         
36858         this.update();
36859     }
36860 });
36861 /*
36862  * - LGPL
36863  *
36864  * RadioSet
36865  *
36866  *
36867  */
36868
36869 /**
36870  * @class Roo.bootstrap.RadioSet
36871  * @extends Roo.bootstrap.Input
36872  * Bootstrap RadioSet class
36873  * @cfg {String} indicatorpos (left|right) default left
36874  * @cfg {Boolean} inline (true|false) inline the element (default true)
36875  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36876  * @constructor
36877  * Create a new RadioSet
36878  * @param {Object} config The config object
36879  */
36880
36881 Roo.bootstrap.RadioSet = function(config){
36882     
36883     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36884     
36885     this.radioes = [];
36886     
36887     Roo.bootstrap.RadioSet.register(this);
36888     
36889     this.addEvents({
36890         /**
36891         * @event check
36892         * Fires when the element is checked or unchecked.
36893         * @param {Roo.bootstrap.RadioSet} this This radio
36894         * @param {Roo.bootstrap.Radio} item The checked item
36895         */
36896        check : true,
36897        /**
36898         * @event click
36899         * Fires when the element is click.
36900         * @param {Roo.bootstrap.RadioSet} this This radio set
36901         * @param {Roo.bootstrap.Radio} item The checked item
36902         * @param {Roo.EventObject} e The event object
36903         */
36904        click : true
36905     });
36906     
36907 };
36908
36909 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36910
36911     radioes : false,
36912     
36913     inline : true,
36914     
36915     weight : '',
36916     
36917     indicatorpos : 'left',
36918     
36919     getAutoCreate : function()
36920     {
36921         var label = {
36922             tag : 'label',
36923             cls : 'roo-radio-set-label',
36924             cn : [
36925                 {
36926                     tag : 'span',
36927                     html : this.fieldLabel
36928                 }
36929             ]
36930         };
36931         if (Roo.bootstrap.version == 3) {
36932             
36933             
36934             if(this.indicatorpos == 'left'){
36935                 label.cn.unshift({
36936                     tag : 'i',
36937                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36938                     tooltip : 'This field is required'
36939                 });
36940             } else {
36941                 label.cn.push({
36942                     tag : 'i',
36943                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36944                     tooltip : 'This field is required'
36945                 });
36946             }
36947         }
36948         var items = {
36949             tag : 'div',
36950             cls : 'roo-radio-set-items'
36951         };
36952         
36953         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36954         
36955         if (align === 'left' && this.fieldLabel.length) {
36956             
36957             items = {
36958                 cls : "roo-radio-set-right", 
36959                 cn: [
36960                     items
36961                 ]
36962             };
36963             
36964             if(this.labelWidth > 12){
36965                 label.style = "width: " + this.labelWidth + 'px';
36966             }
36967             
36968             if(this.labelWidth < 13 && this.labelmd == 0){
36969                 this.labelmd = this.labelWidth;
36970             }
36971             
36972             if(this.labellg > 0){
36973                 label.cls += ' col-lg-' + this.labellg;
36974                 items.cls += ' col-lg-' + (12 - this.labellg);
36975             }
36976             
36977             if(this.labelmd > 0){
36978                 label.cls += ' col-md-' + this.labelmd;
36979                 items.cls += ' col-md-' + (12 - this.labelmd);
36980             }
36981             
36982             if(this.labelsm > 0){
36983                 label.cls += ' col-sm-' + this.labelsm;
36984                 items.cls += ' col-sm-' + (12 - this.labelsm);
36985             }
36986             
36987             if(this.labelxs > 0){
36988                 label.cls += ' col-xs-' + this.labelxs;
36989                 items.cls += ' col-xs-' + (12 - this.labelxs);
36990             }
36991         }
36992         
36993         var cfg = {
36994             tag : 'div',
36995             cls : 'roo-radio-set',
36996             cn : [
36997                 {
36998                     tag : 'input',
36999                     cls : 'roo-radio-set-input',
37000                     type : 'hidden',
37001                     name : this.name,
37002                     value : this.value ? this.value :  ''
37003                 },
37004                 label,
37005                 items
37006             ]
37007         };
37008         
37009         if(this.weight.length){
37010             cfg.cls += ' roo-radio-' + this.weight;
37011         }
37012         
37013         if(this.inline) {
37014             cfg.cls += ' roo-radio-set-inline';
37015         }
37016         
37017         var settings=this;
37018         ['xs','sm','md','lg'].map(function(size){
37019             if (settings[size]) {
37020                 cfg.cls += ' col-' + size + '-' + settings[size];
37021             }
37022         });
37023         
37024         return cfg;
37025         
37026     },
37027
37028     initEvents : function()
37029     {
37030         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37031         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37032         
37033         if(!this.fieldLabel.length){
37034             this.labelEl.hide();
37035         }
37036         
37037         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37038         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37039         
37040         this.indicator = this.indicatorEl();
37041         
37042         if(this.indicator){
37043             this.indicator.addClass('invisible');
37044         }
37045         
37046         this.originalValue = this.getValue();
37047         
37048     },
37049     
37050     inputEl: function ()
37051     {
37052         return this.el.select('.roo-radio-set-input', true).first();
37053     },
37054     
37055     getChildContainer : function()
37056     {
37057         return this.itemsEl;
37058     },
37059     
37060     register : function(item)
37061     {
37062         this.radioes.push(item);
37063         
37064     },
37065     
37066     validate : function()
37067     {   
37068         if(this.getVisibilityEl().hasClass('hidden')){
37069             return true;
37070         }
37071         
37072         var valid = false;
37073         
37074         Roo.each(this.radioes, function(i){
37075             if(!i.checked){
37076                 return;
37077             }
37078             
37079             valid = true;
37080             return false;
37081         });
37082         
37083         if(this.allowBlank) {
37084             return true;
37085         }
37086         
37087         if(this.disabled || valid){
37088             this.markValid();
37089             return true;
37090         }
37091         
37092         this.markInvalid();
37093         return false;
37094         
37095     },
37096     
37097     markValid : function()
37098     {
37099         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37100             this.indicatorEl().removeClass('visible');
37101             this.indicatorEl().addClass('invisible');
37102         }
37103         
37104         
37105         if (Roo.bootstrap.version == 3) {
37106             this.el.removeClass([this.invalidClass, this.validClass]);
37107             this.el.addClass(this.validClass);
37108         } else {
37109             this.el.removeClass(['is-invalid','is-valid']);
37110             this.el.addClass(['is-valid']);
37111         }
37112         this.fireEvent('valid', this);
37113     },
37114     
37115     markInvalid : function(msg)
37116     {
37117         if(this.allowBlank || this.disabled){
37118             return;
37119         }
37120         
37121         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37122             this.indicatorEl().removeClass('invisible');
37123             this.indicatorEl().addClass('visible');
37124         }
37125         if (Roo.bootstrap.version == 3) {
37126             this.el.removeClass([this.invalidClass, this.validClass]);
37127             this.el.addClass(this.invalidClass);
37128         } else {
37129             this.el.removeClass(['is-invalid','is-valid']);
37130             this.el.addClass(['is-invalid']);
37131         }
37132         
37133         this.fireEvent('invalid', this, msg);
37134         
37135     },
37136     
37137     setValue : function(v, suppressEvent)
37138     {   
37139         if(this.value === v){
37140             return;
37141         }
37142         
37143         this.value = v;
37144         
37145         if(this.rendered){
37146             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37147         }
37148         
37149         Roo.each(this.radioes, function(i){
37150             i.checked = false;
37151             i.el.removeClass('checked');
37152         });
37153         
37154         Roo.each(this.radioes, function(i){
37155             
37156             if(i.value === v || i.value.toString() === v.toString()){
37157                 i.checked = true;
37158                 i.el.addClass('checked');
37159                 
37160                 if(suppressEvent !== true){
37161                     this.fireEvent('check', this, i);
37162                 }
37163                 
37164                 return false;
37165             }
37166             
37167         }, this);
37168         
37169         this.validate();
37170     },
37171     
37172     clearInvalid : function(){
37173         
37174         if(!this.el || this.preventMark){
37175             return;
37176         }
37177         
37178         this.el.removeClass([this.invalidClass]);
37179         
37180         this.fireEvent('valid', this);
37181     }
37182     
37183 });
37184
37185 Roo.apply(Roo.bootstrap.RadioSet, {
37186     
37187     groups: {},
37188     
37189     register : function(set)
37190     {
37191         this.groups[set.name] = set;
37192     },
37193     
37194     get: function(name) 
37195     {
37196         if (typeof(this.groups[name]) == 'undefined') {
37197             return false;
37198         }
37199         
37200         return this.groups[name] ;
37201     }
37202     
37203 });
37204 /*
37205  * Based on:
37206  * Ext JS Library 1.1.1
37207  * Copyright(c) 2006-2007, Ext JS, LLC.
37208  *
37209  * Originally Released Under LGPL - original licence link has changed is not relivant.
37210  *
37211  * Fork - LGPL
37212  * <script type="text/javascript">
37213  */
37214
37215
37216 /**
37217  * @class Roo.bootstrap.SplitBar
37218  * @extends Roo.util.Observable
37219  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37220  * <br><br>
37221  * Usage:
37222  * <pre><code>
37223 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37224                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37225 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37226 split.minSize = 100;
37227 split.maxSize = 600;
37228 split.animate = true;
37229 split.on('moved', splitterMoved);
37230 </code></pre>
37231  * @constructor
37232  * Create a new SplitBar
37233  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37234  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37235  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37236  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37237                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37238                         position of the SplitBar).
37239  */
37240 Roo.bootstrap.SplitBar = function(cfg){
37241     
37242     /** @private */
37243     
37244     //{
37245     //  dragElement : elm
37246     //  resizingElement: el,
37247         // optional..
37248     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37249     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37250         // existingProxy ???
37251     //}
37252     
37253     this.el = Roo.get(cfg.dragElement, true);
37254     this.el.dom.unselectable = "on";
37255     /** @private */
37256     this.resizingEl = Roo.get(cfg.resizingElement, true);
37257
37258     /**
37259      * @private
37260      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37261      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37262      * @type Number
37263      */
37264     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37265     
37266     /**
37267      * The minimum size of the resizing element. (Defaults to 0)
37268      * @type Number
37269      */
37270     this.minSize = 0;
37271     
37272     /**
37273      * The maximum size of the resizing element. (Defaults to 2000)
37274      * @type Number
37275      */
37276     this.maxSize = 2000;
37277     
37278     /**
37279      * Whether to animate the transition to the new size
37280      * @type Boolean
37281      */
37282     this.animate = false;
37283     
37284     /**
37285      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37286      * @type Boolean
37287      */
37288     this.useShim = false;
37289     
37290     /** @private */
37291     this.shim = null;
37292     
37293     if(!cfg.existingProxy){
37294         /** @private */
37295         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37296     }else{
37297         this.proxy = Roo.get(cfg.existingProxy).dom;
37298     }
37299     /** @private */
37300     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37301     
37302     /** @private */
37303     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37304     
37305     /** @private */
37306     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37307     
37308     /** @private */
37309     this.dragSpecs = {};
37310     
37311     /**
37312      * @private The adapter to use to positon and resize elements
37313      */
37314     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37315     this.adapter.init(this);
37316     
37317     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37318         /** @private */
37319         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37320         this.el.addClass("roo-splitbar-h");
37321     }else{
37322         /** @private */
37323         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37324         this.el.addClass("roo-splitbar-v");
37325     }
37326     
37327     this.addEvents({
37328         /**
37329          * @event resize
37330          * Fires when the splitter is moved (alias for {@link #event-moved})
37331          * @param {Roo.bootstrap.SplitBar} this
37332          * @param {Number} newSize the new width or height
37333          */
37334         "resize" : true,
37335         /**
37336          * @event moved
37337          * Fires when the splitter is moved
37338          * @param {Roo.bootstrap.SplitBar} this
37339          * @param {Number} newSize the new width or height
37340          */
37341         "moved" : true,
37342         /**
37343          * @event beforeresize
37344          * Fires before the splitter is dragged
37345          * @param {Roo.bootstrap.SplitBar} this
37346          */
37347         "beforeresize" : true,
37348
37349         "beforeapply" : true
37350     });
37351
37352     Roo.util.Observable.call(this);
37353 };
37354
37355 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37356     onStartProxyDrag : function(x, y){
37357         this.fireEvent("beforeresize", this);
37358         if(!this.overlay){
37359             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37360             o.unselectable();
37361             o.enableDisplayMode("block");
37362             // all splitbars share the same overlay
37363             Roo.bootstrap.SplitBar.prototype.overlay = o;
37364         }
37365         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37366         this.overlay.show();
37367         Roo.get(this.proxy).setDisplayed("block");
37368         var size = this.adapter.getElementSize(this);
37369         this.activeMinSize = this.getMinimumSize();;
37370         this.activeMaxSize = this.getMaximumSize();;
37371         var c1 = size - this.activeMinSize;
37372         var c2 = Math.max(this.activeMaxSize - size, 0);
37373         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37374             this.dd.resetConstraints();
37375             this.dd.setXConstraint(
37376                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37377                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37378             );
37379             this.dd.setYConstraint(0, 0);
37380         }else{
37381             this.dd.resetConstraints();
37382             this.dd.setXConstraint(0, 0);
37383             this.dd.setYConstraint(
37384                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37385                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37386             );
37387          }
37388         this.dragSpecs.startSize = size;
37389         this.dragSpecs.startPoint = [x, y];
37390         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37391     },
37392     
37393     /** 
37394      * @private Called after the drag operation by the DDProxy
37395      */
37396     onEndProxyDrag : function(e){
37397         Roo.get(this.proxy).setDisplayed(false);
37398         var endPoint = Roo.lib.Event.getXY(e);
37399         if(this.overlay){
37400             this.overlay.hide();
37401         }
37402         var newSize;
37403         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37404             newSize = this.dragSpecs.startSize + 
37405                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37406                     endPoint[0] - this.dragSpecs.startPoint[0] :
37407                     this.dragSpecs.startPoint[0] - endPoint[0]
37408                 );
37409         }else{
37410             newSize = this.dragSpecs.startSize + 
37411                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37412                     endPoint[1] - this.dragSpecs.startPoint[1] :
37413                     this.dragSpecs.startPoint[1] - endPoint[1]
37414                 );
37415         }
37416         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37417         if(newSize != this.dragSpecs.startSize){
37418             if(this.fireEvent('beforeapply', this, newSize) !== false){
37419                 this.adapter.setElementSize(this, newSize);
37420                 this.fireEvent("moved", this, newSize);
37421                 this.fireEvent("resize", this, newSize);
37422             }
37423         }
37424     },
37425     
37426     /**
37427      * Get the adapter this SplitBar uses
37428      * @return The adapter object
37429      */
37430     getAdapter : function(){
37431         return this.adapter;
37432     },
37433     
37434     /**
37435      * Set the adapter this SplitBar uses
37436      * @param {Object} adapter A SplitBar adapter object
37437      */
37438     setAdapter : function(adapter){
37439         this.adapter = adapter;
37440         this.adapter.init(this);
37441     },
37442     
37443     /**
37444      * Gets the minimum size for the resizing element
37445      * @return {Number} The minimum size
37446      */
37447     getMinimumSize : function(){
37448         return this.minSize;
37449     },
37450     
37451     /**
37452      * Sets the minimum size for the resizing element
37453      * @param {Number} minSize The minimum size
37454      */
37455     setMinimumSize : function(minSize){
37456         this.minSize = minSize;
37457     },
37458     
37459     /**
37460      * Gets the maximum size for the resizing element
37461      * @return {Number} The maximum size
37462      */
37463     getMaximumSize : function(){
37464         return this.maxSize;
37465     },
37466     
37467     /**
37468      * Sets the maximum size for the resizing element
37469      * @param {Number} maxSize The maximum size
37470      */
37471     setMaximumSize : function(maxSize){
37472         this.maxSize = maxSize;
37473     },
37474     
37475     /**
37476      * Sets the initialize size for the resizing element
37477      * @param {Number} size The initial size
37478      */
37479     setCurrentSize : function(size){
37480         var oldAnimate = this.animate;
37481         this.animate = false;
37482         this.adapter.setElementSize(this, size);
37483         this.animate = oldAnimate;
37484     },
37485     
37486     /**
37487      * Destroy this splitbar. 
37488      * @param {Boolean} removeEl True to remove the element
37489      */
37490     destroy : function(removeEl){
37491         if(this.shim){
37492             this.shim.remove();
37493         }
37494         this.dd.unreg();
37495         this.proxy.parentNode.removeChild(this.proxy);
37496         if(removeEl){
37497             this.el.remove();
37498         }
37499     }
37500 });
37501
37502 /**
37503  * @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.
37504  */
37505 Roo.bootstrap.SplitBar.createProxy = function(dir){
37506     var proxy = new Roo.Element(document.createElement("div"));
37507     proxy.unselectable();
37508     var cls = 'roo-splitbar-proxy';
37509     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37510     document.body.appendChild(proxy.dom);
37511     return proxy.dom;
37512 };
37513
37514 /** 
37515  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37516  * Default Adapter. It assumes the splitter and resizing element are not positioned
37517  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37518  */
37519 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37520 };
37521
37522 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37523     // do nothing for now
37524     init : function(s){
37525     
37526     },
37527     /**
37528      * Called before drag operations to get the current size of the resizing element. 
37529      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37530      */
37531      getElementSize : function(s){
37532         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37533             return s.resizingEl.getWidth();
37534         }else{
37535             return s.resizingEl.getHeight();
37536         }
37537     },
37538     
37539     /**
37540      * Called after drag operations to set the size of the resizing element.
37541      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37542      * @param {Number} newSize The new size to set
37543      * @param {Function} onComplete A function to be invoked when resizing is complete
37544      */
37545     setElementSize : function(s, newSize, onComplete){
37546         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37547             if(!s.animate){
37548                 s.resizingEl.setWidth(newSize);
37549                 if(onComplete){
37550                     onComplete(s, newSize);
37551                 }
37552             }else{
37553                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37554             }
37555         }else{
37556             
37557             if(!s.animate){
37558                 s.resizingEl.setHeight(newSize);
37559                 if(onComplete){
37560                     onComplete(s, newSize);
37561                 }
37562             }else{
37563                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37564             }
37565         }
37566     }
37567 };
37568
37569 /** 
37570  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37571  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37572  * Adapter that  moves the splitter element to align with the resized sizing element. 
37573  * Used with an absolute positioned SplitBar.
37574  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37575  * document.body, make sure you assign an id to the body element.
37576  */
37577 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37578     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37579     this.container = Roo.get(container);
37580 };
37581
37582 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37583     init : function(s){
37584         this.basic.init(s);
37585     },
37586     
37587     getElementSize : function(s){
37588         return this.basic.getElementSize(s);
37589     },
37590     
37591     setElementSize : function(s, newSize, onComplete){
37592         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37593     },
37594     
37595     moveSplitter : function(s){
37596         var yes = Roo.bootstrap.SplitBar;
37597         switch(s.placement){
37598             case yes.LEFT:
37599                 s.el.setX(s.resizingEl.getRight());
37600                 break;
37601             case yes.RIGHT:
37602                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37603                 break;
37604             case yes.TOP:
37605                 s.el.setY(s.resizingEl.getBottom());
37606                 break;
37607             case yes.BOTTOM:
37608                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37609                 break;
37610         }
37611     }
37612 };
37613
37614 /**
37615  * Orientation constant - Create a vertical SplitBar
37616  * @static
37617  * @type Number
37618  */
37619 Roo.bootstrap.SplitBar.VERTICAL = 1;
37620
37621 /**
37622  * Orientation constant - Create a horizontal SplitBar
37623  * @static
37624  * @type Number
37625  */
37626 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37627
37628 /**
37629  * Placement constant - The resizing element is to the left of the splitter element
37630  * @static
37631  * @type Number
37632  */
37633 Roo.bootstrap.SplitBar.LEFT = 1;
37634
37635 /**
37636  * Placement constant - The resizing element is to the right of the splitter element
37637  * @static
37638  * @type Number
37639  */
37640 Roo.bootstrap.SplitBar.RIGHT = 2;
37641
37642 /**
37643  * Placement constant - The resizing element is positioned above the splitter element
37644  * @static
37645  * @type Number
37646  */
37647 Roo.bootstrap.SplitBar.TOP = 3;
37648
37649 /**
37650  * Placement constant - The resizing element is positioned under splitter element
37651  * @static
37652  * @type Number
37653  */
37654 Roo.bootstrap.SplitBar.BOTTOM = 4;
37655 Roo.namespace("Roo.bootstrap.layout");/*
37656  * Based on:
37657  * Ext JS Library 1.1.1
37658  * Copyright(c) 2006-2007, Ext JS, LLC.
37659  *
37660  * Originally Released Under LGPL - original licence link has changed is not relivant.
37661  *
37662  * Fork - LGPL
37663  * <script type="text/javascript">
37664  */
37665
37666 /**
37667  * @class Roo.bootstrap.layout.Manager
37668  * @extends Roo.bootstrap.Component
37669  * Base class for layout managers.
37670  */
37671 Roo.bootstrap.layout.Manager = function(config)
37672 {
37673     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37674
37675
37676
37677
37678
37679     /** false to disable window resize monitoring @type Boolean */
37680     this.monitorWindowResize = true;
37681     this.regions = {};
37682     this.addEvents({
37683         /**
37684          * @event layout
37685          * Fires when a layout is performed.
37686          * @param {Roo.LayoutManager} this
37687          */
37688         "layout" : true,
37689         /**
37690          * @event regionresized
37691          * Fires when the user resizes a region.
37692          * @param {Roo.LayoutRegion} region The resized region
37693          * @param {Number} newSize The new size (width for east/west, height for north/south)
37694          */
37695         "regionresized" : true,
37696         /**
37697          * @event regioncollapsed
37698          * Fires when a region is collapsed.
37699          * @param {Roo.LayoutRegion} region The collapsed region
37700          */
37701         "regioncollapsed" : true,
37702         /**
37703          * @event regionexpanded
37704          * Fires when a region is expanded.
37705          * @param {Roo.LayoutRegion} region The expanded region
37706          */
37707         "regionexpanded" : true
37708     });
37709     this.updating = false;
37710
37711     if (config.el) {
37712         this.el = Roo.get(config.el);
37713         this.initEvents();
37714     }
37715
37716 };
37717
37718 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37719
37720
37721     regions : null,
37722
37723     monitorWindowResize : true,
37724
37725
37726     updating : false,
37727
37728
37729     onRender : function(ct, position)
37730     {
37731         if(!this.el){
37732             this.el = Roo.get(ct);
37733             this.initEvents();
37734         }
37735         //this.fireEvent('render',this);
37736     },
37737
37738
37739     initEvents: function()
37740     {
37741
37742
37743         // ie scrollbar fix
37744         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37745             document.body.scroll = "no";
37746         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37747             this.el.position('relative');
37748         }
37749         this.id = this.el.id;
37750         this.el.addClass("roo-layout-container");
37751         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37752         if(this.el.dom != document.body ) {
37753             this.el.on('resize', this.layout,this);
37754             this.el.on('show', this.layout,this);
37755         }
37756
37757     },
37758
37759     /**
37760      * Returns true if this layout is currently being updated
37761      * @return {Boolean}
37762      */
37763     isUpdating : function(){
37764         return this.updating;
37765     },
37766
37767     /**
37768      * Suspend the LayoutManager from doing auto-layouts while
37769      * making multiple add or remove calls
37770      */
37771     beginUpdate : function(){
37772         this.updating = true;
37773     },
37774
37775     /**
37776      * Restore auto-layouts and optionally disable the manager from performing a layout
37777      * @param {Boolean} noLayout true to disable a layout update
37778      */
37779     endUpdate : function(noLayout){
37780         this.updating = false;
37781         if(!noLayout){
37782             this.layout();
37783         }
37784     },
37785
37786     layout: function(){
37787         // abstract...
37788     },
37789
37790     onRegionResized : function(region, newSize){
37791         this.fireEvent("regionresized", region, newSize);
37792         this.layout();
37793     },
37794
37795     onRegionCollapsed : function(region){
37796         this.fireEvent("regioncollapsed", region);
37797     },
37798
37799     onRegionExpanded : function(region){
37800         this.fireEvent("regionexpanded", region);
37801     },
37802
37803     /**
37804      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37805      * performs box-model adjustments.
37806      * @return {Object} The size as an object {width: (the width), height: (the height)}
37807      */
37808     getViewSize : function()
37809     {
37810         var size;
37811         if(this.el.dom != document.body){
37812             size = this.el.getSize();
37813         }else{
37814             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37815         }
37816         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37817         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37818         return size;
37819     },
37820
37821     /**
37822      * Returns the Element this layout is bound to.
37823      * @return {Roo.Element}
37824      */
37825     getEl : function(){
37826         return this.el;
37827     },
37828
37829     /**
37830      * Returns the specified region.
37831      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37832      * @return {Roo.LayoutRegion}
37833      */
37834     getRegion : function(target){
37835         return this.regions[target.toLowerCase()];
37836     },
37837
37838     onWindowResize : function(){
37839         if(this.monitorWindowResize){
37840             this.layout();
37841         }
37842     }
37843 });
37844 /*
37845  * Based on:
37846  * Ext JS Library 1.1.1
37847  * Copyright(c) 2006-2007, Ext JS, LLC.
37848  *
37849  * Originally Released Under LGPL - original licence link has changed is not relivant.
37850  *
37851  * Fork - LGPL
37852  * <script type="text/javascript">
37853  */
37854 /**
37855  * @class Roo.bootstrap.layout.Border
37856  * @extends Roo.bootstrap.layout.Manager
37857  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37858  * please see: examples/bootstrap/nested.html<br><br>
37859  
37860 <b>The container the layout is rendered into can be either the body element or any other element.
37861 If it is not the body element, the container needs to either be an absolute positioned element,
37862 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37863 the container size if it is not the body element.</b>
37864
37865 * @constructor
37866 * Create a new Border
37867 * @param {Object} config Configuration options
37868  */
37869 Roo.bootstrap.layout.Border = function(config){
37870     config = config || {};
37871     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37872     
37873     
37874     
37875     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37876         if(config[region]){
37877             config[region].region = region;
37878             this.addRegion(config[region]);
37879         }
37880     },this);
37881     
37882 };
37883
37884 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37885
37886 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37887     
37888     parent : false, // this might point to a 'nest' or a ???
37889     
37890     /**
37891      * Creates and adds a new region if it doesn't already exist.
37892      * @param {String} target The target region key (north, south, east, west or center).
37893      * @param {Object} config The regions config object
37894      * @return {BorderLayoutRegion} The new region
37895      */
37896     addRegion : function(config)
37897     {
37898         if(!this.regions[config.region]){
37899             var r = this.factory(config);
37900             this.bindRegion(r);
37901         }
37902         return this.regions[config.region];
37903     },
37904
37905     // private (kinda)
37906     bindRegion : function(r){
37907         this.regions[r.config.region] = r;
37908         
37909         r.on("visibilitychange",    this.layout, this);
37910         r.on("paneladded",          this.layout, this);
37911         r.on("panelremoved",        this.layout, this);
37912         r.on("invalidated",         this.layout, this);
37913         r.on("resized",             this.onRegionResized, this);
37914         r.on("collapsed",           this.onRegionCollapsed, this);
37915         r.on("expanded",            this.onRegionExpanded, this);
37916     },
37917
37918     /**
37919      * Performs a layout update.
37920      */
37921     layout : function()
37922     {
37923         if(this.updating) {
37924             return;
37925         }
37926         
37927         // render all the rebions if they have not been done alreayd?
37928         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37929             if(this.regions[region] && !this.regions[region].bodyEl){
37930                 this.regions[region].onRender(this.el)
37931             }
37932         },this);
37933         
37934         var size = this.getViewSize();
37935         var w = size.width;
37936         var h = size.height;
37937         var centerW = w;
37938         var centerH = h;
37939         var centerY = 0;
37940         var centerX = 0;
37941         //var x = 0, y = 0;
37942
37943         var rs = this.regions;
37944         var north = rs["north"];
37945         var south = rs["south"]; 
37946         var west = rs["west"];
37947         var east = rs["east"];
37948         var center = rs["center"];
37949         //if(this.hideOnLayout){ // not supported anymore
37950             //c.el.setStyle("display", "none");
37951         //}
37952         if(north && north.isVisible()){
37953             var b = north.getBox();
37954             var m = north.getMargins();
37955             b.width = w - (m.left+m.right);
37956             b.x = m.left;
37957             b.y = m.top;
37958             centerY = b.height + b.y + m.bottom;
37959             centerH -= centerY;
37960             north.updateBox(this.safeBox(b));
37961         }
37962         if(south && south.isVisible()){
37963             var b = south.getBox();
37964             var m = south.getMargins();
37965             b.width = w - (m.left+m.right);
37966             b.x = m.left;
37967             var totalHeight = (b.height + m.top + m.bottom);
37968             b.y = h - totalHeight + m.top;
37969             centerH -= totalHeight;
37970             south.updateBox(this.safeBox(b));
37971         }
37972         if(west && west.isVisible()){
37973             var b = west.getBox();
37974             var m = west.getMargins();
37975             b.height = centerH - (m.top+m.bottom);
37976             b.x = m.left;
37977             b.y = centerY + m.top;
37978             var totalWidth = (b.width + m.left + m.right);
37979             centerX += totalWidth;
37980             centerW -= totalWidth;
37981             west.updateBox(this.safeBox(b));
37982         }
37983         if(east && east.isVisible()){
37984             var b = east.getBox();
37985             var m = east.getMargins();
37986             b.height = centerH - (m.top+m.bottom);
37987             var totalWidth = (b.width + m.left + m.right);
37988             b.x = w - totalWidth + m.left;
37989             b.y = centerY + m.top;
37990             centerW -= totalWidth;
37991             east.updateBox(this.safeBox(b));
37992         }
37993         if(center){
37994             var m = center.getMargins();
37995             var centerBox = {
37996                 x: centerX + m.left,
37997                 y: centerY + m.top,
37998                 width: centerW - (m.left+m.right),
37999                 height: centerH - (m.top+m.bottom)
38000             };
38001             //if(this.hideOnLayout){
38002                 //center.el.setStyle("display", "block");
38003             //}
38004             center.updateBox(this.safeBox(centerBox));
38005         }
38006         this.el.repaint();
38007         this.fireEvent("layout", this);
38008     },
38009
38010     // private
38011     safeBox : function(box){
38012         box.width = Math.max(0, box.width);
38013         box.height = Math.max(0, box.height);
38014         return box;
38015     },
38016
38017     /**
38018      * Adds a ContentPanel (or subclass) to this layout.
38019      * @param {String} target The target region key (north, south, east, west or center).
38020      * @param {Roo.ContentPanel} panel The panel to add
38021      * @return {Roo.ContentPanel} The added panel
38022      */
38023     add : function(target, panel){
38024          
38025         target = target.toLowerCase();
38026         return this.regions[target].add(panel);
38027     },
38028
38029     /**
38030      * Remove a ContentPanel (or subclass) to this layout.
38031      * @param {String} target The target region key (north, south, east, west or center).
38032      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38033      * @return {Roo.ContentPanel} The removed panel
38034      */
38035     remove : function(target, panel){
38036         target = target.toLowerCase();
38037         return this.regions[target].remove(panel);
38038     },
38039
38040     /**
38041      * Searches all regions for a panel with the specified id
38042      * @param {String} panelId
38043      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38044      */
38045     findPanel : function(panelId){
38046         var rs = this.regions;
38047         for(var target in rs){
38048             if(typeof rs[target] != "function"){
38049                 var p = rs[target].getPanel(panelId);
38050                 if(p){
38051                     return p;
38052                 }
38053             }
38054         }
38055         return null;
38056     },
38057
38058     /**
38059      * Searches all regions for a panel with the specified id and activates (shows) it.
38060      * @param {String/ContentPanel} panelId The panels id or the panel itself
38061      * @return {Roo.ContentPanel} The shown panel or null
38062      */
38063     showPanel : function(panelId) {
38064       var rs = this.regions;
38065       for(var target in rs){
38066          var r = rs[target];
38067          if(typeof r != "function"){
38068             if(r.hasPanel(panelId)){
38069                return r.showPanel(panelId);
38070             }
38071          }
38072       }
38073       return null;
38074    },
38075
38076    /**
38077      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38078      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38079      */
38080    /*
38081     restoreState : function(provider){
38082         if(!provider){
38083             provider = Roo.state.Manager;
38084         }
38085         var sm = new Roo.LayoutStateManager();
38086         sm.init(this, provider);
38087     },
38088 */
38089  
38090  
38091     /**
38092      * Adds a xtype elements to the layout.
38093      * <pre><code>
38094
38095 layout.addxtype({
38096        xtype : 'ContentPanel',
38097        region: 'west',
38098        items: [ .... ]
38099    }
38100 );
38101
38102 layout.addxtype({
38103         xtype : 'NestedLayoutPanel',
38104         region: 'west',
38105         layout: {
38106            center: { },
38107            west: { }   
38108         },
38109         items : [ ... list of content panels or nested layout panels.. ]
38110    }
38111 );
38112 </code></pre>
38113      * @param {Object} cfg Xtype definition of item to add.
38114      */
38115     addxtype : function(cfg)
38116     {
38117         // basically accepts a pannel...
38118         // can accept a layout region..!?!?
38119         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38120         
38121         
38122         // theory?  children can only be panels??
38123         
38124         //if (!cfg.xtype.match(/Panel$/)) {
38125         //    return false;
38126         //}
38127         var ret = false;
38128         
38129         if (typeof(cfg.region) == 'undefined') {
38130             Roo.log("Failed to add Panel, region was not set");
38131             Roo.log(cfg);
38132             return false;
38133         }
38134         var region = cfg.region;
38135         delete cfg.region;
38136         
38137           
38138         var xitems = [];
38139         if (cfg.items) {
38140             xitems = cfg.items;
38141             delete cfg.items;
38142         }
38143         var nb = false;
38144         
38145         if ( region == 'center') {
38146             Roo.log("Center: " + cfg.title);
38147         }
38148         
38149         
38150         switch(cfg.xtype) 
38151         {
38152             case 'Content':  // ContentPanel (el, cfg)
38153             case 'Scroll':  // ContentPanel (el, cfg)
38154             case 'View': 
38155                 cfg.autoCreate = cfg.autoCreate || true;
38156                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38157                 //} else {
38158                 //    var el = this.el.createChild();
38159                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38160                 //}
38161                 
38162                 this.add(region, ret);
38163                 break;
38164             
38165             /*
38166             case 'TreePanel': // our new panel!
38167                 cfg.el = this.el.createChild();
38168                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38169                 this.add(region, ret);
38170                 break;
38171             */
38172             
38173             case 'Nest': 
38174                 // create a new Layout (which is  a Border Layout...
38175                 
38176                 var clayout = cfg.layout;
38177                 clayout.el  = this.el.createChild();
38178                 clayout.items   = clayout.items  || [];
38179                 
38180                 delete cfg.layout;
38181                 
38182                 // replace this exitems with the clayout ones..
38183                 xitems = clayout.items;
38184                  
38185                 // force background off if it's in center...
38186                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38187                     cfg.background = false;
38188                 }
38189                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38190                 
38191                 
38192                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38193                 //console.log('adding nested layout panel '  + cfg.toSource());
38194                 this.add(region, ret);
38195                 nb = {}; /// find first...
38196                 break;
38197             
38198             case 'Grid':
38199                 
38200                 // needs grid and region
38201                 
38202                 //var el = this.getRegion(region).el.createChild();
38203                 /*
38204                  *var el = this.el.createChild();
38205                 // create the grid first...
38206                 cfg.grid.container = el;
38207                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38208                 */
38209                 
38210                 if (region == 'center' && this.active ) {
38211                     cfg.background = false;
38212                 }
38213                 
38214                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38215                 
38216                 this.add(region, ret);
38217                 /*
38218                 if (cfg.background) {
38219                     // render grid on panel activation (if panel background)
38220                     ret.on('activate', function(gp) {
38221                         if (!gp.grid.rendered) {
38222                     //        gp.grid.render(el);
38223                         }
38224                     });
38225                 } else {
38226                   //  cfg.grid.render(el);
38227                 }
38228                 */
38229                 break;
38230            
38231            
38232             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38233                 // it was the old xcomponent building that caused this before.
38234                 // espeically if border is the top element in the tree.
38235                 ret = this;
38236                 break; 
38237                 
38238                     
38239                 
38240                 
38241                 
38242             default:
38243                 /*
38244                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38245                     
38246                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38247                     this.add(region, ret);
38248                 } else {
38249                 */
38250                     Roo.log(cfg);
38251                     throw "Can not add '" + cfg.xtype + "' to Border";
38252                     return null;
38253              
38254                                 
38255              
38256         }
38257         this.beginUpdate();
38258         // add children..
38259         var region = '';
38260         var abn = {};
38261         Roo.each(xitems, function(i)  {
38262             region = nb && i.region ? i.region : false;
38263             
38264             var add = ret.addxtype(i);
38265            
38266             if (region) {
38267                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38268                 if (!i.background) {
38269                     abn[region] = nb[region] ;
38270                 }
38271             }
38272             
38273         });
38274         this.endUpdate();
38275
38276         // make the last non-background panel active..
38277         //if (nb) { Roo.log(abn); }
38278         if (nb) {
38279             
38280             for(var r in abn) {
38281                 region = this.getRegion(r);
38282                 if (region) {
38283                     // tried using nb[r], but it does not work..
38284                      
38285                     region.showPanel(abn[r]);
38286                    
38287                 }
38288             }
38289         }
38290         return ret;
38291         
38292     },
38293     
38294     
38295 // private
38296     factory : function(cfg)
38297     {
38298         
38299         var validRegions = Roo.bootstrap.layout.Border.regions;
38300
38301         var target = cfg.region;
38302         cfg.mgr = this;
38303         
38304         var r = Roo.bootstrap.layout;
38305         Roo.log(target);
38306         switch(target){
38307             case "north":
38308                 return new r.North(cfg);
38309             case "south":
38310                 return new r.South(cfg);
38311             case "east":
38312                 return new r.East(cfg);
38313             case "west":
38314                 return new r.West(cfg);
38315             case "center":
38316                 return new r.Center(cfg);
38317         }
38318         throw 'Layout region "'+target+'" not supported.';
38319     }
38320     
38321     
38322 });
38323  /*
38324  * Based on:
38325  * Ext JS Library 1.1.1
38326  * Copyright(c) 2006-2007, Ext JS, LLC.
38327  *
38328  * Originally Released Under LGPL - original licence link has changed is not relivant.
38329  *
38330  * Fork - LGPL
38331  * <script type="text/javascript">
38332  */
38333  
38334 /**
38335  * @class Roo.bootstrap.layout.Basic
38336  * @extends Roo.util.Observable
38337  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38338  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38339  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38340  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38341  * @cfg {string}   region  the region that it inhabits..
38342  * @cfg {bool}   skipConfig skip config?
38343  * 
38344
38345  */
38346 Roo.bootstrap.layout.Basic = function(config){
38347     
38348     this.mgr = config.mgr;
38349     
38350     this.position = config.region;
38351     
38352     var skipConfig = config.skipConfig;
38353     
38354     this.events = {
38355         /**
38356          * @scope Roo.BasicLayoutRegion
38357          */
38358         
38359         /**
38360          * @event beforeremove
38361          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38362          * @param {Roo.LayoutRegion} this
38363          * @param {Roo.ContentPanel} panel The panel
38364          * @param {Object} e The cancel event object
38365          */
38366         "beforeremove" : true,
38367         /**
38368          * @event invalidated
38369          * Fires when the layout for this region is changed.
38370          * @param {Roo.LayoutRegion} this
38371          */
38372         "invalidated" : true,
38373         /**
38374          * @event visibilitychange
38375          * Fires when this region is shown or hidden 
38376          * @param {Roo.LayoutRegion} this
38377          * @param {Boolean} visibility true or false
38378          */
38379         "visibilitychange" : true,
38380         /**
38381          * @event paneladded
38382          * Fires when a panel is added. 
38383          * @param {Roo.LayoutRegion} this
38384          * @param {Roo.ContentPanel} panel The panel
38385          */
38386         "paneladded" : true,
38387         /**
38388          * @event panelremoved
38389          * Fires when a panel is removed. 
38390          * @param {Roo.LayoutRegion} this
38391          * @param {Roo.ContentPanel} panel The panel
38392          */
38393         "panelremoved" : true,
38394         /**
38395          * @event beforecollapse
38396          * Fires when this region before collapse.
38397          * @param {Roo.LayoutRegion} this
38398          */
38399         "beforecollapse" : true,
38400         /**
38401          * @event collapsed
38402          * Fires when this region is collapsed.
38403          * @param {Roo.LayoutRegion} this
38404          */
38405         "collapsed" : true,
38406         /**
38407          * @event expanded
38408          * Fires when this region is expanded.
38409          * @param {Roo.LayoutRegion} this
38410          */
38411         "expanded" : true,
38412         /**
38413          * @event slideshow
38414          * Fires when this region is slid into view.
38415          * @param {Roo.LayoutRegion} this
38416          */
38417         "slideshow" : true,
38418         /**
38419          * @event slidehide
38420          * Fires when this region slides out of view. 
38421          * @param {Roo.LayoutRegion} this
38422          */
38423         "slidehide" : true,
38424         /**
38425          * @event panelactivated
38426          * Fires when a panel is activated. 
38427          * @param {Roo.LayoutRegion} this
38428          * @param {Roo.ContentPanel} panel The activated panel
38429          */
38430         "panelactivated" : true,
38431         /**
38432          * @event resized
38433          * Fires when the user resizes this region. 
38434          * @param {Roo.LayoutRegion} this
38435          * @param {Number} newSize The new size (width for east/west, height for north/south)
38436          */
38437         "resized" : true
38438     };
38439     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38440     this.panels = new Roo.util.MixedCollection();
38441     this.panels.getKey = this.getPanelId.createDelegate(this);
38442     this.box = null;
38443     this.activePanel = null;
38444     // ensure listeners are added...
38445     
38446     if (config.listeners || config.events) {
38447         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38448             listeners : config.listeners || {},
38449             events : config.events || {}
38450         });
38451     }
38452     
38453     if(skipConfig !== true){
38454         this.applyConfig(config);
38455     }
38456 };
38457
38458 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38459 {
38460     getPanelId : function(p){
38461         return p.getId();
38462     },
38463     
38464     applyConfig : function(config){
38465         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38466         this.config = config;
38467         
38468     },
38469     
38470     /**
38471      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38472      * the width, for horizontal (north, south) the height.
38473      * @param {Number} newSize The new width or height
38474      */
38475     resizeTo : function(newSize){
38476         var el = this.el ? this.el :
38477                  (this.activePanel ? this.activePanel.getEl() : null);
38478         if(el){
38479             switch(this.position){
38480                 case "east":
38481                 case "west":
38482                     el.setWidth(newSize);
38483                     this.fireEvent("resized", this, newSize);
38484                 break;
38485                 case "north":
38486                 case "south":
38487                     el.setHeight(newSize);
38488                     this.fireEvent("resized", this, newSize);
38489                 break;                
38490             }
38491         }
38492     },
38493     
38494     getBox : function(){
38495         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38496     },
38497     
38498     getMargins : function(){
38499         return this.margins;
38500     },
38501     
38502     updateBox : function(box){
38503         this.box = box;
38504         var el = this.activePanel.getEl();
38505         el.dom.style.left = box.x + "px";
38506         el.dom.style.top = box.y + "px";
38507         this.activePanel.setSize(box.width, box.height);
38508     },
38509     
38510     /**
38511      * Returns the container element for this region.
38512      * @return {Roo.Element}
38513      */
38514     getEl : function(){
38515         return this.activePanel;
38516     },
38517     
38518     /**
38519      * Returns true if this region is currently visible.
38520      * @return {Boolean}
38521      */
38522     isVisible : function(){
38523         return this.activePanel ? true : false;
38524     },
38525     
38526     setActivePanel : function(panel){
38527         panel = this.getPanel(panel);
38528         if(this.activePanel && this.activePanel != panel){
38529             this.activePanel.setActiveState(false);
38530             this.activePanel.getEl().setLeftTop(-10000,-10000);
38531         }
38532         this.activePanel = panel;
38533         panel.setActiveState(true);
38534         if(this.box){
38535             panel.setSize(this.box.width, this.box.height);
38536         }
38537         this.fireEvent("panelactivated", this, panel);
38538         this.fireEvent("invalidated");
38539     },
38540     
38541     /**
38542      * Show the specified panel.
38543      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38544      * @return {Roo.ContentPanel} The shown panel or null
38545      */
38546     showPanel : function(panel){
38547         panel = this.getPanel(panel);
38548         if(panel){
38549             this.setActivePanel(panel);
38550         }
38551         return panel;
38552     },
38553     
38554     /**
38555      * Get the active panel for this region.
38556      * @return {Roo.ContentPanel} The active panel or null
38557      */
38558     getActivePanel : function(){
38559         return this.activePanel;
38560     },
38561     
38562     /**
38563      * Add the passed ContentPanel(s)
38564      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38565      * @return {Roo.ContentPanel} The panel added (if only one was added)
38566      */
38567     add : function(panel){
38568         if(arguments.length > 1){
38569             for(var i = 0, len = arguments.length; i < len; i++) {
38570                 this.add(arguments[i]);
38571             }
38572             return null;
38573         }
38574         if(this.hasPanel(panel)){
38575             this.showPanel(panel);
38576             return panel;
38577         }
38578         var el = panel.getEl();
38579         if(el.dom.parentNode != this.mgr.el.dom){
38580             this.mgr.el.dom.appendChild(el.dom);
38581         }
38582         if(panel.setRegion){
38583             panel.setRegion(this);
38584         }
38585         this.panels.add(panel);
38586         el.setStyle("position", "absolute");
38587         if(!panel.background){
38588             this.setActivePanel(panel);
38589             if(this.config.initialSize && this.panels.getCount()==1){
38590                 this.resizeTo(this.config.initialSize);
38591             }
38592         }
38593         this.fireEvent("paneladded", this, panel);
38594         return panel;
38595     },
38596     
38597     /**
38598      * Returns true if the panel is in this region.
38599      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38600      * @return {Boolean}
38601      */
38602     hasPanel : function(panel){
38603         if(typeof panel == "object"){ // must be panel obj
38604             panel = panel.getId();
38605         }
38606         return this.getPanel(panel) ? true : false;
38607     },
38608     
38609     /**
38610      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38611      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38612      * @param {Boolean} preservePanel Overrides the config preservePanel option
38613      * @return {Roo.ContentPanel} The panel that was removed
38614      */
38615     remove : function(panel, preservePanel){
38616         panel = this.getPanel(panel);
38617         if(!panel){
38618             return null;
38619         }
38620         var e = {};
38621         this.fireEvent("beforeremove", this, panel, e);
38622         if(e.cancel === true){
38623             return null;
38624         }
38625         var panelId = panel.getId();
38626         this.panels.removeKey(panelId);
38627         return panel;
38628     },
38629     
38630     /**
38631      * Returns the panel specified or null if it's not in this region.
38632      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38633      * @return {Roo.ContentPanel}
38634      */
38635     getPanel : function(id){
38636         if(typeof id == "object"){ // must be panel obj
38637             return id;
38638         }
38639         return this.panels.get(id);
38640     },
38641     
38642     /**
38643      * Returns this regions position (north/south/east/west/center).
38644      * @return {String} 
38645      */
38646     getPosition: function(){
38647         return this.position;    
38648     }
38649 });/*
38650  * Based on:
38651  * Ext JS Library 1.1.1
38652  * Copyright(c) 2006-2007, Ext JS, LLC.
38653  *
38654  * Originally Released Under LGPL - original licence link has changed is not relivant.
38655  *
38656  * Fork - LGPL
38657  * <script type="text/javascript">
38658  */
38659  
38660 /**
38661  * @class Roo.bootstrap.layout.Region
38662  * @extends Roo.bootstrap.layout.Basic
38663  * This class represents a region in a layout manager.
38664  
38665  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38666  * @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})
38667  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38668  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38669  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38670  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38671  * @cfg {String}    title           The title for the region (overrides panel titles)
38672  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38673  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38674  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38675  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38676  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38677  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38678  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38679  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38680  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38681  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38682
38683  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38684  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38685  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38686  * @cfg {Number}    width           For East/West panels
38687  * @cfg {Number}    height          For North/South panels
38688  * @cfg {Boolean}   split           To show the splitter
38689  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38690  * 
38691  * @cfg {string}   cls             Extra CSS classes to add to region
38692  * 
38693  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38694  * @cfg {string}   region  the region that it inhabits..
38695  *
38696
38697  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38698  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38699
38700  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38701  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38702  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38703  */
38704 Roo.bootstrap.layout.Region = function(config)
38705 {
38706     this.applyConfig(config);
38707
38708     var mgr = config.mgr;
38709     var pos = config.region;
38710     config.skipConfig = true;
38711     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38712     
38713     if (mgr.el) {
38714         this.onRender(mgr.el);   
38715     }
38716      
38717     this.visible = true;
38718     this.collapsed = false;
38719     this.unrendered_panels = [];
38720 };
38721
38722 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38723
38724     position: '', // set by wrapper (eg. north/south etc..)
38725     unrendered_panels : null,  // unrendered panels.
38726     
38727     tabPosition : false,
38728     
38729     mgr: false, // points to 'Border'
38730     
38731     
38732     createBody : function(){
38733         /** This region's body element 
38734         * @type Roo.Element */
38735         this.bodyEl = this.el.createChild({
38736                 tag: "div",
38737                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38738         });
38739     },
38740
38741     onRender: function(ctr, pos)
38742     {
38743         var dh = Roo.DomHelper;
38744         /** This region's container element 
38745         * @type Roo.Element */
38746         this.el = dh.append(ctr.dom, {
38747                 tag: "div",
38748                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38749             }, true);
38750         /** This region's title element 
38751         * @type Roo.Element */
38752     
38753         this.titleEl = dh.append(this.el.dom,  {
38754                 tag: "div",
38755                 unselectable: "on",
38756                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38757                 children:[
38758                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38759                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38760                 ]
38761             }, true);
38762         
38763         this.titleEl.enableDisplayMode();
38764         /** This region's title text element 
38765         * @type HTMLElement */
38766         this.titleTextEl = this.titleEl.dom.firstChild;
38767         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38768         /*
38769         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38770         this.closeBtn.enableDisplayMode();
38771         this.closeBtn.on("click", this.closeClicked, this);
38772         this.closeBtn.hide();
38773     */
38774         this.createBody(this.config);
38775         if(this.config.hideWhenEmpty){
38776             this.hide();
38777             this.on("paneladded", this.validateVisibility, this);
38778             this.on("panelremoved", this.validateVisibility, this);
38779         }
38780         if(this.autoScroll){
38781             this.bodyEl.setStyle("overflow", "auto");
38782         }else{
38783             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38784         }
38785         //if(c.titlebar !== false){
38786             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38787                 this.titleEl.hide();
38788             }else{
38789                 this.titleEl.show();
38790                 if(this.config.title){
38791                     this.titleTextEl.innerHTML = this.config.title;
38792                 }
38793             }
38794         //}
38795         if(this.config.collapsed){
38796             this.collapse(true);
38797         }
38798         if(this.config.hidden){
38799             this.hide();
38800         }
38801         
38802         if (this.unrendered_panels && this.unrendered_panels.length) {
38803             for (var i =0;i< this.unrendered_panels.length; i++) {
38804                 this.add(this.unrendered_panels[i]);
38805             }
38806             this.unrendered_panels = null;
38807             
38808         }
38809         
38810     },
38811     
38812     applyConfig : function(c)
38813     {
38814         /*
38815          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38816             var dh = Roo.DomHelper;
38817             if(c.titlebar !== false){
38818                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38819                 this.collapseBtn.on("click", this.collapse, this);
38820                 this.collapseBtn.enableDisplayMode();
38821                 /*
38822                 if(c.showPin === true || this.showPin){
38823                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38824                     this.stickBtn.enableDisplayMode();
38825                     this.stickBtn.on("click", this.expand, this);
38826                     this.stickBtn.hide();
38827                 }
38828                 
38829             }
38830             */
38831             /** This region's collapsed element
38832             * @type Roo.Element */
38833             /*
38834              *
38835             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38836                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38837             ]}, true);
38838             
38839             if(c.floatable !== false){
38840                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38841                this.collapsedEl.on("click", this.collapseClick, this);
38842             }
38843
38844             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38845                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38846                    id: "message", unselectable: "on", style:{"float":"left"}});
38847                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38848              }
38849             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38850             this.expandBtn.on("click", this.expand, this);
38851             
38852         }
38853         
38854         if(this.collapseBtn){
38855             this.collapseBtn.setVisible(c.collapsible == true);
38856         }
38857         
38858         this.cmargins = c.cmargins || this.cmargins ||
38859                          (this.position == "west" || this.position == "east" ?
38860                              {top: 0, left: 2, right:2, bottom: 0} :
38861                              {top: 2, left: 0, right:0, bottom: 2});
38862         */
38863         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38864         
38865         
38866         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38867         
38868         this.autoScroll = c.autoScroll || false;
38869         
38870         
38871        
38872         
38873         this.duration = c.duration || .30;
38874         this.slideDuration = c.slideDuration || .45;
38875         this.config = c;
38876        
38877     },
38878     /**
38879      * Returns true if this region is currently visible.
38880      * @return {Boolean}
38881      */
38882     isVisible : function(){
38883         return this.visible;
38884     },
38885
38886     /**
38887      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38888      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38889      */
38890     //setCollapsedTitle : function(title){
38891     //    title = title || "&#160;";
38892      //   if(this.collapsedTitleTextEl){
38893       //      this.collapsedTitleTextEl.innerHTML = title;
38894        // }
38895     //},
38896
38897     getBox : function(){
38898         var b;
38899       //  if(!this.collapsed){
38900             b = this.el.getBox(false, true);
38901        // }else{
38902           //  b = this.collapsedEl.getBox(false, true);
38903         //}
38904         return b;
38905     },
38906
38907     getMargins : function(){
38908         return this.margins;
38909         //return this.collapsed ? this.cmargins : this.margins;
38910     },
38911 /*
38912     highlight : function(){
38913         this.el.addClass("x-layout-panel-dragover");
38914     },
38915
38916     unhighlight : function(){
38917         this.el.removeClass("x-layout-panel-dragover");
38918     },
38919 */
38920     updateBox : function(box)
38921     {
38922         if (!this.bodyEl) {
38923             return; // not rendered yet..
38924         }
38925         
38926         this.box = box;
38927         if(!this.collapsed){
38928             this.el.dom.style.left = box.x + "px";
38929             this.el.dom.style.top = box.y + "px";
38930             this.updateBody(box.width, box.height);
38931         }else{
38932             this.collapsedEl.dom.style.left = box.x + "px";
38933             this.collapsedEl.dom.style.top = box.y + "px";
38934             this.collapsedEl.setSize(box.width, box.height);
38935         }
38936         if(this.tabs){
38937             this.tabs.autoSizeTabs();
38938         }
38939     },
38940
38941     updateBody : function(w, h)
38942     {
38943         if(w !== null){
38944             this.el.setWidth(w);
38945             w -= this.el.getBorderWidth("rl");
38946             if(this.config.adjustments){
38947                 w += this.config.adjustments[0];
38948             }
38949         }
38950         if(h !== null && h > 0){
38951             this.el.setHeight(h);
38952             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38953             h -= this.el.getBorderWidth("tb");
38954             if(this.config.adjustments){
38955                 h += this.config.adjustments[1];
38956             }
38957             this.bodyEl.setHeight(h);
38958             if(this.tabs){
38959                 h = this.tabs.syncHeight(h);
38960             }
38961         }
38962         if(this.panelSize){
38963             w = w !== null ? w : this.panelSize.width;
38964             h = h !== null ? h : this.panelSize.height;
38965         }
38966         if(this.activePanel){
38967             var el = this.activePanel.getEl();
38968             w = w !== null ? w : el.getWidth();
38969             h = h !== null ? h : el.getHeight();
38970             this.panelSize = {width: w, height: h};
38971             this.activePanel.setSize(w, h);
38972         }
38973         if(Roo.isIE && this.tabs){
38974             this.tabs.el.repaint();
38975         }
38976     },
38977
38978     /**
38979      * Returns the container element for this region.
38980      * @return {Roo.Element}
38981      */
38982     getEl : function(){
38983         return this.el;
38984     },
38985
38986     /**
38987      * Hides this region.
38988      */
38989     hide : function(){
38990         //if(!this.collapsed){
38991             this.el.dom.style.left = "-2000px";
38992             this.el.hide();
38993         //}else{
38994          //   this.collapsedEl.dom.style.left = "-2000px";
38995          //   this.collapsedEl.hide();
38996        // }
38997         this.visible = false;
38998         this.fireEvent("visibilitychange", this, false);
38999     },
39000
39001     /**
39002      * Shows this region if it was previously hidden.
39003      */
39004     show : function(){
39005         //if(!this.collapsed){
39006             this.el.show();
39007         //}else{
39008         //    this.collapsedEl.show();
39009        // }
39010         this.visible = true;
39011         this.fireEvent("visibilitychange", this, true);
39012     },
39013 /*
39014     closeClicked : function(){
39015         if(this.activePanel){
39016             this.remove(this.activePanel);
39017         }
39018     },
39019
39020     collapseClick : function(e){
39021         if(this.isSlid){
39022            e.stopPropagation();
39023            this.slideIn();
39024         }else{
39025            e.stopPropagation();
39026            this.slideOut();
39027         }
39028     },
39029 */
39030     /**
39031      * Collapses this region.
39032      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39033      */
39034     /*
39035     collapse : function(skipAnim, skipCheck = false){
39036         if(this.collapsed) {
39037             return;
39038         }
39039         
39040         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39041             
39042             this.collapsed = true;
39043             if(this.split){
39044                 this.split.el.hide();
39045             }
39046             if(this.config.animate && skipAnim !== true){
39047                 this.fireEvent("invalidated", this);
39048                 this.animateCollapse();
39049             }else{
39050                 this.el.setLocation(-20000,-20000);
39051                 this.el.hide();
39052                 this.collapsedEl.show();
39053                 this.fireEvent("collapsed", this);
39054                 this.fireEvent("invalidated", this);
39055             }
39056         }
39057         
39058     },
39059 */
39060     animateCollapse : function(){
39061         // overridden
39062     },
39063
39064     /**
39065      * Expands this region if it was previously collapsed.
39066      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39067      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39068      */
39069     /*
39070     expand : function(e, skipAnim){
39071         if(e) {
39072             e.stopPropagation();
39073         }
39074         if(!this.collapsed || this.el.hasActiveFx()) {
39075             return;
39076         }
39077         if(this.isSlid){
39078             this.afterSlideIn();
39079             skipAnim = true;
39080         }
39081         this.collapsed = false;
39082         if(this.config.animate && skipAnim !== true){
39083             this.animateExpand();
39084         }else{
39085             this.el.show();
39086             if(this.split){
39087                 this.split.el.show();
39088             }
39089             this.collapsedEl.setLocation(-2000,-2000);
39090             this.collapsedEl.hide();
39091             this.fireEvent("invalidated", this);
39092             this.fireEvent("expanded", this);
39093         }
39094     },
39095 */
39096     animateExpand : function(){
39097         // overridden
39098     },
39099
39100     initTabs : function()
39101     {
39102         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39103         
39104         var ts = new Roo.bootstrap.panel.Tabs({
39105             el: this.bodyEl.dom,
39106             region : this,
39107             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39108             disableTooltips: this.config.disableTabTips,
39109             toolbar : this.config.toolbar
39110         });
39111         
39112         if(this.config.hideTabs){
39113             ts.stripWrap.setDisplayed(false);
39114         }
39115         this.tabs = ts;
39116         ts.resizeTabs = this.config.resizeTabs === true;
39117         ts.minTabWidth = this.config.minTabWidth || 40;
39118         ts.maxTabWidth = this.config.maxTabWidth || 250;
39119         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39120         ts.monitorResize = false;
39121         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39122         ts.bodyEl.addClass('roo-layout-tabs-body');
39123         this.panels.each(this.initPanelAsTab, this);
39124     },
39125
39126     initPanelAsTab : function(panel){
39127         var ti = this.tabs.addTab(
39128             panel.getEl().id,
39129             panel.getTitle(),
39130             null,
39131             this.config.closeOnTab && panel.isClosable(),
39132             panel.tpl
39133         );
39134         if(panel.tabTip !== undefined){
39135             ti.setTooltip(panel.tabTip);
39136         }
39137         ti.on("activate", function(){
39138               this.setActivePanel(panel);
39139         }, this);
39140         
39141         if(this.config.closeOnTab){
39142             ti.on("beforeclose", function(t, e){
39143                 e.cancel = true;
39144                 this.remove(panel);
39145             }, this);
39146         }
39147         
39148         panel.tabItem = ti;
39149         
39150         return ti;
39151     },
39152
39153     updatePanelTitle : function(panel, title)
39154     {
39155         if(this.activePanel == panel){
39156             this.updateTitle(title);
39157         }
39158         if(this.tabs){
39159             var ti = this.tabs.getTab(panel.getEl().id);
39160             ti.setText(title);
39161             if(panel.tabTip !== undefined){
39162                 ti.setTooltip(panel.tabTip);
39163             }
39164         }
39165     },
39166
39167     updateTitle : function(title){
39168         if(this.titleTextEl && !this.config.title){
39169             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39170         }
39171     },
39172
39173     setActivePanel : function(panel)
39174     {
39175         panel = this.getPanel(panel);
39176         if(this.activePanel && this.activePanel != panel){
39177             if(this.activePanel.setActiveState(false) === false){
39178                 return;
39179             }
39180         }
39181         this.activePanel = panel;
39182         panel.setActiveState(true);
39183         if(this.panelSize){
39184             panel.setSize(this.panelSize.width, this.panelSize.height);
39185         }
39186         if(this.closeBtn){
39187             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39188         }
39189         this.updateTitle(panel.getTitle());
39190         if(this.tabs){
39191             this.fireEvent("invalidated", this);
39192         }
39193         this.fireEvent("panelactivated", this, panel);
39194     },
39195
39196     /**
39197      * Shows the specified panel.
39198      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39199      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39200      */
39201     showPanel : function(panel)
39202     {
39203         panel = this.getPanel(panel);
39204         if(panel){
39205             if(this.tabs){
39206                 var tab = this.tabs.getTab(panel.getEl().id);
39207                 if(tab.isHidden()){
39208                     this.tabs.unhideTab(tab.id);
39209                 }
39210                 tab.activate();
39211             }else{
39212                 this.setActivePanel(panel);
39213             }
39214         }
39215         return panel;
39216     },
39217
39218     /**
39219      * Get the active panel for this region.
39220      * @return {Roo.ContentPanel} The active panel or null
39221      */
39222     getActivePanel : function(){
39223         return this.activePanel;
39224     },
39225
39226     validateVisibility : function(){
39227         if(this.panels.getCount() < 1){
39228             this.updateTitle("&#160;");
39229             this.closeBtn.hide();
39230             this.hide();
39231         }else{
39232             if(!this.isVisible()){
39233                 this.show();
39234             }
39235         }
39236     },
39237
39238     /**
39239      * Adds the passed ContentPanel(s) to this region.
39240      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39241      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39242      */
39243     add : function(panel)
39244     {
39245         if(arguments.length > 1){
39246             for(var i = 0, len = arguments.length; i < len; i++) {
39247                 this.add(arguments[i]);
39248             }
39249             return null;
39250         }
39251         
39252         // if we have not been rendered yet, then we can not really do much of this..
39253         if (!this.bodyEl) {
39254             this.unrendered_panels.push(panel);
39255             return panel;
39256         }
39257         
39258         
39259         
39260         
39261         if(this.hasPanel(panel)){
39262             this.showPanel(panel);
39263             return panel;
39264         }
39265         panel.setRegion(this);
39266         this.panels.add(panel);
39267        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39268             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39269             // and hide them... ???
39270             this.bodyEl.dom.appendChild(panel.getEl().dom);
39271             if(panel.background !== true){
39272                 this.setActivePanel(panel);
39273             }
39274             this.fireEvent("paneladded", this, panel);
39275             return panel;
39276         }
39277         */
39278         if(!this.tabs){
39279             this.initTabs();
39280         }else{
39281             this.initPanelAsTab(panel);
39282         }
39283         
39284         
39285         if(panel.background !== true){
39286             this.tabs.activate(panel.getEl().id);
39287         }
39288         this.fireEvent("paneladded", this, panel);
39289         return panel;
39290     },
39291
39292     /**
39293      * Hides the tab for the specified panel.
39294      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39295      */
39296     hidePanel : function(panel){
39297         if(this.tabs && (panel = this.getPanel(panel))){
39298             this.tabs.hideTab(panel.getEl().id);
39299         }
39300     },
39301
39302     /**
39303      * Unhides the tab for a previously hidden panel.
39304      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39305      */
39306     unhidePanel : function(panel){
39307         if(this.tabs && (panel = this.getPanel(panel))){
39308             this.tabs.unhideTab(panel.getEl().id);
39309         }
39310     },
39311
39312     clearPanels : function(){
39313         while(this.panels.getCount() > 0){
39314              this.remove(this.panels.first());
39315         }
39316     },
39317
39318     /**
39319      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39320      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39321      * @param {Boolean} preservePanel Overrides the config preservePanel option
39322      * @return {Roo.ContentPanel} The panel that was removed
39323      */
39324     remove : function(panel, preservePanel)
39325     {
39326         panel = this.getPanel(panel);
39327         if(!panel){
39328             return null;
39329         }
39330         var e = {};
39331         this.fireEvent("beforeremove", this, panel, e);
39332         if(e.cancel === true){
39333             return null;
39334         }
39335         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39336         var panelId = panel.getId();
39337         this.panels.removeKey(panelId);
39338         if(preservePanel){
39339             document.body.appendChild(panel.getEl().dom);
39340         }
39341         if(this.tabs){
39342             this.tabs.removeTab(panel.getEl().id);
39343         }else if (!preservePanel){
39344             this.bodyEl.dom.removeChild(panel.getEl().dom);
39345         }
39346         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39347             var p = this.panels.first();
39348             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39349             tempEl.appendChild(p.getEl().dom);
39350             this.bodyEl.update("");
39351             this.bodyEl.dom.appendChild(p.getEl().dom);
39352             tempEl = null;
39353             this.updateTitle(p.getTitle());
39354             this.tabs = null;
39355             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39356             this.setActivePanel(p);
39357         }
39358         panel.setRegion(null);
39359         if(this.activePanel == panel){
39360             this.activePanel = null;
39361         }
39362         if(this.config.autoDestroy !== false && preservePanel !== true){
39363             try{panel.destroy();}catch(e){}
39364         }
39365         this.fireEvent("panelremoved", this, panel);
39366         return panel;
39367     },
39368
39369     /**
39370      * Returns the TabPanel component used by this region
39371      * @return {Roo.TabPanel}
39372      */
39373     getTabs : function(){
39374         return this.tabs;
39375     },
39376
39377     createTool : function(parentEl, className){
39378         var btn = Roo.DomHelper.append(parentEl, {
39379             tag: "div",
39380             cls: "x-layout-tools-button",
39381             children: [ {
39382                 tag: "div",
39383                 cls: "roo-layout-tools-button-inner " + className,
39384                 html: "&#160;"
39385             }]
39386         }, true);
39387         btn.addClassOnOver("roo-layout-tools-button-over");
39388         return btn;
39389     }
39390 });/*
39391  * Based on:
39392  * Ext JS Library 1.1.1
39393  * Copyright(c) 2006-2007, Ext JS, LLC.
39394  *
39395  * Originally Released Under LGPL - original licence link has changed is not relivant.
39396  *
39397  * Fork - LGPL
39398  * <script type="text/javascript">
39399  */
39400  
39401
39402
39403 /**
39404  * @class Roo.SplitLayoutRegion
39405  * @extends Roo.LayoutRegion
39406  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39407  */
39408 Roo.bootstrap.layout.Split = function(config){
39409     this.cursor = config.cursor;
39410     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39411 };
39412
39413 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39414 {
39415     splitTip : "Drag to resize.",
39416     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39417     useSplitTips : false,
39418
39419     applyConfig : function(config){
39420         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39421     },
39422     
39423     onRender : function(ctr,pos) {
39424         
39425         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39426         if(!this.config.split){
39427             return;
39428         }
39429         if(!this.split){
39430             
39431             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39432                             tag: "div",
39433                             id: this.el.id + "-split",
39434                             cls: "roo-layout-split roo-layout-split-"+this.position,
39435                             html: "&#160;"
39436             });
39437             /** The SplitBar for this region 
39438             * @type Roo.SplitBar */
39439             // does not exist yet...
39440             Roo.log([this.position, this.orientation]);
39441             
39442             this.split = new Roo.bootstrap.SplitBar({
39443                 dragElement : splitEl,
39444                 resizingElement: this.el,
39445                 orientation : this.orientation
39446             });
39447             
39448             this.split.on("moved", this.onSplitMove, this);
39449             this.split.useShim = this.config.useShim === true;
39450             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39451             if(this.useSplitTips){
39452                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39453             }
39454             //if(config.collapsible){
39455             //    this.split.el.on("dblclick", this.collapse,  this);
39456             //}
39457         }
39458         if(typeof this.config.minSize != "undefined"){
39459             this.split.minSize = this.config.minSize;
39460         }
39461         if(typeof this.config.maxSize != "undefined"){
39462             this.split.maxSize = this.config.maxSize;
39463         }
39464         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39465             this.hideSplitter();
39466         }
39467         
39468     },
39469
39470     getHMaxSize : function(){
39471          var cmax = this.config.maxSize || 10000;
39472          var center = this.mgr.getRegion("center");
39473          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39474     },
39475
39476     getVMaxSize : function(){
39477          var cmax = this.config.maxSize || 10000;
39478          var center = this.mgr.getRegion("center");
39479          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39480     },
39481
39482     onSplitMove : function(split, newSize){
39483         this.fireEvent("resized", this, newSize);
39484     },
39485     
39486     /** 
39487      * Returns the {@link Roo.SplitBar} for this region.
39488      * @return {Roo.SplitBar}
39489      */
39490     getSplitBar : function(){
39491         return this.split;
39492     },
39493     
39494     hide : function(){
39495         this.hideSplitter();
39496         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39497     },
39498
39499     hideSplitter : function(){
39500         if(this.split){
39501             this.split.el.setLocation(-2000,-2000);
39502             this.split.el.hide();
39503         }
39504     },
39505
39506     show : function(){
39507         if(this.split){
39508             this.split.el.show();
39509         }
39510         Roo.bootstrap.layout.Split.superclass.show.call(this);
39511     },
39512     
39513     beforeSlide: function(){
39514         if(Roo.isGecko){// firefox overflow auto bug workaround
39515             this.bodyEl.clip();
39516             if(this.tabs) {
39517                 this.tabs.bodyEl.clip();
39518             }
39519             if(this.activePanel){
39520                 this.activePanel.getEl().clip();
39521                 
39522                 if(this.activePanel.beforeSlide){
39523                     this.activePanel.beforeSlide();
39524                 }
39525             }
39526         }
39527     },
39528     
39529     afterSlide : function(){
39530         if(Roo.isGecko){// firefox overflow auto bug workaround
39531             this.bodyEl.unclip();
39532             if(this.tabs) {
39533                 this.tabs.bodyEl.unclip();
39534             }
39535             if(this.activePanel){
39536                 this.activePanel.getEl().unclip();
39537                 if(this.activePanel.afterSlide){
39538                     this.activePanel.afterSlide();
39539                 }
39540             }
39541         }
39542     },
39543
39544     initAutoHide : function(){
39545         if(this.autoHide !== false){
39546             if(!this.autoHideHd){
39547                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39548                 this.autoHideHd = {
39549                     "mouseout": function(e){
39550                         if(!e.within(this.el, true)){
39551                             st.delay(500);
39552                         }
39553                     },
39554                     "mouseover" : function(e){
39555                         st.cancel();
39556                     },
39557                     scope : this
39558                 };
39559             }
39560             this.el.on(this.autoHideHd);
39561         }
39562     },
39563
39564     clearAutoHide : function(){
39565         if(this.autoHide !== false){
39566             this.el.un("mouseout", this.autoHideHd.mouseout);
39567             this.el.un("mouseover", this.autoHideHd.mouseover);
39568         }
39569     },
39570
39571     clearMonitor : function(){
39572         Roo.get(document).un("click", this.slideInIf, this);
39573     },
39574
39575     // these names are backwards but not changed for compat
39576     slideOut : function(){
39577         if(this.isSlid || this.el.hasActiveFx()){
39578             return;
39579         }
39580         this.isSlid = true;
39581         if(this.collapseBtn){
39582             this.collapseBtn.hide();
39583         }
39584         this.closeBtnState = this.closeBtn.getStyle('display');
39585         this.closeBtn.hide();
39586         if(this.stickBtn){
39587             this.stickBtn.show();
39588         }
39589         this.el.show();
39590         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39591         this.beforeSlide();
39592         this.el.setStyle("z-index", 10001);
39593         this.el.slideIn(this.getSlideAnchor(), {
39594             callback: function(){
39595                 this.afterSlide();
39596                 this.initAutoHide();
39597                 Roo.get(document).on("click", this.slideInIf, this);
39598                 this.fireEvent("slideshow", this);
39599             },
39600             scope: this,
39601             block: true
39602         });
39603     },
39604
39605     afterSlideIn : function(){
39606         this.clearAutoHide();
39607         this.isSlid = false;
39608         this.clearMonitor();
39609         this.el.setStyle("z-index", "");
39610         if(this.collapseBtn){
39611             this.collapseBtn.show();
39612         }
39613         this.closeBtn.setStyle('display', this.closeBtnState);
39614         if(this.stickBtn){
39615             this.stickBtn.hide();
39616         }
39617         this.fireEvent("slidehide", this);
39618     },
39619
39620     slideIn : function(cb){
39621         if(!this.isSlid || this.el.hasActiveFx()){
39622             Roo.callback(cb);
39623             return;
39624         }
39625         this.isSlid = false;
39626         this.beforeSlide();
39627         this.el.slideOut(this.getSlideAnchor(), {
39628             callback: function(){
39629                 this.el.setLeftTop(-10000, -10000);
39630                 this.afterSlide();
39631                 this.afterSlideIn();
39632                 Roo.callback(cb);
39633             },
39634             scope: this,
39635             block: true
39636         });
39637     },
39638     
39639     slideInIf : function(e){
39640         if(!e.within(this.el)){
39641             this.slideIn();
39642         }
39643     },
39644
39645     animateCollapse : function(){
39646         this.beforeSlide();
39647         this.el.setStyle("z-index", 20000);
39648         var anchor = this.getSlideAnchor();
39649         this.el.slideOut(anchor, {
39650             callback : function(){
39651                 this.el.setStyle("z-index", "");
39652                 this.collapsedEl.slideIn(anchor, {duration:.3});
39653                 this.afterSlide();
39654                 this.el.setLocation(-10000,-10000);
39655                 this.el.hide();
39656                 this.fireEvent("collapsed", this);
39657             },
39658             scope: this,
39659             block: true
39660         });
39661     },
39662
39663     animateExpand : function(){
39664         this.beforeSlide();
39665         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39666         this.el.setStyle("z-index", 20000);
39667         this.collapsedEl.hide({
39668             duration:.1
39669         });
39670         this.el.slideIn(this.getSlideAnchor(), {
39671             callback : function(){
39672                 this.el.setStyle("z-index", "");
39673                 this.afterSlide();
39674                 if(this.split){
39675                     this.split.el.show();
39676                 }
39677                 this.fireEvent("invalidated", this);
39678                 this.fireEvent("expanded", this);
39679             },
39680             scope: this,
39681             block: true
39682         });
39683     },
39684
39685     anchors : {
39686         "west" : "left",
39687         "east" : "right",
39688         "north" : "top",
39689         "south" : "bottom"
39690     },
39691
39692     sanchors : {
39693         "west" : "l",
39694         "east" : "r",
39695         "north" : "t",
39696         "south" : "b"
39697     },
39698
39699     canchors : {
39700         "west" : "tl-tr",
39701         "east" : "tr-tl",
39702         "north" : "tl-bl",
39703         "south" : "bl-tl"
39704     },
39705
39706     getAnchor : function(){
39707         return this.anchors[this.position];
39708     },
39709
39710     getCollapseAnchor : function(){
39711         return this.canchors[this.position];
39712     },
39713
39714     getSlideAnchor : function(){
39715         return this.sanchors[this.position];
39716     },
39717
39718     getAlignAdj : function(){
39719         var cm = this.cmargins;
39720         switch(this.position){
39721             case "west":
39722                 return [0, 0];
39723             break;
39724             case "east":
39725                 return [0, 0];
39726             break;
39727             case "north":
39728                 return [0, 0];
39729             break;
39730             case "south":
39731                 return [0, 0];
39732             break;
39733         }
39734     },
39735
39736     getExpandAdj : function(){
39737         var c = this.collapsedEl, cm = this.cmargins;
39738         switch(this.position){
39739             case "west":
39740                 return [-(cm.right+c.getWidth()+cm.left), 0];
39741             break;
39742             case "east":
39743                 return [cm.right+c.getWidth()+cm.left, 0];
39744             break;
39745             case "north":
39746                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39747             break;
39748             case "south":
39749                 return [0, cm.top+cm.bottom+c.getHeight()];
39750             break;
39751         }
39752     }
39753 });/*
39754  * Based on:
39755  * Ext JS Library 1.1.1
39756  * Copyright(c) 2006-2007, Ext JS, LLC.
39757  *
39758  * Originally Released Under LGPL - original licence link has changed is not relivant.
39759  *
39760  * Fork - LGPL
39761  * <script type="text/javascript">
39762  */
39763 /*
39764  * These classes are private internal classes
39765  */
39766 Roo.bootstrap.layout.Center = function(config){
39767     config.region = "center";
39768     Roo.bootstrap.layout.Region.call(this, config);
39769     this.visible = true;
39770     this.minWidth = config.minWidth || 20;
39771     this.minHeight = config.minHeight || 20;
39772 };
39773
39774 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39775     hide : function(){
39776         // center panel can't be hidden
39777     },
39778     
39779     show : function(){
39780         // center panel can't be hidden
39781     },
39782     
39783     getMinWidth: function(){
39784         return this.minWidth;
39785     },
39786     
39787     getMinHeight: function(){
39788         return this.minHeight;
39789     }
39790 });
39791
39792
39793
39794
39795  
39796
39797
39798
39799
39800
39801
39802 Roo.bootstrap.layout.North = function(config)
39803 {
39804     config.region = 'north';
39805     config.cursor = 'n-resize';
39806     
39807     Roo.bootstrap.layout.Split.call(this, config);
39808     
39809     
39810     if(this.split){
39811         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39812         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39813         this.split.el.addClass("roo-layout-split-v");
39814     }
39815     //var size = config.initialSize || config.height;
39816     //if(this.el && typeof size != "undefined"){
39817     //    this.el.setHeight(size);
39818     //}
39819 };
39820 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39821 {
39822     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39823      
39824      
39825     onRender : function(ctr, pos)
39826     {
39827         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39828         var size = this.config.initialSize || this.config.height;
39829         if(this.el && typeof size != "undefined"){
39830             this.el.setHeight(size);
39831         }
39832     
39833     },
39834     
39835     getBox : function(){
39836         if(this.collapsed){
39837             return this.collapsedEl.getBox();
39838         }
39839         var box = this.el.getBox();
39840         if(this.split){
39841             box.height += this.split.el.getHeight();
39842         }
39843         return box;
39844     },
39845     
39846     updateBox : function(box){
39847         if(this.split && !this.collapsed){
39848             box.height -= this.split.el.getHeight();
39849             this.split.el.setLeft(box.x);
39850             this.split.el.setTop(box.y+box.height);
39851             this.split.el.setWidth(box.width);
39852         }
39853         if(this.collapsed){
39854             this.updateBody(box.width, null);
39855         }
39856         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39857     }
39858 });
39859
39860
39861
39862
39863
39864 Roo.bootstrap.layout.South = function(config){
39865     config.region = 'south';
39866     config.cursor = 's-resize';
39867     Roo.bootstrap.layout.Split.call(this, config);
39868     if(this.split){
39869         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39870         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39871         this.split.el.addClass("roo-layout-split-v");
39872     }
39873     
39874 };
39875
39876 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39877     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39878     
39879     onRender : function(ctr, pos)
39880     {
39881         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39882         var size = this.config.initialSize || this.config.height;
39883         if(this.el && typeof size != "undefined"){
39884             this.el.setHeight(size);
39885         }
39886     
39887     },
39888     
39889     getBox : function(){
39890         if(this.collapsed){
39891             return this.collapsedEl.getBox();
39892         }
39893         var box = this.el.getBox();
39894         if(this.split){
39895             var sh = this.split.el.getHeight();
39896             box.height += sh;
39897             box.y -= sh;
39898         }
39899         return box;
39900     },
39901     
39902     updateBox : function(box){
39903         if(this.split && !this.collapsed){
39904             var sh = this.split.el.getHeight();
39905             box.height -= sh;
39906             box.y += sh;
39907             this.split.el.setLeft(box.x);
39908             this.split.el.setTop(box.y-sh);
39909             this.split.el.setWidth(box.width);
39910         }
39911         if(this.collapsed){
39912             this.updateBody(box.width, null);
39913         }
39914         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39915     }
39916 });
39917
39918 Roo.bootstrap.layout.East = function(config){
39919     config.region = "east";
39920     config.cursor = "e-resize";
39921     Roo.bootstrap.layout.Split.call(this, config);
39922     if(this.split){
39923         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39924         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39925         this.split.el.addClass("roo-layout-split-h");
39926     }
39927     
39928 };
39929 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39930     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39931     
39932     onRender : function(ctr, pos)
39933     {
39934         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39935         var size = this.config.initialSize || this.config.width;
39936         if(this.el && typeof size != "undefined"){
39937             this.el.setWidth(size);
39938         }
39939     
39940     },
39941     
39942     getBox : function(){
39943         if(this.collapsed){
39944             return this.collapsedEl.getBox();
39945         }
39946         var box = this.el.getBox();
39947         if(this.split){
39948             var sw = this.split.el.getWidth();
39949             box.width += sw;
39950             box.x -= sw;
39951         }
39952         return box;
39953     },
39954
39955     updateBox : function(box){
39956         if(this.split && !this.collapsed){
39957             var sw = this.split.el.getWidth();
39958             box.width -= sw;
39959             this.split.el.setLeft(box.x);
39960             this.split.el.setTop(box.y);
39961             this.split.el.setHeight(box.height);
39962             box.x += sw;
39963         }
39964         if(this.collapsed){
39965             this.updateBody(null, box.height);
39966         }
39967         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39968     }
39969 });
39970
39971 Roo.bootstrap.layout.West = function(config){
39972     config.region = "west";
39973     config.cursor = "w-resize";
39974     
39975     Roo.bootstrap.layout.Split.call(this, config);
39976     if(this.split){
39977         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39978         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39979         this.split.el.addClass("roo-layout-split-h");
39980     }
39981     
39982 };
39983 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39984     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39985     
39986     onRender: function(ctr, pos)
39987     {
39988         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39989         var size = this.config.initialSize || this.config.width;
39990         if(typeof size != "undefined"){
39991             this.el.setWidth(size);
39992         }
39993     },
39994     
39995     getBox : function(){
39996         if(this.collapsed){
39997             return this.collapsedEl.getBox();
39998         }
39999         var box = this.el.getBox();
40000         if (box.width == 0) {
40001             box.width = this.config.width; // kludge?
40002         }
40003         if(this.split){
40004             box.width += this.split.el.getWidth();
40005         }
40006         return box;
40007     },
40008     
40009     updateBox : function(box){
40010         if(this.split && !this.collapsed){
40011             var sw = this.split.el.getWidth();
40012             box.width -= sw;
40013             this.split.el.setLeft(box.x+box.width);
40014             this.split.el.setTop(box.y);
40015             this.split.el.setHeight(box.height);
40016         }
40017         if(this.collapsed){
40018             this.updateBody(null, box.height);
40019         }
40020         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40021     }
40022 });Roo.namespace("Roo.bootstrap.panel");/*
40023  * Based on:
40024  * Ext JS Library 1.1.1
40025  * Copyright(c) 2006-2007, Ext JS, LLC.
40026  *
40027  * Originally Released Under LGPL - original licence link has changed is not relivant.
40028  *
40029  * Fork - LGPL
40030  * <script type="text/javascript">
40031  */
40032 /**
40033  * @class Roo.ContentPanel
40034  * @extends Roo.util.Observable
40035  * A basic ContentPanel element.
40036  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40037  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40038  * @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
40039  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40040  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40041  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40042  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40043  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40044  * @cfg {String} title          The title for this panel
40045  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40046  * @cfg {String} url            Calls {@link #setUrl} with this value
40047  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40048  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40049  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40050  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40051  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40052  * @cfg {Boolean} badges render the badges
40053  * @cfg {String} cls  extra classes to use  
40054  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40055
40056  * @constructor
40057  * Create a new ContentPanel.
40058  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40059  * @param {String/Object} config A string to set only the title or a config object
40060  * @param {String} content (optional) Set the HTML content for this panel
40061  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40062  */
40063 Roo.bootstrap.panel.Content = function( config){
40064     
40065     this.tpl = config.tpl || false;
40066     
40067     var el = config.el;
40068     var content = config.content;
40069
40070     if(config.autoCreate){ // xtype is available if this is called from factory
40071         el = Roo.id();
40072     }
40073     this.el = Roo.get(el);
40074     if(!this.el && config && config.autoCreate){
40075         if(typeof config.autoCreate == "object"){
40076             if(!config.autoCreate.id){
40077                 config.autoCreate.id = config.id||el;
40078             }
40079             this.el = Roo.DomHelper.append(document.body,
40080                         config.autoCreate, true);
40081         }else{
40082             var elcfg =  {
40083                 tag: "div",
40084                 cls: (config.cls || '') +
40085                     (config.background ? ' bg-' + config.background : '') +
40086                     " roo-layout-inactive-content",
40087                 id: config.id||el
40088             };
40089             if (config.iframe) {
40090                 elcfg.cn = [
40091                     {
40092                         tag : 'iframe',
40093                         style : 'border: 0px',
40094                         src : 'about:blank'
40095                     }
40096                 ];
40097             }
40098               
40099             if (config.html) {
40100                 elcfg.html = config.html;
40101                 
40102             }
40103                         
40104             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40105             if (config.iframe) {
40106                 this.iframeEl = this.el.select('iframe',true).first();
40107             }
40108             
40109         }
40110     } 
40111     this.closable = false;
40112     this.loaded = false;
40113     this.active = false;
40114    
40115       
40116     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40117         
40118         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40119         
40120         this.wrapEl = this.el; //this.el.wrap();
40121         var ti = [];
40122         if (config.toolbar.items) {
40123             ti = config.toolbar.items ;
40124             delete config.toolbar.items ;
40125         }
40126         
40127         var nitems = [];
40128         this.toolbar.render(this.wrapEl, 'before');
40129         for(var i =0;i < ti.length;i++) {
40130           //  Roo.log(['add child', items[i]]);
40131             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40132         }
40133         this.toolbar.items = nitems;
40134         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40135         delete config.toolbar;
40136         
40137     }
40138     /*
40139     // xtype created footer. - not sure if will work as we normally have to render first..
40140     if (this.footer && !this.footer.el && this.footer.xtype) {
40141         if (!this.wrapEl) {
40142             this.wrapEl = this.el.wrap();
40143         }
40144     
40145         this.footer.container = this.wrapEl.createChild();
40146          
40147         this.footer = Roo.factory(this.footer, Roo);
40148         
40149     }
40150     */
40151     
40152      if(typeof config == "string"){
40153         this.title = config;
40154     }else{
40155         Roo.apply(this, config);
40156     }
40157     
40158     if(this.resizeEl){
40159         this.resizeEl = Roo.get(this.resizeEl, true);
40160     }else{
40161         this.resizeEl = this.el;
40162     }
40163     // handle view.xtype
40164     
40165  
40166     
40167     
40168     this.addEvents({
40169         /**
40170          * @event activate
40171          * Fires when this panel is activated. 
40172          * @param {Roo.ContentPanel} this
40173          */
40174         "activate" : true,
40175         /**
40176          * @event deactivate
40177          * Fires when this panel is activated. 
40178          * @param {Roo.ContentPanel} this
40179          */
40180         "deactivate" : true,
40181
40182         /**
40183          * @event resize
40184          * Fires when this panel is resized if fitToFrame is true.
40185          * @param {Roo.ContentPanel} this
40186          * @param {Number} width The width after any component adjustments
40187          * @param {Number} height The height after any component adjustments
40188          */
40189         "resize" : true,
40190         
40191          /**
40192          * @event render
40193          * Fires when this tab is created
40194          * @param {Roo.ContentPanel} this
40195          */
40196         "render" : true
40197         
40198         
40199         
40200     });
40201     
40202
40203     
40204     
40205     if(this.autoScroll && !this.iframe){
40206         this.resizeEl.setStyle("overflow", "auto");
40207     } else {
40208         // fix randome scrolling
40209         //this.el.on('scroll', function() {
40210         //    Roo.log('fix random scolling');
40211         //    this.scrollTo('top',0); 
40212         //});
40213     }
40214     content = content || this.content;
40215     if(content){
40216         this.setContent(content);
40217     }
40218     if(config && config.url){
40219         this.setUrl(this.url, this.params, this.loadOnce);
40220     }
40221     
40222     
40223     
40224     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40225     
40226     if (this.view && typeof(this.view.xtype) != 'undefined') {
40227         this.view.el = this.el.appendChild(document.createElement("div"));
40228         this.view = Roo.factory(this.view); 
40229         this.view.render  &&  this.view.render(false, '');  
40230     }
40231     
40232     
40233     this.fireEvent('render', this);
40234 };
40235
40236 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40237     
40238     cls : '',
40239     background : '',
40240     
40241     tabTip : '',
40242     
40243     iframe : false,
40244     iframeEl : false,
40245     
40246     setRegion : function(region){
40247         this.region = region;
40248         this.setActiveClass(region && !this.background);
40249     },
40250     
40251     
40252     setActiveClass: function(state)
40253     {
40254         if(state){
40255            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40256            this.el.setStyle('position','relative');
40257         }else{
40258            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40259            this.el.setStyle('position', 'absolute');
40260         } 
40261     },
40262     
40263     /**
40264      * Returns the toolbar for this Panel if one was configured. 
40265      * @return {Roo.Toolbar} 
40266      */
40267     getToolbar : function(){
40268         return this.toolbar;
40269     },
40270     
40271     setActiveState : function(active)
40272     {
40273         this.active = active;
40274         this.setActiveClass(active);
40275         if(!active){
40276             if(this.fireEvent("deactivate", this) === false){
40277                 return false;
40278             }
40279             return true;
40280         }
40281         this.fireEvent("activate", this);
40282         return true;
40283     },
40284     /**
40285      * Updates this panel's element (not for iframe)
40286      * @param {String} content The new content
40287      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40288     */
40289     setContent : function(content, loadScripts){
40290         if (this.iframe) {
40291             return;
40292         }
40293         
40294         this.el.update(content, loadScripts);
40295     },
40296
40297     ignoreResize : function(w, h){
40298         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40299             return true;
40300         }else{
40301             this.lastSize = {width: w, height: h};
40302             return false;
40303         }
40304     },
40305     /**
40306      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40307      * @return {Roo.UpdateManager} The UpdateManager
40308      */
40309     getUpdateManager : function(){
40310         if (this.iframe) {
40311             return false;
40312         }
40313         return this.el.getUpdateManager();
40314     },
40315      /**
40316      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40317      * Does not work with IFRAME contents
40318      * @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:
40319 <pre><code>
40320 panel.load({
40321     url: "your-url.php",
40322     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40323     callback: yourFunction,
40324     scope: yourObject, //(optional scope)
40325     discardUrl: false,
40326     nocache: false,
40327     text: "Loading...",
40328     timeout: 30,
40329     scripts: false
40330 });
40331 </code></pre>
40332      
40333      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40334      * 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.
40335      * @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}
40336      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40337      * @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.
40338      * @return {Roo.ContentPanel} this
40339      */
40340     load : function(){
40341         
40342         if (this.iframe) {
40343             return this;
40344         }
40345         
40346         var um = this.el.getUpdateManager();
40347         um.update.apply(um, arguments);
40348         return this;
40349     },
40350
40351
40352     /**
40353      * 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.
40354      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40355      * @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)
40356      * @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)
40357      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40358      */
40359     setUrl : function(url, params, loadOnce){
40360         if (this.iframe) {
40361             this.iframeEl.dom.src = url;
40362             return false;
40363         }
40364         
40365         if(this.refreshDelegate){
40366             this.removeListener("activate", this.refreshDelegate);
40367         }
40368         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40369         this.on("activate", this.refreshDelegate);
40370         return this.el.getUpdateManager();
40371     },
40372     
40373     _handleRefresh : function(url, params, loadOnce){
40374         if(!loadOnce || !this.loaded){
40375             var updater = this.el.getUpdateManager();
40376             updater.update(url, params, this._setLoaded.createDelegate(this));
40377         }
40378     },
40379     
40380     _setLoaded : function(){
40381         this.loaded = true;
40382     }, 
40383     
40384     /**
40385      * Returns this panel's id
40386      * @return {String} 
40387      */
40388     getId : function(){
40389         return this.el.id;
40390     },
40391     
40392     /** 
40393      * Returns this panel's element - used by regiosn to add.
40394      * @return {Roo.Element} 
40395      */
40396     getEl : function(){
40397         return this.wrapEl || this.el;
40398     },
40399     
40400    
40401     
40402     adjustForComponents : function(width, height)
40403     {
40404         //Roo.log('adjustForComponents ');
40405         if(this.resizeEl != this.el){
40406             width -= this.el.getFrameWidth('lr');
40407             height -= this.el.getFrameWidth('tb');
40408         }
40409         if(this.toolbar){
40410             var te = this.toolbar.getEl();
40411             te.setWidth(width);
40412             height -= te.getHeight();
40413         }
40414         if(this.footer){
40415             var te = this.footer.getEl();
40416             te.setWidth(width);
40417             height -= te.getHeight();
40418         }
40419         
40420         
40421         if(this.adjustments){
40422             width += this.adjustments[0];
40423             height += this.adjustments[1];
40424         }
40425         return {"width": width, "height": height};
40426     },
40427     
40428     setSize : function(width, height){
40429         if(this.fitToFrame && !this.ignoreResize(width, height)){
40430             if(this.fitContainer && this.resizeEl != this.el){
40431                 this.el.setSize(width, height);
40432             }
40433             var size = this.adjustForComponents(width, height);
40434             if (this.iframe) {
40435                 this.iframeEl.setSize(width,height);
40436             }
40437             
40438             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40439             this.fireEvent('resize', this, size.width, size.height);
40440             
40441             
40442         }
40443     },
40444     
40445     /**
40446      * Returns this panel's title
40447      * @return {String} 
40448      */
40449     getTitle : function(){
40450         
40451         if (typeof(this.title) != 'object') {
40452             return this.title;
40453         }
40454         
40455         var t = '';
40456         for (var k in this.title) {
40457             if (!this.title.hasOwnProperty(k)) {
40458                 continue;
40459             }
40460             
40461             if (k.indexOf('-') >= 0) {
40462                 var s = k.split('-');
40463                 for (var i = 0; i<s.length; i++) {
40464                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40465                 }
40466             } else {
40467                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40468             }
40469         }
40470         return t;
40471     },
40472     
40473     /**
40474      * Set this panel's title
40475      * @param {String} title
40476      */
40477     setTitle : function(title){
40478         this.title = title;
40479         if(this.region){
40480             this.region.updatePanelTitle(this, title);
40481         }
40482     },
40483     
40484     /**
40485      * Returns true is this panel was configured to be closable
40486      * @return {Boolean} 
40487      */
40488     isClosable : function(){
40489         return this.closable;
40490     },
40491     
40492     beforeSlide : function(){
40493         this.el.clip();
40494         this.resizeEl.clip();
40495     },
40496     
40497     afterSlide : function(){
40498         this.el.unclip();
40499         this.resizeEl.unclip();
40500     },
40501     
40502     /**
40503      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40504      *   Will fail silently if the {@link #setUrl} method has not been called.
40505      *   This does not activate the panel, just updates its content.
40506      */
40507     refresh : function(){
40508         if(this.refreshDelegate){
40509            this.loaded = false;
40510            this.refreshDelegate();
40511         }
40512     },
40513     
40514     /**
40515      * Destroys this panel
40516      */
40517     destroy : function(){
40518         this.el.removeAllListeners();
40519         var tempEl = document.createElement("span");
40520         tempEl.appendChild(this.el.dom);
40521         tempEl.innerHTML = "";
40522         this.el.remove();
40523         this.el = null;
40524     },
40525     
40526     /**
40527      * form - if the content panel contains a form - this is a reference to it.
40528      * @type {Roo.form.Form}
40529      */
40530     form : false,
40531     /**
40532      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40533      *    This contains a reference to it.
40534      * @type {Roo.View}
40535      */
40536     view : false,
40537     
40538       /**
40539      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40540      * <pre><code>
40541
40542 layout.addxtype({
40543        xtype : 'Form',
40544        items: [ .... ]
40545    }
40546 );
40547
40548 </code></pre>
40549      * @param {Object} cfg Xtype definition of item to add.
40550      */
40551     
40552     
40553     getChildContainer: function () {
40554         return this.getEl();
40555     }
40556     
40557     
40558     /*
40559         var  ret = new Roo.factory(cfg);
40560         return ret;
40561         
40562         
40563         // add form..
40564         if (cfg.xtype.match(/^Form$/)) {
40565             
40566             var el;
40567             //if (this.footer) {
40568             //    el = this.footer.container.insertSibling(false, 'before');
40569             //} else {
40570                 el = this.el.createChild();
40571             //}
40572
40573             this.form = new  Roo.form.Form(cfg);
40574             
40575             
40576             if ( this.form.allItems.length) {
40577                 this.form.render(el.dom);
40578             }
40579             return this.form;
40580         }
40581         // should only have one of theses..
40582         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40583             // views.. should not be just added - used named prop 'view''
40584             
40585             cfg.el = this.el.appendChild(document.createElement("div"));
40586             // factory?
40587             
40588             var ret = new Roo.factory(cfg);
40589              
40590              ret.render && ret.render(false, ''); // render blank..
40591             this.view = ret;
40592             return ret;
40593         }
40594         return false;
40595     }
40596     \*/
40597 });
40598  
40599 /**
40600  * @class Roo.bootstrap.panel.Grid
40601  * @extends Roo.bootstrap.panel.Content
40602  * @constructor
40603  * Create a new GridPanel.
40604  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40605  * @param {Object} config A the config object
40606   
40607  */
40608
40609
40610
40611 Roo.bootstrap.panel.Grid = function(config)
40612 {
40613     
40614       
40615     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40616         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40617
40618     config.el = this.wrapper;
40619     //this.el = this.wrapper;
40620     
40621       if (config.container) {
40622         // ctor'ed from a Border/panel.grid
40623         
40624         
40625         this.wrapper.setStyle("overflow", "hidden");
40626         this.wrapper.addClass('roo-grid-container');
40627
40628     }
40629     
40630     
40631     if(config.toolbar){
40632         var tool_el = this.wrapper.createChild();    
40633         this.toolbar = Roo.factory(config.toolbar);
40634         var ti = [];
40635         if (config.toolbar.items) {
40636             ti = config.toolbar.items ;
40637             delete config.toolbar.items ;
40638         }
40639         
40640         var nitems = [];
40641         this.toolbar.render(tool_el);
40642         for(var i =0;i < ti.length;i++) {
40643           //  Roo.log(['add child', items[i]]);
40644             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40645         }
40646         this.toolbar.items = nitems;
40647         
40648         delete config.toolbar;
40649     }
40650     
40651     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40652     config.grid.scrollBody = true;;
40653     config.grid.monitorWindowResize = false; // turn off autosizing
40654     config.grid.autoHeight = false;
40655     config.grid.autoWidth = false;
40656     
40657     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40658     
40659     if (config.background) {
40660         // render grid on panel activation (if panel background)
40661         this.on('activate', function(gp) {
40662             if (!gp.grid.rendered) {
40663                 gp.grid.render(this.wrapper);
40664                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40665             }
40666         });
40667             
40668     } else {
40669         this.grid.render(this.wrapper);
40670         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40671
40672     }
40673     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40674     // ??? needed ??? config.el = this.wrapper;
40675     
40676     
40677     
40678   
40679     // xtype created footer. - not sure if will work as we normally have to render first..
40680     if (this.footer && !this.footer.el && this.footer.xtype) {
40681         
40682         var ctr = this.grid.getView().getFooterPanel(true);
40683         this.footer.dataSource = this.grid.dataSource;
40684         this.footer = Roo.factory(this.footer, Roo);
40685         this.footer.render(ctr);
40686         
40687     }
40688     
40689     
40690     
40691     
40692      
40693 };
40694
40695 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40696     getId : function(){
40697         return this.grid.id;
40698     },
40699     
40700     /**
40701      * Returns the grid for this panel
40702      * @return {Roo.bootstrap.Table} 
40703      */
40704     getGrid : function(){
40705         return this.grid;    
40706     },
40707     
40708     setSize : function(width, height){
40709         if(!this.ignoreResize(width, height)){
40710             var grid = this.grid;
40711             var size = this.adjustForComponents(width, height);
40712             // tfoot is not a footer?
40713           
40714             
40715             var gridel = grid.getGridEl();
40716             gridel.setSize(size.width, size.height);
40717             
40718             var tbd = grid.getGridEl().select('tbody', true).first();
40719             var thd = grid.getGridEl().select('thead',true).first();
40720             var tbf= grid.getGridEl().select('tfoot', true).first();
40721
40722             if (tbf) {
40723                 size.height -= tbf.getHeight();
40724             }
40725             if (thd) {
40726                 size.height -= thd.getHeight();
40727             }
40728             
40729             tbd.setSize(size.width, size.height );
40730             // this is for the account management tab -seems to work there.
40731             var thd = grid.getGridEl().select('thead',true).first();
40732             //if (tbd) {
40733             //    tbd.setSize(size.width, size.height - thd.getHeight());
40734             //}
40735              
40736             grid.autoSize();
40737         }
40738     },
40739      
40740     
40741     
40742     beforeSlide : function(){
40743         this.grid.getView().scroller.clip();
40744     },
40745     
40746     afterSlide : function(){
40747         this.grid.getView().scroller.unclip();
40748     },
40749     
40750     destroy : function(){
40751         this.grid.destroy();
40752         delete this.grid;
40753         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40754     }
40755 });
40756
40757 /**
40758  * @class Roo.bootstrap.panel.Nest
40759  * @extends Roo.bootstrap.panel.Content
40760  * @constructor
40761  * Create a new Panel, that can contain a layout.Border.
40762  * 
40763  * 
40764  * @param {Roo.BorderLayout} layout The layout for this panel
40765  * @param {String/Object} config A string to set only the title or a config object
40766  */
40767 Roo.bootstrap.panel.Nest = function(config)
40768 {
40769     // construct with only one argument..
40770     /* FIXME - implement nicer consturctors
40771     if (layout.layout) {
40772         config = layout;
40773         layout = config.layout;
40774         delete config.layout;
40775     }
40776     if (layout.xtype && !layout.getEl) {
40777         // then layout needs constructing..
40778         layout = Roo.factory(layout, Roo);
40779     }
40780     */
40781     
40782     config.el =  config.layout.getEl();
40783     
40784     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40785     
40786     config.layout.monitorWindowResize = false; // turn off autosizing
40787     this.layout = config.layout;
40788     this.layout.getEl().addClass("roo-layout-nested-layout");
40789     this.layout.parent = this;
40790     
40791     
40792     
40793     
40794 };
40795
40796 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40797
40798     setSize : function(width, height){
40799         if(!this.ignoreResize(width, height)){
40800             var size = this.adjustForComponents(width, height);
40801             var el = this.layout.getEl();
40802             if (size.height < 1) {
40803                 el.setWidth(size.width);   
40804             } else {
40805                 el.setSize(size.width, size.height);
40806             }
40807             var touch = el.dom.offsetWidth;
40808             this.layout.layout();
40809             // ie requires a double layout on the first pass
40810             if(Roo.isIE && !this.initialized){
40811                 this.initialized = true;
40812                 this.layout.layout();
40813             }
40814         }
40815     },
40816     
40817     // activate all subpanels if not currently active..
40818     
40819     setActiveState : function(active){
40820         this.active = active;
40821         this.setActiveClass(active);
40822         
40823         if(!active){
40824             this.fireEvent("deactivate", this);
40825             return;
40826         }
40827         
40828         this.fireEvent("activate", this);
40829         // not sure if this should happen before or after..
40830         if (!this.layout) {
40831             return; // should not happen..
40832         }
40833         var reg = false;
40834         for (var r in this.layout.regions) {
40835             reg = this.layout.getRegion(r);
40836             if (reg.getActivePanel()) {
40837                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40838                 reg.setActivePanel(reg.getActivePanel());
40839                 continue;
40840             }
40841             if (!reg.panels.length) {
40842                 continue;
40843             }
40844             reg.showPanel(reg.getPanel(0));
40845         }
40846         
40847         
40848         
40849         
40850     },
40851     
40852     /**
40853      * Returns the nested BorderLayout for this panel
40854      * @return {Roo.BorderLayout} 
40855      */
40856     getLayout : function(){
40857         return this.layout;
40858     },
40859     
40860      /**
40861      * Adds a xtype elements to the layout of the nested panel
40862      * <pre><code>
40863
40864 panel.addxtype({
40865        xtype : 'ContentPanel',
40866        region: 'west',
40867        items: [ .... ]
40868    }
40869 );
40870
40871 panel.addxtype({
40872         xtype : 'NestedLayoutPanel',
40873         region: 'west',
40874         layout: {
40875            center: { },
40876            west: { }   
40877         },
40878         items : [ ... list of content panels or nested layout panels.. ]
40879    }
40880 );
40881 </code></pre>
40882      * @param {Object} cfg Xtype definition of item to add.
40883      */
40884     addxtype : function(cfg) {
40885         return this.layout.addxtype(cfg);
40886     
40887     }
40888 });/*
40889  * Based on:
40890  * Ext JS Library 1.1.1
40891  * Copyright(c) 2006-2007, Ext JS, LLC.
40892  *
40893  * Originally Released Under LGPL - original licence link has changed is not relivant.
40894  *
40895  * Fork - LGPL
40896  * <script type="text/javascript">
40897  */
40898 /**
40899  * @class Roo.TabPanel
40900  * @extends Roo.util.Observable
40901  * A lightweight tab container.
40902  * <br><br>
40903  * Usage:
40904  * <pre><code>
40905 // basic tabs 1, built from existing content
40906 var tabs = new Roo.TabPanel("tabs1");
40907 tabs.addTab("script", "View Script");
40908 tabs.addTab("markup", "View Markup");
40909 tabs.activate("script");
40910
40911 // more advanced tabs, built from javascript
40912 var jtabs = new Roo.TabPanel("jtabs");
40913 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40914
40915 // set up the UpdateManager
40916 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40917 var updater = tab2.getUpdateManager();
40918 updater.setDefaultUrl("ajax1.htm");
40919 tab2.on('activate', updater.refresh, updater, true);
40920
40921 // Use setUrl for Ajax loading
40922 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40923 tab3.setUrl("ajax2.htm", null, true);
40924
40925 // Disabled tab
40926 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40927 tab4.disable();
40928
40929 jtabs.activate("jtabs-1");
40930  * </code></pre>
40931  * @constructor
40932  * Create a new TabPanel.
40933  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40934  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40935  */
40936 Roo.bootstrap.panel.Tabs = function(config){
40937     /**
40938     * The container element for this TabPanel.
40939     * @type Roo.Element
40940     */
40941     this.el = Roo.get(config.el);
40942     delete config.el;
40943     if(config){
40944         if(typeof config == "boolean"){
40945             this.tabPosition = config ? "bottom" : "top";
40946         }else{
40947             Roo.apply(this, config);
40948         }
40949     }
40950     
40951     if(this.tabPosition == "bottom"){
40952         // if tabs are at the bottom = create the body first.
40953         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40954         this.el.addClass("roo-tabs-bottom");
40955     }
40956     // next create the tabs holders
40957     
40958     if (this.tabPosition == "west"){
40959         
40960         var reg = this.region; // fake it..
40961         while (reg) {
40962             if (!reg.mgr.parent) {
40963                 break;
40964             }
40965             reg = reg.mgr.parent.region;
40966         }
40967         Roo.log("got nest?");
40968         Roo.log(reg);
40969         if (reg.mgr.getRegion('west')) {
40970             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40971             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40972             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40973             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40974             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40975         
40976             
40977         }
40978         
40979         
40980     } else {
40981      
40982         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40983         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40984         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40985         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40986     }
40987     
40988     
40989     if(Roo.isIE){
40990         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40991     }
40992     
40993     // finally - if tabs are at the top, then create the body last..
40994     if(this.tabPosition != "bottom"){
40995         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40996          * @type Roo.Element
40997          */
40998         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40999         this.el.addClass("roo-tabs-top");
41000     }
41001     this.items = [];
41002
41003     this.bodyEl.setStyle("position", "relative");
41004
41005     this.active = null;
41006     this.activateDelegate = this.activate.createDelegate(this);
41007
41008     this.addEvents({
41009         /**
41010          * @event tabchange
41011          * Fires when the active tab changes
41012          * @param {Roo.TabPanel} this
41013          * @param {Roo.TabPanelItem} activePanel The new active tab
41014          */
41015         "tabchange": true,
41016         /**
41017          * @event beforetabchange
41018          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41019          * @param {Roo.TabPanel} this
41020          * @param {Object} e Set cancel to true on this object to cancel the tab change
41021          * @param {Roo.TabPanelItem} tab The tab being changed to
41022          */
41023         "beforetabchange" : true
41024     });
41025
41026     Roo.EventManager.onWindowResize(this.onResize, this);
41027     this.cpad = this.el.getPadding("lr");
41028     this.hiddenCount = 0;
41029
41030
41031     // toolbar on the tabbar support...
41032     if (this.toolbar) {
41033         alert("no toolbar support yet");
41034         this.toolbar  = false;
41035         /*
41036         var tcfg = this.toolbar;
41037         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41038         this.toolbar = new Roo.Toolbar(tcfg);
41039         if (Roo.isSafari) {
41040             var tbl = tcfg.container.child('table', true);
41041             tbl.setAttribute('width', '100%');
41042         }
41043         */
41044         
41045     }
41046    
41047
41048
41049     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41050 };
41051
41052 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41053     /*
41054      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41055      */
41056     tabPosition : "top",
41057     /*
41058      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41059      */
41060     currentTabWidth : 0,
41061     /*
41062      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41063      */
41064     minTabWidth : 40,
41065     /*
41066      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41067      */
41068     maxTabWidth : 250,
41069     /*
41070      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41071      */
41072     preferredTabWidth : 175,
41073     /*
41074      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41075      */
41076     resizeTabs : false,
41077     /*
41078      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41079      */
41080     monitorResize : true,
41081     /*
41082      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41083      */
41084     toolbar : false,  // set by caller..
41085     
41086     region : false, /// set by caller
41087     
41088     disableTooltips : true, // not used yet...
41089
41090     /**
41091      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41092      * @param {String} id The id of the div to use <b>or create</b>
41093      * @param {String} text The text for the tab
41094      * @param {String} content (optional) Content to put in the TabPanelItem body
41095      * @param {Boolean} closable (optional) True to create a close icon on the tab
41096      * @return {Roo.TabPanelItem} The created TabPanelItem
41097      */
41098     addTab : function(id, text, content, closable, tpl)
41099     {
41100         var item = new Roo.bootstrap.panel.TabItem({
41101             panel: this,
41102             id : id,
41103             text : text,
41104             closable : closable,
41105             tpl : tpl
41106         });
41107         this.addTabItem(item);
41108         if(content){
41109             item.setContent(content);
41110         }
41111         return item;
41112     },
41113
41114     /**
41115      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41116      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41117      * @return {Roo.TabPanelItem}
41118      */
41119     getTab : function(id){
41120         return this.items[id];
41121     },
41122
41123     /**
41124      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41125      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41126      */
41127     hideTab : function(id){
41128         var t = this.items[id];
41129         if(!t.isHidden()){
41130            t.setHidden(true);
41131            this.hiddenCount++;
41132            this.autoSizeTabs();
41133         }
41134     },
41135
41136     /**
41137      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41138      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41139      */
41140     unhideTab : function(id){
41141         var t = this.items[id];
41142         if(t.isHidden()){
41143            t.setHidden(false);
41144            this.hiddenCount--;
41145            this.autoSizeTabs();
41146         }
41147     },
41148
41149     /**
41150      * Adds an existing {@link Roo.TabPanelItem}.
41151      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41152      */
41153     addTabItem : function(item)
41154     {
41155         this.items[item.id] = item;
41156         this.items.push(item);
41157         this.autoSizeTabs();
41158       //  if(this.resizeTabs){
41159     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41160   //         this.autoSizeTabs();
41161 //        }else{
41162 //            item.autoSize();
41163        // }
41164     },
41165
41166     /**
41167      * Removes a {@link Roo.TabPanelItem}.
41168      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41169      */
41170     removeTab : function(id){
41171         var items = this.items;
41172         var tab = items[id];
41173         if(!tab) { return; }
41174         var index = items.indexOf(tab);
41175         if(this.active == tab && items.length > 1){
41176             var newTab = this.getNextAvailable(index);
41177             if(newTab) {
41178                 newTab.activate();
41179             }
41180         }
41181         this.stripEl.dom.removeChild(tab.pnode.dom);
41182         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41183             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41184         }
41185         items.splice(index, 1);
41186         delete this.items[tab.id];
41187         tab.fireEvent("close", tab);
41188         tab.purgeListeners();
41189         this.autoSizeTabs();
41190     },
41191
41192     getNextAvailable : function(start){
41193         var items = this.items;
41194         var index = start;
41195         // look for a next tab that will slide over to
41196         // replace the one being removed
41197         while(index < items.length){
41198             var item = items[++index];
41199             if(item && !item.isHidden()){
41200                 return item;
41201             }
41202         }
41203         // if one isn't found select the previous tab (on the left)
41204         index = start;
41205         while(index >= 0){
41206             var item = items[--index];
41207             if(item && !item.isHidden()){
41208                 return item;
41209             }
41210         }
41211         return null;
41212     },
41213
41214     /**
41215      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41216      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41217      */
41218     disableTab : function(id){
41219         var tab = this.items[id];
41220         if(tab && this.active != tab){
41221             tab.disable();
41222         }
41223     },
41224
41225     /**
41226      * Enables a {@link Roo.TabPanelItem} that is disabled.
41227      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41228      */
41229     enableTab : function(id){
41230         var tab = this.items[id];
41231         tab.enable();
41232     },
41233
41234     /**
41235      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41236      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41237      * @return {Roo.TabPanelItem} The TabPanelItem.
41238      */
41239     activate : function(id)
41240     {
41241         //Roo.log('activite:'  + id);
41242         
41243         var tab = this.items[id];
41244         if(!tab){
41245             return null;
41246         }
41247         if(tab == this.active || tab.disabled){
41248             return tab;
41249         }
41250         var e = {};
41251         this.fireEvent("beforetabchange", this, e, tab);
41252         if(e.cancel !== true && !tab.disabled){
41253             if(this.active){
41254                 this.active.hide();
41255             }
41256             this.active = this.items[id];
41257             this.active.show();
41258             this.fireEvent("tabchange", this, this.active);
41259         }
41260         return tab;
41261     },
41262
41263     /**
41264      * Gets the active {@link Roo.TabPanelItem}.
41265      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41266      */
41267     getActiveTab : function(){
41268         return this.active;
41269     },
41270
41271     /**
41272      * Updates the tab body element to fit the height of the container element
41273      * for overflow scrolling
41274      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41275      */
41276     syncHeight : function(targetHeight){
41277         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41278         var bm = this.bodyEl.getMargins();
41279         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41280         this.bodyEl.setHeight(newHeight);
41281         return newHeight;
41282     },
41283
41284     onResize : function(){
41285         if(this.monitorResize){
41286             this.autoSizeTabs();
41287         }
41288     },
41289
41290     /**
41291      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41292      */
41293     beginUpdate : function(){
41294         this.updating = true;
41295     },
41296
41297     /**
41298      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41299      */
41300     endUpdate : function(){
41301         this.updating = false;
41302         this.autoSizeTabs();
41303     },
41304
41305     /**
41306      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41307      */
41308     autoSizeTabs : function()
41309     {
41310         var count = this.items.length;
41311         var vcount = count - this.hiddenCount;
41312         
41313         if (vcount < 2) {
41314             this.stripEl.hide();
41315         } else {
41316             this.stripEl.show();
41317         }
41318         
41319         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41320             return;
41321         }
41322         
41323         
41324         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41325         var availWidth = Math.floor(w / vcount);
41326         var b = this.stripBody;
41327         if(b.getWidth() > w){
41328             var tabs = this.items;
41329             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41330             if(availWidth < this.minTabWidth){
41331                 /*if(!this.sleft){    // incomplete scrolling code
41332                     this.createScrollButtons();
41333                 }
41334                 this.showScroll();
41335                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41336             }
41337         }else{
41338             if(this.currentTabWidth < this.preferredTabWidth){
41339                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41340             }
41341         }
41342     },
41343
41344     /**
41345      * Returns the number of tabs in this TabPanel.
41346      * @return {Number}
41347      */
41348      getCount : function(){
41349          return this.items.length;
41350      },
41351
41352     /**
41353      * Resizes all the tabs to the passed width
41354      * @param {Number} The new width
41355      */
41356     setTabWidth : function(width){
41357         this.currentTabWidth = width;
41358         for(var i = 0, len = this.items.length; i < len; i++) {
41359                 if(!this.items[i].isHidden()) {
41360                 this.items[i].setWidth(width);
41361             }
41362         }
41363     },
41364
41365     /**
41366      * Destroys this TabPanel
41367      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41368      */
41369     destroy : function(removeEl){
41370         Roo.EventManager.removeResizeListener(this.onResize, this);
41371         for(var i = 0, len = this.items.length; i < len; i++){
41372             this.items[i].purgeListeners();
41373         }
41374         if(removeEl === true){
41375             this.el.update("");
41376             this.el.remove();
41377         }
41378     },
41379     
41380     createStrip : function(container)
41381     {
41382         var strip = document.createElement("nav");
41383         strip.className = Roo.bootstrap.version == 4 ?
41384             "navbar-light bg-light" : 
41385             "navbar navbar-default"; //"x-tabs-wrap";
41386         container.appendChild(strip);
41387         return strip;
41388     },
41389     
41390     createStripList : function(strip)
41391     {
41392         // div wrapper for retard IE
41393         // returns the "tr" element.
41394         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41395         //'<div class="x-tabs-strip-wrap">'+
41396           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41397           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41398         return strip.firstChild; //.firstChild.firstChild.firstChild;
41399     },
41400     createBody : function(container)
41401     {
41402         var body = document.createElement("div");
41403         Roo.id(body, "tab-body");
41404         //Roo.fly(body).addClass("x-tabs-body");
41405         Roo.fly(body).addClass("tab-content");
41406         container.appendChild(body);
41407         return body;
41408     },
41409     createItemBody :function(bodyEl, id){
41410         var body = Roo.getDom(id);
41411         if(!body){
41412             body = document.createElement("div");
41413             body.id = id;
41414         }
41415         //Roo.fly(body).addClass("x-tabs-item-body");
41416         Roo.fly(body).addClass("tab-pane");
41417          bodyEl.insertBefore(body, bodyEl.firstChild);
41418         return body;
41419     },
41420     /** @private */
41421     createStripElements :  function(stripEl, text, closable, tpl)
41422     {
41423         var td = document.createElement("li"); // was td..
41424         td.className = 'nav-item';
41425         
41426         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41427         
41428         
41429         stripEl.appendChild(td);
41430         /*if(closable){
41431             td.className = "x-tabs-closable";
41432             if(!this.closeTpl){
41433                 this.closeTpl = new Roo.Template(
41434                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41435                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41436                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41437                 );
41438             }
41439             var el = this.closeTpl.overwrite(td, {"text": text});
41440             var close = el.getElementsByTagName("div")[0];
41441             var inner = el.getElementsByTagName("em")[0];
41442             return {"el": el, "close": close, "inner": inner};
41443         } else {
41444         */
41445         // not sure what this is..
41446 //            if(!this.tabTpl){
41447                 //this.tabTpl = new Roo.Template(
41448                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41449                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41450                 //);
41451 //                this.tabTpl = new Roo.Template(
41452 //                   '<a href="#">' +
41453 //                   '<span unselectable="on"' +
41454 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41455 //                            ' >{text}</span></a>'
41456 //                );
41457 //                
41458 //            }
41459
41460
41461             var template = tpl || this.tabTpl || false;
41462             
41463             if(!template){
41464                 template =  new Roo.Template(
41465                         Roo.bootstrap.version == 4 ? 
41466                             (
41467                                 '<a class="nav-link" href="#" unselectable="on"' +
41468                                      (this.disableTooltips ? '' : ' title="{text}"') +
41469                                      ' >{text}</a>'
41470                             ) : (
41471                                 '<a class="nav-link" href="#">' +
41472                                 '<span unselectable="on"' +
41473                                          (this.disableTooltips ? '' : ' title="{text}"') +
41474                                     ' >{text}</span></a>'
41475                             )
41476                 );
41477             }
41478             
41479             switch (typeof(template)) {
41480                 case 'object' :
41481                     break;
41482                 case 'string' :
41483                     template = new Roo.Template(template);
41484                     break;
41485                 default :
41486                     break;
41487             }
41488             
41489             var el = template.overwrite(td, {"text": text});
41490             
41491             var inner = el.getElementsByTagName("span")[0];
41492             
41493             return {"el": el, "inner": inner};
41494             
41495     }
41496         
41497     
41498 });
41499
41500 /**
41501  * @class Roo.TabPanelItem
41502  * @extends Roo.util.Observable
41503  * Represents an individual item (tab plus body) in a TabPanel.
41504  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41505  * @param {String} id The id of this TabPanelItem
41506  * @param {String} text The text for the tab of this TabPanelItem
41507  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41508  */
41509 Roo.bootstrap.panel.TabItem = function(config){
41510     /**
41511      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41512      * @type Roo.TabPanel
41513      */
41514     this.tabPanel = config.panel;
41515     /**
41516      * The id for this TabPanelItem
41517      * @type String
41518      */
41519     this.id = config.id;
41520     /** @private */
41521     this.disabled = false;
41522     /** @private */
41523     this.text = config.text;
41524     /** @private */
41525     this.loaded = false;
41526     this.closable = config.closable;
41527
41528     /**
41529      * The body element for this TabPanelItem.
41530      * @type Roo.Element
41531      */
41532     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41533     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41534     this.bodyEl.setStyle("display", "block");
41535     this.bodyEl.setStyle("zoom", "1");
41536     //this.hideAction();
41537
41538     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41539     /** @private */
41540     this.el = Roo.get(els.el);
41541     this.inner = Roo.get(els.inner, true);
41542      this.textEl = Roo.bootstrap.version == 4 ?
41543         this.el : Roo.get(this.el.dom.firstChild, true);
41544
41545     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41546     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41547
41548     
41549 //    this.el.on("mousedown", this.onTabMouseDown, this);
41550     this.el.on("click", this.onTabClick, this);
41551     /** @private */
41552     if(config.closable){
41553         var c = Roo.get(els.close, true);
41554         c.dom.title = this.closeText;
41555         c.addClassOnOver("close-over");
41556         c.on("click", this.closeClick, this);
41557      }
41558
41559     this.addEvents({
41560          /**
41561          * @event activate
41562          * Fires when this tab becomes the active tab.
41563          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41564          * @param {Roo.TabPanelItem} this
41565          */
41566         "activate": true,
41567         /**
41568          * @event beforeclose
41569          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41570          * @param {Roo.TabPanelItem} this
41571          * @param {Object} e Set cancel to true on this object to cancel the close.
41572          */
41573         "beforeclose": true,
41574         /**
41575          * @event close
41576          * Fires when this tab is closed.
41577          * @param {Roo.TabPanelItem} this
41578          */
41579          "close": true,
41580         /**
41581          * @event deactivate
41582          * Fires when this tab is no longer the active tab.
41583          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41584          * @param {Roo.TabPanelItem} this
41585          */
41586          "deactivate" : true
41587     });
41588     this.hidden = false;
41589
41590     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41591 };
41592
41593 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41594            {
41595     purgeListeners : function(){
41596        Roo.util.Observable.prototype.purgeListeners.call(this);
41597        this.el.removeAllListeners();
41598     },
41599     /**
41600      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41601      */
41602     show : function(){
41603         this.status_node.addClass("active");
41604         this.showAction();
41605         if(Roo.isOpera){
41606             this.tabPanel.stripWrap.repaint();
41607         }
41608         this.fireEvent("activate", this.tabPanel, this);
41609     },
41610
41611     /**
41612      * Returns true if this tab is the active tab.
41613      * @return {Boolean}
41614      */
41615     isActive : function(){
41616         return this.tabPanel.getActiveTab() == this;
41617     },
41618
41619     /**
41620      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41621      */
41622     hide : function(){
41623         this.status_node.removeClass("active");
41624         this.hideAction();
41625         this.fireEvent("deactivate", this.tabPanel, this);
41626     },
41627
41628     hideAction : function(){
41629         this.bodyEl.hide();
41630         this.bodyEl.setStyle("position", "absolute");
41631         this.bodyEl.setLeft("-20000px");
41632         this.bodyEl.setTop("-20000px");
41633     },
41634
41635     showAction : function(){
41636         this.bodyEl.setStyle("position", "relative");
41637         this.bodyEl.setTop("");
41638         this.bodyEl.setLeft("");
41639         this.bodyEl.show();
41640     },
41641
41642     /**
41643      * Set the tooltip for the tab.
41644      * @param {String} tooltip The tab's tooltip
41645      */
41646     setTooltip : function(text){
41647         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41648             this.textEl.dom.qtip = text;
41649             this.textEl.dom.removeAttribute('title');
41650         }else{
41651             this.textEl.dom.title = text;
41652         }
41653     },
41654
41655     onTabClick : function(e){
41656         e.preventDefault();
41657         this.tabPanel.activate(this.id);
41658     },
41659
41660     onTabMouseDown : function(e){
41661         e.preventDefault();
41662         this.tabPanel.activate(this.id);
41663     },
41664 /*
41665     getWidth : function(){
41666         return this.inner.getWidth();
41667     },
41668
41669     setWidth : function(width){
41670         var iwidth = width - this.linode.getPadding("lr");
41671         this.inner.setWidth(iwidth);
41672         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41673         this.linode.setWidth(width);
41674     },
41675 */
41676     /**
41677      * Show or hide the tab
41678      * @param {Boolean} hidden True to hide or false to show.
41679      */
41680     setHidden : function(hidden){
41681         this.hidden = hidden;
41682         this.linode.setStyle("display", hidden ? "none" : "");
41683     },
41684
41685     /**
41686      * Returns true if this tab is "hidden"
41687      * @return {Boolean}
41688      */
41689     isHidden : function(){
41690         return this.hidden;
41691     },
41692
41693     /**
41694      * Returns the text for this tab
41695      * @return {String}
41696      */
41697     getText : function(){
41698         return this.text;
41699     },
41700     /*
41701     autoSize : function(){
41702         //this.el.beginMeasure();
41703         this.textEl.setWidth(1);
41704         /*
41705          *  #2804 [new] Tabs in Roojs
41706          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41707          */
41708         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41709         //this.el.endMeasure();
41710     //},
41711
41712     /**
41713      * Sets the text for the tab (Note: this also sets the tooltip text)
41714      * @param {String} text The tab's text and tooltip
41715      */
41716     setText : function(text){
41717         this.text = text;
41718         this.textEl.update(text);
41719         this.setTooltip(text);
41720         //if(!this.tabPanel.resizeTabs){
41721         //    this.autoSize();
41722         //}
41723     },
41724     /**
41725      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41726      */
41727     activate : function(){
41728         this.tabPanel.activate(this.id);
41729     },
41730
41731     /**
41732      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41733      */
41734     disable : function(){
41735         if(this.tabPanel.active != this){
41736             this.disabled = true;
41737             this.status_node.addClass("disabled");
41738         }
41739     },
41740
41741     /**
41742      * Enables this TabPanelItem if it was previously disabled.
41743      */
41744     enable : function(){
41745         this.disabled = false;
41746         this.status_node.removeClass("disabled");
41747     },
41748
41749     /**
41750      * Sets the content for this TabPanelItem.
41751      * @param {String} content The content
41752      * @param {Boolean} loadScripts true to look for and load scripts
41753      */
41754     setContent : function(content, loadScripts){
41755         this.bodyEl.update(content, loadScripts);
41756     },
41757
41758     /**
41759      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41760      * @return {Roo.UpdateManager} The UpdateManager
41761      */
41762     getUpdateManager : function(){
41763         return this.bodyEl.getUpdateManager();
41764     },
41765
41766     /**
41767      * Set a URL to be used to load the content for this TabPanelItem.
41768      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41769      * @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)
41770      * @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)
41771      * @return {Roo.UpdateManager} The UpdateManager
41772      */
41773     setUrl : function(url, params, loadOnce){
41774         if(this.refreshDelegate){
41775             this.un('activate', this.refreshDelegate);
41776         }
41777         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41778         this.on("activate", this.refreshDelegate);
41779         return this.bodyEl.getUpdateManager();
41780     },
41781
41782     /** @private */
41783     _handleRefresh : function(url, params, loadOnce){
41784         if(!loadOnce || !this.loaded){
41785             var updater = this.bodyEl.getUpdateManager();
41786             updater.update(url, params, this._setLoaded.createDelegate(this));
41787         }
41788     },
41789
41790     /**
41791      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41792      *   Will fail silently if the setUrl method has not been called.
41793      *   This does not activate the panel, just updates its content.
41794      */
41795     refresh : function(){
41796         if(this.refreshDelegate){
41797            this.loaded = false;
41798            this.refreshDelegate();
41799         }
41800     },
41801
41802     /** @private */
41803     _setLoaded : function(){
41804         this.loaded = true;
41805     },
41806
41807     /** @private */
41808     closeClick : function(e){
41809         var o = {};
41810         e.stopEvent();
41811         this.fireEvent("beforeclose", this, o);
41812         if(o.cancel !== true){
41813             this.tabPanel.removeTab(this.id);
41814         }
41815     },
41816     /**
41817      * The text displayed in the tooltip for the close icon.
41818      * @type String
41819      */
41820     closeText : "Close this tab"
41821 });
41822 /**
41823 *    This script refer to:
41824 *    Title: International Telephone Input
41825 *    Author: Jack O'Connor
41826 *    Code version:  v12.1.12
41827 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41828 **/
41829
41830 Roo.bootstrap.PhoneInputData = function() {
41831     var d = [
41832       [
41833         "Afghanistan (‫افغانستان‬‎)",
41834         "af",
41835         "93"
41836       ],
41837       [
41838         "Albania (Shqipëri)",
41839         "al",
41840         "355"
41841       ],
41842       [
41843         "Algeria (‫الجزائر‬‎)",
41844         "dz",
41845         "213"
41846       ],
41847       [
41848         "American Samoa",
41849         "as",
41850         "1684"
41851       ],
41852       [
41853         "Andorra",
41854         "ad",
41855         "376"
41856       ],
41857       [
41858         "Angola",
41859         "ao",
41860         "244"
41861       ],
41862       [
41863         "Anguilla",
41864         "ai",
41865         "1264"
41866       ],
41867       [
41868         "Antigua and Barbuda",
41869         "ag",
41870         "1268"
41871       ],
41872       [
41873         "Argentina",
41874         "ar",
41875         "54"
41876       ],
41877       [
41878         "Armenia (Հայաստան)",
41879         "am",
41880         "374"
41881       ],
41882       [
41883         "Aruba",
41884         "aw",
41885         "297"
41886       ],
41887       [
41888         "Australia",
41889         "au",
41890         "61",
41891         0
41892       ],
41893       [
41894         "Austria (Österreich)",
41895         "at",
41896         "43"
41897       ],
41898       [
41899         "Azerbaijan (Azərbaycan)",
41900         "az",
41901         "994"
41902       ],
41903       [
41904         "Bahamas",
41905         "bs",
41906         "1242"
41907       ],
41908       [
41909         "Bahrain (‫البحرين‬‎)",
41910         "bh",
41911         "973"
41912       ],
41913       [
41914         "Bangladesh (বাংলাদেশ)",
41915         "bd",
41916         "880"
41917       ],
41918       [
41919         "Barbados",
41920         "bb",
41921         "1246"
41922       ],
41923       [
41924         "Belarus (Беларусь)",
41925         "by",
41926         "375"
41927       ],
41928       [
41929         "Belgium (België)",
41930         "be",
41931         "32"
41932       ],
41933       [
41934         "Belize",
41935         "bz",
41936         "501"
41937       ],
41938       [
41939         "Benin (Bénin)",
41940         "bj",
41941         "229"
41942       ],
41943       [
41944         "Bermuda",
41945         "bm",
41946         "1441"
41947       ],
41948       [
41949         "Bhutan (འབྲུག)",
41950         "bt",
41951         "975"
41952       ],
41953       [
41954         "Bolivia",
41955         "bo",
41956         "591"
41957       ],
41958       [
41959         "Bosnia and Herzegovina (Босна и Херцеговина)",
41960         "ba",
41961         "387"
41962       ],
41963       [
41964         "Botswana",
41965         "bw",
41966         "267"
41967       ],
41968       [
41969         "Brazil (Brasil)",
41970         "br",
41971         "55"
41972       ],
41973       [
41974         "British Indian Ocean Territory",
41975         "io",
41976         "246"
41977       ],
41978       [
41979         "British Virgin Islands",
41980         "vg",
41981         "1284"
41982       ],
41983       [
41984         "Brunei",
41985         "bn",
41986         "673"
41987       ],
41988       [
41989         "Bulgaria (България)",
41990         "bg",
41991         "359"
41992       ],
41993       [
41994         "Burkina Faso",
41995         "bf",
41996         "226"
41997       ],
41998       [
41999         "Burundi (Uburundi)",
42000         "bi",
42001         "257"
42002       ],
42003       [
42004         "Cambodia (កម្ពុជា)",
42005         "kh",
42006         "855"
42007       ],
42008       [
42009         "Cameroon (Cameroun)",
42010         "cm",
42011         "237"
42012       ],
42013       [
42014         "Canada",
42015         "ca",
42016         "1",
42017         1,
42018         ["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"]
42019       ],
42020       [
42021         "Cape Verde (Kabu Verdi)",
42022         "cv",
42023         "238"
42024       ],
42025       [
42026         "Caribbean Netherlands",
42027         "bq",
42028         "599",
42029         1
42030       ],
42031       [
42032         "Cayman Islands",
42033         "ky",
42034         "1345"
42035       ],
42036       [
42037         "Central African Republic (République centrafricaine)",
42038         "cf",
42039         "236"
42040       ],
42041       [
42042         "Chad (Tchad)",
42043         "td",
42044         "235"
42045       ],
42046       [
42047         "Chile",
42048         "cl",
42049         "56"
42050       ],
42051       [
42052         "China (中国)",
42053         "cn",
42054         "86"
42055       ],
42056       [
42057         "Christmas Island",
42058         "cx",
42059         "61",
42060         2
42061       ],
42062       [
42063         "Cocos (Keeling) Islands",
42064         "cc",
42065         "61",
42066         1
42067       ],
42068       [
42069         "Colombia",
42070         "co",
42071         "57"
42072       ],
42073       [
42074         "Comoros (‫جزر القمر‬‎)",
42075         "km",
42076         "269"
42077       ],
42078       [
42079         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42080         "cd",
42081         "243"
42082       ],
42083       [
42084         "Congo (Republic) (Congo-Brazzaville)",
42085         "cg",
42086         "242"
42087       ],
42088       [
42089         "Cook Islands",
42090         "ck",
42091         "682"
42092       ],
42093       [
42094         "Costa Rica",
42095         "cr",
42096         "506"
42097       ],
42098       [
42099         "Côte d’Ivoire",
42100         "ci",
42101         "225"
42102       ],
42103       [
42104         "Croatia (Hrvatska)",
42105         "hr",
42106         "385"
42107       ],
42108       [
42109         "Cuba",
42110         "cu",
42111         "53"
42112       ],
42113       [
42114         "Curaçao",
42115         "cw",
42116         "599",
42117         0
42118       ],
42119       [
42120         "Cyprus (Κύπρος)",
42121         "cy",
42122         "357"
42123       ],
42124       [
42125         "Czech Republic (Česká republika)",
42126         "cz",
42127         "420"
42128       ],
42129       [
42130         "Denmark (Danmark)",
42131         "dk",
42132         "45"
42133       ],
42134       [
42135         "Djibouti",
42136         "dj",
42137         "253"
42138       ],
42139       [
42140         "Dominica",
42141         "dm",
42142         "1767"
42143       ],
42144       [
42145         "Dominican Republic (República Dominicana)",
42146         "do",
42147         "1",
42148         2,
42149         ["809", "829", "849"]
42150       ],
42151       [
42152         "Ecuador",
42153         "ec",
42154         "593"
42155       ],
42156       [
42157         "Egypt (‫مصر‬‎)",
42158         "eg",
42159         "20"
42160       ],
42161       [
42162         "El Salvador",
42163         "sv",
42164         "503"
42165       ],
42166       [
42167         "Equatorial Guinea (Guinea Ecuatorial)",
42168         "gq",
42169         "240"
42170       ],
42171       [
42172         "Eritrea",
42173         "er",
42174         "291"
42175       ],
42176       [
42177         "Estonia (Eesti)",
42178         "ee",
42179         "372"
42180       ],
42181       [
42182         "Ethiopia",
42183         "et",
42184         "251"
42185       ],
42186       [
42187         "Falkland Islands (Islas Malvinas)",
42188         "fk",
42189         "500"
42190       ],
42191       [
42192         "Faroe Islands (Føroyar)",
42193         "fo",
42194         "298"
42195       ],
42196       [
42197         "Fiji",
42198         "fj",
42199         "679"
42200       ],
42201       [
42202         "Finland (Suomi)",
42203         "fi",
42204         "358",
42205         0
42206       ],
42207       [
42208         "France",
42209         "fr",
42210         "33"
42211       ],
42212       [
42213         "French Guiana (Guyane française)",
42214         "gf",
42215         "594"
42216       ],
42217       [
42218         "French Polynesia (Polynésie française)",
42219         "pf",
42220         "689"
42221       ],
42222       [
42223         "Gabon",
42224         "ga",
42225         "241"
42226       ],
42227       [
42228         "Gambia",
42229         "gm",
42230         "220"
42231       ],
42232       [
42233         "Georgia (საქართველო)",
42234         "ge",
42235         "995"
42236       ],
42237       [
42238         "Germany (Deutschland)",
42239         "de",
42240         "49"
42241       ],
42242       [
42243         "Ghana (Gaana)",
42244         "gh",
42245         "233"
42246       ],
42247       [
42248         "Gibraltar",
42249         "gi",
42250         "350"
42251       ],
42252       [
42253         "Greece (Ελλάδα)",
42254         "gr",
42255         "30"
42256       ],
42257       [
42258         "Greenland (Kalaallit Nunaat)",
42259         "gl",
42260         "299"
42261       ],
42262       [
42263         "Grenada",
42264         "gd",
42265         "1473"
42266       ],
42267       [
42268         "Guadeloupe",
42269         "gp",
42270         "590",
42271         0
42272       ],
42273       [
42274         "Guam",
42275         "gu",
42276         "1671"
42277       ],
42278       [
42279         "Guatemala",
42280         "gt",
42281         "502"
42282       ],
42283       [
42284         "Guernsey",
42285         "gg",
42286         "44",
42287         1
42288       ],
42289       [
42290         "Guinea (Guinée)",
42291         "gn",
42292         "224"
42293       ],
42294       [
42295         "Guinea-Bissau (Guiné Bissau)",
42296         "gw",
42297         "245"
42298       ],
42299       [
42300         "Guyana",
42301         "gy",
42302         "592"
42303       ],
42304       [
42305         "Haiti",
42306         "ht",
42307         "509"
42308       ],
42309       [
42310         "Honduras",
42311         "hn",
42312         "504"
42313       ],
42314       [
42315         "Hong Kong (香港)",
42316         "hk",
42317         "852"
42318       ],
42319       [
42320         "Hungary (Magyarország)",
42321         "hu",
42322         "36"
42323       ],
42324       [
42325         "Iceland (Ísland)",
42326         "is",
42327         "354"
42328       ],
42329       [
42330         "India (भारत)",
42331         "in",
42332         "91"
42333       ],
42334       [
42335         "Indonesia",
42336         "id",
42337         "62"
42338       ],
42339       [
42340         "Iran (‫ایران‬‎)",
42341         "ir",
42342         "98"
42343       ],
42344       [
42345         "Iraq (‫العراق‬‎)",
42346         "iq",
42347         "964"
42348       ],
42349       [
42350         "Ireland",
42351         "ie",
42352         "353"
42353       ],
42354       [
42355         "Isle of Man",
42356         "im",
42357         "44",
42358         2
42359       ],
42360       [
42361         "Israel (‫ישראל‬‎)",
42362         "il",
42363         "972"
42364       ],
42365       [
42366         "Italy (Italia)",
42367         "it",
42368         "39",
42369         0
42370       ],
42371       [
42372         "Jamaica",
42373         "jm",
42374         "1876"
42375       ],
42376       [
42377         "Japan (日本)",
42378         "jp",
42379         "81"
42380       ],
42381       [
42382         "Jersey",
42383         "je",
42384         "44",
42385         3
42386       ],
42387       [
42388         "Jordan (‫الأردن‬‎)",
42389         "jo",
42390         "962"
42391       ],
42392       [
42393         "Kazakhstan (Казахстан)",
42394         "kz",
42395         "7",
42396         1
42397       ],
42398       [
42399         "Kenya",
42400         "ke",
42401         "254"
42402       ],
42403       [
42404         "Kiribati",
42405         "ki",
42406         "686"
42407       ],
42408       [
42409         "Kosovo",
42410         "xk",
42411         "383"
42412       ],
42413       [
42414         "Kuwait (‫الكويت‬‎)",
42415         "kw",
42416         "965"
42417       ],
42418       [
42419         "Kyrgyzstan (Кыргызстан)",
42420         "kg",
42421         "996"
42422       ],
42423       [
42424         "Laos (ລາວ)",
42425         "la",
42426         "856"
42427       ],
42428       [
42429         "Latvia (Latvija)",
42430         "lv",
42431         "371"
42432       ],
42433       [
42434         "Lebanon (‫لبنان‬‎)",
42435         "lb",
42436         "961"
42437       ],
42438       [
42439         "Lesotho",
42440         "ls",
42441         "266"
42442       ],
42443       [
42444         "Liberia",
42445         "lr",
42446         "231"
42447       ],
42448       [
42449         "Libya (‫ليبيا‬‎)",
42450         "ly",
42451         "218"
42452       ],
42453       [
42454         "Liechtenstein",
42455         "li",
42456         "423"
42457       ],
42458       [
42459         "Lithuania (Lietuva)",
42460         "lt",
42461         "370"
42462       ],
42463       [
42464         "Luxembourg",
42465         "lu",
42466         "352"
42467       ],
42468       [
42469         "Macau (澳門)",
42470         "mo",
42471         "853"
42472       ],
42473       [
42474         "Macedonia (FYROM) (Македонија)",
42475         "mk",
42476         "389"
42477       ],
42478       [
42479         "Madagascar (Madagasikara)",
42480         "mg",
42481         "261"
42482       ],
42483       [
42484         "Malawi",
42485         "mw",
42486         "265"
42487       ],
42488       [
42489         "Malaysia",
42490         "my",
42491         "60"
42492       ],
42493       [
42494         "Maldives",
42495         "mv",
42496         "960"
42497       ],
42498       [
42499         "Mali",
42500         "ml",
42501         "223"
42502       ],
42503       [
42504         "Malta",
42505         "mt",
42506         "356"
42507       ],
42508       [
42509         "Marshall Islands",
42510         "mh",
42511         "692"
42512       ],
42513       [
42514         "Martinique",
42515         "mq",
42516         "596"
42517       ],
42518       [
42519         "Mauritania (‫موريتانيا‬‎)",
42520         "mr",
42521         "222"
42522       ],
42523       [
42524         "Mauritius (Moris)",
42525         "mu",
42526         "230"
42527       ],
42528       [
42529         "Mayotte",
42530         "yt",
42531         "262",
42532         1
42533       ],
42534       [
42535         "Mexico (México)",
42536         "mx",
42537         "52"
42538       ],
42539       [
42540         "Micronesia",
42541         "fm",
42542         "691"
42543       ],
42544       [
42545         "Moldova (Republica Moldova)",
42546         "md",
42547         "373"
42548       ],
42549       [
42550         "Monaco",
42551         "mc",
42552         "377"
42553       ],
42554       [
42555         "Mongolia (Монгол)",
42556         "mn",
42557         "976"
42558       ],
42559       [
42560         "Montenegro (Crna Gora)",
42561         "me",
42562         "382"
42563       ],
42564       [
42565         "Montserrat",
42566         "ms",
42567         "1664"
42568       ],
42569       [
42570         "Morocco (‫المغرب‬‎)",
42571         "ma",
42572         "212",
42573         0
42574       ],
42575       [
42576         "Mozambique (Moçambique)",
42577         "mz",
42578         "258"
42579       ],
42580       [
42581         "Myanmar (Burma) (မြန်မာ)",
42582         "mm",
42583         "95"
42584       ],
42585       [
42586         "Namibia (Namibië)",
42587         "na",
42588         "264"
42589       ],
42590       [
42591         "Nauru",
42592         "nr",
42593         "674"
42594       ],
42595       [
42596         "Nepal (नेपाल)",
42597         "np",
42598         "977"
42599       ],
42600       [
42601         "Netherlands (Nederland)",
42602         "nl",
42603         "31"
42604       ],
42605       [
42606         "New Caledonia (Nouvelle-Calédonie)",
42607         "nc",
42608         "687"
42609       ],
42610       [
42611         "New Zealand",
42612         "nz",
42613         "64"
42614       ],
42615       [
42616         "Nicaragua",
42617         "ni",
42618         "505"
42619       ],
42620       [
42621         "Niger (Nijar)",
42622         "ne",
42623         "227"
42624       ],
42625       [
42626         "Nigeria",
42627         "ng",
42628         "234"
42629       ],
42630       [
42631         "Niue",
42632         "nu",
42633         "683"
42634       ],
42635       [
42636         "Norfolk Island",
42637         "nf",
42638         "672"
42639       ],
42640       [
42641         "North Korea (조선 민주주의 인민 공화국)",
42642         "kp",
42643         "850"
42644       ],
42645       [
42646         "Northern Mariana Islands",
42647         "mp",
42648         "1670"
42649       ],
42650       [
42651         "Norway (Norge)",
42652         "no",
42653         "47",
42654         0
42655       ],
42656       [
42657         "Oman (‫عُمان‬‎)",
42658         "om",
42659         "968"
42660       ],
42661       [
42662         "Pakistan (‫پاکستان‬‎)",
42663         "pk",
42664         "92"
42665       ],
42666       [
42667         "Palau",
42668         "pw",
42669         "680"
42670       ],
42671       [
42672         "Palestine (‫فلسطين‬‎)",
42673         "ps",
42674         "970"
42675       ],
42676       [
42677         "Panama (Panamá)",
42678         "pa",
42679         "507"
42680       ],
42681       [
42682         "Papua New Guinea",
42683         "pg",
42684         "675"
42685       ],
42686       [
42687         "Paraguay",
42688         "py",
42689         "595"
42690       ],
42691       [
42692         "Peru (Perú)",
42693         "pe",
42694         "51"
42695       ],
42696       [
42697         "Philippines",
42698         "ph",
42699         "63"
42700       ],
42701       [
42702         "Poland (Polska)",
42703         "pl",
42704         "48"
42705       ],
42706       [
42707         "Portugal",
42708         "pt",
42709         "351"
42710       ],
42711       [
42712         "Puerto Rico",
42713         "pr",
42714         "1",
42715         3,
42716         ["787", "939"]
42717       ],
42718       [
42719         "Qatar (‫قطر‬‎)",
42720         "qa",
42721         "974"
42722       ],
42723       [
42724         "Réunion (La Réunion)",
42725         "re",
42726         "262",
42727         0
42728       ],
42729       [
42730         "Romania (România)",
42731         "ro",
42732         "40"
42733       ],
42734       [
42735         "Russia (Россия)",
42736         "ru",
42737         "7",
42738         0
42739       ],
42740       [
42741         "Rwanda",
42742         "rw",
42743         "250"
42744       ],
42745       [
42746         "Saint Barthélemy",
42747         "bl",
42748         "590",
42749         1
42750       ],
42751       [
42752         "Saint Helena",
42753         "sh",
42754         "290"
42755       ],
42756       [
42757         "Saint Kitts and Nevis",
42758         "kn",
42759         "1869"
42760       ],
42761       [
42762         "Saint Lucia",
42763         "lc",
42764         "1758"
42765       ],
42766       [
42767         "Saint Martin (Saint-Martin (partie française))",
42768         "mf",
42769         "590",
42770         2
42771       ],
42772       [
42773         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42774         "pm",
42775         "508"
42776       ],
42777       [
42778         "Saint Vincent and the Grenadines",
42779         "vc",
42780         "1784"
42781       ],
42782       [
42783         "Samoa",
42784         "ws",
42785         "685"
42786       ],
42787       [
42788         "San Marino",
42789         "sm",
42790         "378"
42791       ],
42792       [
42793         "São Tomé and Príncipe (São Tomé e Príncipe)",
42794         "st",
42795         "239"
42796       ],
42797       [
42798         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42799         "sa",
42800         "966"
42801       ],
42802       [
42803         "Senegal (Sénégal)",
42804         "sn",
42805         "221"
42806       ],
42807       [
42808         "Serbia (Србија)",
42809         "rs",
42810         "381"
42811       ],
42812       [
42813         "Seychelles",
42814         "sc",
42815         "248"
42816       ],
42817       [
42818         "Sierra Leone",
42819         "sl",
42820         "232"
42821       ],
42822       [
42823         "Singapore",
42824         "sg",
42825         "65"
42826       ],
42827       [
42828         "Sint Maarten",
42829         "sx",
42830         "1721"
42831       ],
42832       [
42833         "Slovakia (Slovensko)",
42834         "sk",
42835         "421"
42836       ],
42837       [
42838         "Slovenia (Slovenija)",
42839         "si",
42840         "386"
42841       ],
42842       [
42843         "Solomon Islands",
42844         "sb",
42845         "677"
42846       ],
42847       [
42848         "Somalia (Soomaaliya)",
42849         "so",
42850         "252"
42851       ],
42852       [
42853         "South Africa",
42854         "za",
42855         "27"
42856       ],
42857       [
42858         "South Korea (대한민국)",
42859         "kr",
42860         "82"
42861       ],
42862       [
42863         "South Sudan (‫جنوب السودان‬‎)",
42864         "ss",
42865         "211"
42866       ],
42867       [
42868         "Spain (España)",
42869         "es",
42870         "34"
42871       ],
42872       [
42873         "Sri Lanka (ශ්‍රී ලංකාව)",
42874         "lk",
42875         "94"
42876       ],
42877       [
42878         "Sudan (‫السودان‬‎)",
42879         "sd",
42880         "249"
42881       ],
42882       [
42883         "Suriname",
42884         "sr",
42885         "597"
42886       ],
42887       [
42888         "Svalbard and Jan Mayen",
42889         "sj",
42890         "47",
42891         1
42892       ],
42893       [
42894         "Swaziland",
42895         "sz",
42896         "268"
42897       ],
42898       [
42899         "Sweden (Sverige)",
42900         "se",
42901         "46"
42902       ],
42903       [
42904         "Switzerland (Schweiz)",
42905         "ch",
42906         "41"
42907       ],
42908       [
42909         "Syria (‫سوريا‬‎)",
42910         "sy",
42911         "963"
42912       ],
42913       [
42914         "Taiwan (台灣)",
42915         "tw",
42916         "886"
42917       ],
42918       [
42919         "Tajikistan",
42920         "tj",
42921         "992"
42922       ],
42923       [
42924         "Tanzania",
42925         "tz",
42926         "255"
42927       ],
42928       [
42929         "Thailand (ไทย)",
42930         "th",
42931         "66"
42932       ],
42933       [
42934         "Timor-Leste",
42935         "tl",
42936         "670"
42937       ],
42938       [
42939         "Togo",
42940         "tg",
42941         "228"
42942       ],
42943       [
42944         "Tokelau",
42945         "tk",
42946         "690"
42947       ],
42948       [
42949         "Tonga",
42950         "to",
42951         "676"
42952       ],
42953       [
42954         "Trinidad and Tobago",
42955         "tt",
42956         "1868"
42957       ],
42958       [
42959         "Tunisia (‫تونس‬‎)",
42960         "tn",
42961         "216"
42962       ],
42963       [
42964         "Turkey (Türkiye)",
42965         "tr",
42966         "90"
42967       ],
42968       [
42969         "Turkmenistan",
42970         "tm",
42971         "993"
42972       ],
42973       [
42974         "Turks and Caicos Islands",
42975         "tc",
42976         "1649"
42977       ],
42978       [
42979         "Tuvalu",
42980         "tv",
42981         "688"
42982       ],
42983       [
42984         "U.S. Virgin Islands",
42985         "vi",
42986         "1340"
42987       ],
42988       [
42989         "Uganda",
42990         "ug",
42991         "256"
42992       ],
42993       [
42994         "Ukraine (Україна)",
42995         "ua",
42996         "380"
42997       ],
42998       [
42999         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43000         "ae",
43001         "971"
43002       ],
43003       [
43004         "United Kingdom",
43005         "gb",
43006         "44",
43007         0
43008       ],
43009       [
43010         "United States",
43011         "us",
43012         "1",
43013         0
43014       ],
43015       [
43016         "Uruguay",
43017         "uy",
43018         "598"
43019       ],
43020       [
43021         "Uzbekistan (Oʻzbekiston)",
43022         "uz",
43023         "998"
43024       ],
43025       [
43026         "Vanuatu",
43027         "vu",
43028         "678"
43029       ],
43030       [
43031         "Vatican City (Città del Vaticano)",
43032         "va",
43033         "39",
43034         1
43035       ],
43036       [
43037         "Venezuela",
43038         "ve",
43039         "58"
43040       ],
43041       [
43042         "Vietnam (Việt Nam)",
43043         "vn",
43044         "84"
43045       ],
43046       [
43047         "Wallis and Futuna (Wallis-et-Futuna)",
43048         "wf",
43049         "681"
43050       ],
43051       [
43052         "Western Sahara (‫الصحراء الغربية‬‎)",
43053         "eh",
43054         "212",
43055         1
43056       ],
43057       [
43058         "Yemen (‫اليمن‬‎)",
43059         "ye",
43060         "967"
43061       ],
43062       [
43063         "Zambia",
43064         "zm",
43065         "260"
43066       ],
43067       [
43068         "Zimbabwe",
43069         "zw",
43070         "263"
43071       ],
43072       [
43073         "Åland Islands",
43074         "ax",
43075         "358",
43076         1
43077       ]
43078   ];
43079   
43080   return d;
43081 }/**
43082 *    This script refer to:
43083 *    Title: International Telephone Input
43084 *    Author: Jack O'Connor
43085 *    Code version:  v12.1.12
43086 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43087 **/
43088
43089 /**
43090  * @class Roo.bootstrap.PhoneInput
43091  * @extends Roo.bootstrap.TriggerField
43092  * An input with International dial-code selection
43093  
43094  * @cfg {String} defaultDialCode default '+852'
43095  * @cfg {Array} preferedCountries default []
43096   
43097  * @constructor
43098  * Create a new PhoneInput.
43099  * @param {Object} config Configuration options
43100  */
43101
43102 Roo.bootstrap.PhoneInput = function(config) {
43103     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43104 };
43105
43106 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43107         
43108         listWidth: undefined,
43109         
43110         selectedClass: 'active',
43111         
43112         invalidClass : "has-warning",
43113         
43114         validClass: 'has-success',
43115         
43116         allowed: '0123456789',
43117         
43118         max_length: 15,
43119         
43120         /**
43121          * @cfg {String} defaultDialCode The default dial code when initializing the input
43122          */
43123         defaultDialCode: '+852',
43124         
43125         /**
43126          * @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
43127          */
43128         preferedCountries: false,
43129         
43130         getAutoCreate : function()
43131         {
43132             var data = Roo.bootstrap.PhoneInputData();
43133             var align = this.labelAlign || this.parentLabelAlign();
43134             var id = Roo.id();
43135             
43136             this.allCountries = [];
43137             this.dialCodeMapping = [];
43138             
43139             for (var i = 0; i < data.length; i++) {
43140               var c = data[i];
43141               this.allCountries[i] = {
43142                 name: c[0],
43143                 iso2: c[1],
43144                 dialCode: c[2],
43145                 priority: c[3] || 0,
43146                 areaCodes: c[4] || null
43147               };
43148               this.dialCodeMapping[c[2]] = {
43149                   name: c[0],
43150                   iso2: c[1],
43151                   priority: c[3] || 0,
43152                   areaCodes: c[4] || null
43153               };
43154             }
43155             
43156             var cfg = {
43157                 cls: 'form-group',
43158                 cn: []
43159             };
43160             
43161             var input =  {
43162                 tag: 'input',
43163                 id : id,
43164                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43165                 maxlength: this.max_length,
43166                 cls : 'form-control tel-input',
43167                 autocomplete: 'new-password'
43168             };
43169             
43170             var hiddenInput = {
43171                 tag: 'input',
43172                 type: 'hidden',
43173                 cls: 'hidden-tel-input'
43174             };
43175             
43176             if (this.name) {
43177                 hiddenInput.name = this.name;
43178             }
43179             
43180             if (this.disabled) {
43181                 input.disabled = true;
43182             }
43183             
43184             var flag_container = {
43185                 tag: 'div',
43186                 cls: 'flag-box',
43187                 cn: [
43188                     {
43189                         tag: 'div',
43190                         cls: 'flag'
43191                     },
43192                     {
43193                         tag: 'div',
43194                         cls: 'caret'
43195                     }
43196                 ]
43197             };
43198             
43199             var box = {
43200                 tag: 'div',
43201                 cls: this.hasFeedback ? 'has-feedback' : '',
43202                 cn: [
43203                     hiddenInput,
43204                     input,
43205                     {
43206                         tag: 'input',
43207                         cls: 'dial-code-holder',
43208                         disabled: true
43209                     }
43210                 ]
43211             };
43212             
43213             var container = {
43214                 cls: 'roo-select2-container input-group',
43215                 cn: [
43216                     flag_container,
43217                     box
43218                 ]
43219             };
43220             
43221             if (this.fieldLabel.length) {
43222                 var indicator = {
43223                     tag: 'i',
43224                     tooltip: 'This field is required'
43225                 };
43226                 
43227                 var label = {
43228                     tag: 'label',
43229                     'for':  id,
43230                     cls: 'control-label',
43231                     cn: []
43232                 };
43233                 
43234                 var label_text = {
43235                     tag: 'span',
43236                     html: this.fieldLabel
43237                 };
43238                 
43239                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43240                 label.cn = [
43241                     indicator,
43242                     label_text
43243                 ];
43244                 
43245                 if(this.indicatorpos == 'right') {
43246                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43247                     label.cn = [
43248                         label_text,
43249                         indicator
43250                     ];
43251                 }
43252                 
43253                 if(align == 'left') {
43254                     container = {
43255                         tag: 'div',
43256                         cn: [
43257                             container
43258                         ]
43259                     };
43260                     
43261                     if(this.labelWidth > 12){
43262                         label.style = "width: " + this.labelWidth + 'px';
43263                     }
43264                     if(this.labelWidth < 13 && this.labelmd == 0){
43265                         this.labelmd = this.labelWidth;
43266                     }
43267                     if(this.labellg > 0){
43268                         label.cls += ' col-lg-' + this.labellg;
43269                         input.cls += ' col-lg-' + (12 - this.labellg);
43270                     }
43271                     if(this.labelmd > 0){
43272                         label.cls += ' col-md-' + this.labelmd;
43273                         container.cls += ' col-md-' + (12 - this.labelmd);
43274                     }
43275                     if(this.labelsm > 0){
43276                         label.cls += ' col-sm-' + this.labelsm;
43277                         container.cls += ' col-sm-' + (12 - this.labelsm);
43278                     }
43279                     if(this.labelxs > 0){
43280                         label.cls += ' col-xs-' + this.labelxs;
43281                         container.cls += ' col-xs-' + (12 - this.labelxs);
43282                     }
43283                 }
43284             }
43285             
43286             cfg.cn = [
43287                 label,
43288                 container
43289             ];
43290             
43291             var settings = this;
43292             
43293             ['xs','sm','md','lg'].map(function(size){
43294                 if (settings[size]) {
43295                     cfg.cls += ' col-' + size + '-' + settings[size];
43296                 }
43297             });
43298             
43299             this.store = new Roo.data.Store({
43300                 proxy : new Roo.data.MemoryProxy({}),
43301                 reader : new Roo.data.JsonReader({
43302                     fields : [
43303                         {
43304                             'name' : 'name',
43305                             'type' : 'string'
43306                         },
43307                         {
43308                             'name' : 'iso2',
43309                             'type' : 'string'
43310                         },
43311                         {
43312                             'name' : 'dialCode',
43313                             'type' : 'string'
43314                         },
43315                         {
43316                             'name' : 'priority',
43317                             'type' : 'string'
43318                         },
43319                         {
43320                             'name' : 'areaCodes',
43321                             'type' : 'string'
43322                         }
43323                     ]
43324                 })
43325             });
43326             
43327             if(!this.preferedCountries) {
43328                 this.preferedCountries = [
43329                     'hk',
43330                     'gb',
43331                     'us'
43332                 ];
43333             }
43334             
43335             var p = this.preferedCountries.reverse();
43336             
43337             if(p) {
43338                 for (var i = 0; i < p.length; i++) {
43339                     for (var j = 0; j < this.allCountries.length; j++) {
43340                         if(this.allCountries[j].iso2 == p[i]) {
43341                             var t = this.allCountries[j];
43342                             this.allCountries.splice(j,1);
43343                             this.allCountries.unshift(t);
43344                         }
43345                     } 
43346                 }
43347             }
43348             
43349             this.store.proxy.data = {
43350                 success: true,
43351                 data: this.allCountries
43352             };
43353             
43354             return cfg;
43355         },
43356         
43357         initEvents : function()
43358         {
43359             this.createList();
43360             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43361             
43362             this.indicator = this.indicatorEl();
43363             this.flag = this.flagEl();
43364             this.dialCodeHolder = this.dialCodeHolderEl();
43365             
43366             this.trigger = this.el.select('div.flag-box',true).first();
43367             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43368             
43369             var _this = this;
43370             
43371             (function(){
43372                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43373                 _this.list.setWidth(lw);
43374             }).defer(100);
43375             
43376             this.list.on('mouseover', this.onViewOver, this);
43377             this.list.on('mousemove', this.onViewMove, this);
43378             this.inputEl().on("keyup", this.onKeyUp, this);
43379             this.inputEl().on("keypress", this.onKeyPress, this);
43380             
43381             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43382
43383             this.view = new Roo.View(this.list, this.tpl, {
43384                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43385             });
43386             
43387             this.view.on('click', this.onViewClick, this);
43388             this.setValue(this.defaultDialCode);
43389         },
43390         
43391         onTriggerClick : function(e)
43392         {
43393             Roo.log('trigger click');
43394             if(this.disabled){
43395                 return;
43396             }
43397             
43398             if(this.isExpanded()){
43399                 this.collapse();
43400                 this.hasFocus = false;
43401             }else {
43402                 this.store.load({});
43403                 this.hasFocus = true;
43404                 this.expand();
43405             }
43406         },
43407         
43408         isExpanded : function()
43409         {
43410             return this.list.isVisible();
43411         },
43412         
43413         collapse : function()
43414         {
43415             if(!this.isExpanded()){
43416                 return;
43417             }
43418             this.list.hide();
43419             Roo.get(document).un('mousedown', this.collapseIf, this);
43420             Roo.get(document).un('mousewheel', this.collapseIf, this);
43421             this.fireEvent('collapse', this);
43422             this.validate();
43423         },
43424         
43425         expand : function()
43426         {
43427             Roo.log('expand');
43428
43429             if(this.isExpanded() || !this.hasFocus){
43430                 return;
43431             }
43432             
43433             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43434             this.list.setWidth(lw);
43435             
43436             this.list.show();
43437             this.restrictHeight();
43438             
43439             Roo.get(document).on('mousedown', this.collapseIf, this);
43440             Roo.get(document).on('mousewheel', this.collapseIf, this);
43441             
43442             this.fireEvent('expand', this);
43443         },
43444         
43445         restrictHeight : function()
43446         {
43447             this.list.alignTo(this.inputEl(), this.listAlign);
43448             this.list.alignTo(this.inputEl(), this.listAlign);
43449         },
43450         
43451         onViewOver : function(e, t)
43452         {
43453             if(this.inKeyMode){
43454                 return;
43455             }
43456             var item = this.view.findItemFromChild(t);
43457             
43458             if(item){
43459                 var index = this.view.indexOf(item);
43460                 this.select(index, false);
43461             }
43462         },
43463
43464         // private
43465         onViewClick : function(view, doFocus, el, e)
43466         {
43467             var index = this.view.getSelectedIndexes()[0];
43468             
43469             var r = this.store.getAt(index);
43470             
43471             if(r){
43472                 this.onSelect(r, index);
43473             }
43474             if(doFocus !== false && !this.blockFocus){
43475                 this.inputEl().focus();
43476             }
43477         },
43478         
43479         onViewMove : function(e, t)
43480         {
43481             this.inKeyMode = false;
43482         },
43483         
43484         select : function(index, scrollIntoView)
43485         {
43486             this.selectedIndex = index;
43487             this.view.select(index);
43488             if(scrollIntoView !== false){
43489                 var el = this.view.getNode(index);
43490                 if(el){
43491                     this.list.scrollChildIntoView(el, false);
43492                 }
43493             }
43494         },
43495         
43496         createList : function()
43497         {
43498             this.list = Roo.get(document.body).createChild({
43499                 tag: 'ul',
43500                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43501                 style: 'display:none'
43502             });
43503             
43504             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43505         },
43506         
43507         collapseIf : function(e)
43508         {
43509             var in_combo  = e.within(this.el);
43510             var in_list =  e.within(this.list);
43511             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43512             
43513             if (in_combo || in_list || is_list) {
43514                 return;
43515             }
43516             this.collapse();
43517         },
43518         
43519         onSelect : function(record, index)
43520         {
43521             if(this.fireEvent('beforeselect', this, record, index) !== false){
43522                 
43523                 this.setFlagClass(record.data.iso2);
43524                 this.setDialCode(record.data.dialCode);
43525                 this.hasFocus = false;
43526                 this.collapse();
43527                 this.fireEvent('select', this, record, index);
43528             }
43529         },
43530         
43531         flagEl : function()
43532         {
43533             var flag = this.el.select('div.flag',true).first();
43534             if(!flag){
43535                 return false;
43536             }
43537             return flag;
43538         },
43539         
43540         dialCodeHolderEl : function()
43541         {
43542             var d = this.el.select('input.dial-code-holder',true).first();
43543             if(!d){
43544                 return false;
43545             }
43546             return d;
43547         },
43548         
43549         setDialCode : function(v)
43550         {
43551             this.dialCodeHolder.dom.value = '+'+v;
43552         },
43553         
43554         setFlagClass : function(n)
43555         {
43556             this.flag.dom.className = 'flag '+n;
43557         },
43558         
43559         getValue : function()
43560         {
43561             var v = this.inputEl().getValue();
43562             if(this.dialCodeHolder) {
43563                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43564             }
43565             return v;
43566         },
43567         
43568         setValue : function(v)
43569         {
43570             var d = this.getDialCode(v);
43571             
43572             //invalid dial code
43573             if(v.length == 0 || !d || d.length == 0) {
43574                 if(this.rendered){
43575                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43576                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43577                 }
43578                 return;
43579             }
43580             
43581             //valid dial code
43582             this.setFlagClass(this.dialCodeMapping[d].iso2);
43583             this.setDialCode(d);
43584             this.inputEl().dom.value = v.replace('+'+d,'');
43585             this.hiddenEl().dom.value = this.getValue();
43586             
43587             this.validate();
43588         },
43589         
43590         getDialCode : function(v)
43591         {
43592             v = v ||  '';
43593             
43594             if (v.length == 0) {
43595                 return this.dialCodeHolder.dom.value;
43596             }
43597             
43598             var dialCode = "";
43599             if (v.charAt(0) != "+") {
43600                 return false;
43601             }
43602             var numericChars = "";
43603             for (var i = 1; i < v.length; i++) {
43604               var c = v.charAt(i);
43605               if (!isNaN(c)) {
43606                 numericChars += c;
43607                 if (this.dialCodeMapping[numericChars]) {
43608                   dialCode = v.substr(1, i);
43609                 }
43610                 if (numericChars.length == 4) {
43611                   break;
43612                 }
43613               }
43614             }
43615             return dialCode;
43616         },
43617         
43618         reset : function()
43619         {
43620             this.setValue(this.defaultDialCode);
43621             this.validate();
43622         },
43623         
43624         hiddenEl : function()
43625         {
43626             return this.el.select('input.hidden-tel-input',true).first();
43627         },
43628         
43629         // after setting val
43630         onKeyUp : function(e){
43631             this.setValue(this.getValue());
43632         },
43633         
43634         onKeyPress : function(e){
43635             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43636                 e.stopEvent();
43637             }
43638         }
43639         
43640 });
43641 /**
43642  * @class Roo.bootstrap.MoneyField
43643  * @extends Roo.bootstrap.ComboBox
43644  * Bootstrap MoneyField class
43645  * 
43646  * @constructor
43647  * Create a new MoneyField.
43648  * @param {Object} config Configuration options
43649  */
43650
43651 Roo.bootstrap.MoneyField = function(config) {
43652     
43653     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43654     
43655 };
43656
43657 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43658     
43659     /**
43660      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43661      */
43662     allowDecimals : true,
43663     /**
43664      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43665      */
43666     decimalSeparator : ".",
43667     /**
43668      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43669      */
43670     decimalPrecision : 0,
43671     /**
43672      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43673      */
43674     allowNegative : true,
43675     /**
43676      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43677      */
43678     allowZero: true,
43679     /**
43680      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43681      */
43682     minValue : Number.NEGATIVE_INFINITY,
43683     /**
43684      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43685      */
43686     maxValue : Number.MAX_VALUE,
43687     /**
43688      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43689      */
43690     minText : "The minimum value for this field is {0}",
43691     /**
43692      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43693      */
43694     maxText : "The maximum value for this field is {0}",
43695     /**
43696      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43697      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43698      */
43699     nanText : "{0} is not a valid number",
43700     /**
43701      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43702      */
43703     castInt : true,
43704     /**
43705      * @cfg {String} defaults currency of the MoneyField
43706      * value should be in lkey
43707      */
43708     defaultCurrency : false,
43709     /**
43710      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43711      */
43712     thousandsDelimiter : false,
43713     /**
43714      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43715      */
43716     max_length: false,
43717     
43718     inputlg : 9,
43719     inputmd : 9,
43720     inputsm : 9,
43721     inputxs : 6,
43722     
43723     store : false,
43724     
43725     getAutoCreate : function()
43726     {
43727         var align = this.labelAlign || this.parentLabelAlign();
43728         
43729         var id = Roo.id();
43730
43731         var cfg = {
43732             cls: 'form-group',
43733             cn: []
43734         };
43735
43736         var input =  {
43737             tag: 'input',
43738             id : id,
43739             cls : 'form-control roo-money-amount-input',
43740             autocomplete: 'new-password'
43741         };
43742         
43743         var hiddenInput = {
43744             tag: 'input',
43745             type: 'hidden',
43746             id: Roo.id(),
43747             cls: 'hidden-number-input'
43748         };
43749         
43750         if(this.max_length) {
43751             input.maxlength = this.max_length; 
43752         }
43753         
43754         if (this.name) {
43755             hiddenInput.name = this.name;
43756         }
43757
43758         if (this.disabled) {
43759             input.disabled = true;
43760         }
43761
43762         var clg = 12 - this.inputlg;
43763         var cmd = 12 - this.inputmd;
43764         var csm = 12 - this.inputsm;
43765         var cxs = 12 - this.inputxs;
43766         
43767         var container = {
43768             tag : 'div',
43769             cls : 'row roo-money-field',
43770             cn : [
43771                 {
43772                     tag : 'div',
43773                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43774                     cn : [
43775                         {
43776                             tag : 'div',
43777                             cls: 'roo-select2-container input-group',
43778                             cn: [
43779                                 {
43780                                     tag : 'input',
43781                                     cls : 'form-control roo-money-currency-input',
43782                                     autocomplete: 'new-password',
43783                                     readOnly : 1,
43784                                     name : this.currencyName
43785                                 },
43786                                 {
43787                                     tag :'span',
43788                                     cls : 'input-group-addon',
43789                                     cn : [
43790                                         {
43791                                             tag: 'span',
43792                                             cls: 'caret'
43793                                         }
43794                                     ]
43795                                 }
43796                             ]
43797                         }
43798                     ]
43799                 },
43800                 {
43801                     tag : 'div',
43802                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43803                     cn : [
43804                         {
43805                             tag: 'div',
43806                             cls: this.hasFeedback ? 'has-feedback' : '',
43807                             cn: [
43808                                 input
43809                             ]
43810                         }
43811                     ]
43812                 }
43813             ]
43814             
43815         };
43816         
43817         if (this.fieldLabel.length) {
43818             var indicator = {
43819                 tag: 'i',
43820                 tooltip: 'This field is required'
43821             };
43822
43823             var label = {
43824                 tag: 'label',
43825                 'for':  id,
43826                 cls: 'control-label',
43827                 cn: []
43828             };
43829
43830             var label_text = {
43831                 tag: 'span',
43832                 html: this.fieldLabel
43833             };
43834
43835             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43836             label.cn = [
43837                 indicator,
43838                 label_text
43839             ];
43840
43841             if(this.indicatorpos == 'right') {
43842                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43843                 label.cn = [
43844                     label_text,
43845                     indicator
43846                 ];
43847             }
43848
43849             if(align == 'left') {
43850                 container = {
43851                     tag: 'div',
43852                     cn: [
43853                         container
43854                     ]
43855                 };
43856
43857                 if(this.labelWidth > 12){
43858                     label.style = "width: " + this.labelWidth + 'px';
43859                 }
43860                 if(this.labelWidth < 13 && this.labelmd == 0){
43861                     this.labelmd = this.labelWidth;
43862                 }
43863                 if(this.labellg > 0){
43864                     label.cls += ' col-lg-' + this.labellg;
43865                     input.cls += ' col-lg-' + (12 - this.labellg);
43866                 }
43867                 if(this.labelmd > 0){
43868                     label.cls += ' col-md-' + this.labelmd;
43869                     container.cls += ' col-md-' + (12 - this.labelmd);
43870                 }
43871                 if(this.labelsm > 0){
43872                     label.cls += ' col-sm-' + this.labelsm;
43873                     container.cls += ' col-sm-' + (12 - this.labelsm);
43874                 }
43875                 if(this.labelxs > 0){
43876                     label.cls += ' col-xs-' + this.labelxs;
43877                     container.cls += ' col-xs-' + (12 - this.labelxs);
43878                 }
43879             }
43880         }
43881
43882         cfg.cn = [
43883             label,
43884             container,
43885             hiddenInput
43886         ];
43887         
43888         var settings = this;
43889
43890         ['xs','sm','md','lg'].map(function(size){
43891             if (settings[size]) {
43892                 cfg.cls += ' col-' + size + '-' + settings[size];
43893             }
43894         });
43895         
43896         return cfg;
43897     },
43898     
43899     initEvents : function()
43900     {
43901         this.indicator = this.indicatorEl();
43902         
43903         this.initCurrencyEvent();
43904         
43905         this.initNumberEvent();
43906     },
43907     
43908     initCurrencyEvent : function()
43909     {
43910         if (!this.store) {
43911             throw "can not find store for combo";
43912         }
43913         
43914         this.store = Roo.factory(this.store, Roo.data);
43915         this.store.parent = this;
43916         
43917         this.createList();
43918         
43919         this.triggerEl = this.el.select('.input-group-addon', true).first();
43920         
43921         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43922         
43923         var _this = this;
43924         
43925         (function(){
43926             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43927             _this.list.setWidth(lw);
43928         }).defer(100);
43929         
43930         this.list.on('mouseover', this.onViewOver, this);
43931         this.list.on('mousemove', this.onViewMove, this);
43932         this.list.on('scroll', this.onViewScroll, this);
43933         
43934         if(!this.tpl){
43935             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43936         }
43937         
43938         this.view = new Roo.View(this.list, this.tpl, {
43939             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43940         });
43941         
43942         this.view.on('click', this.onViewClick, this);
43943         
43944         this.store.on('beforeload', this.onBeforeLoad, this);
43945         this.store.on('load', this.onLoad, this);
43946         this.store.on('loadexception', this.onLoadException, this);
43947         
43948         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43949             "up" : function(e){
43950                 this.inKeyMode = true;
43951                 this.selectPrev();
43952             },
43953
43954             "down" : function(e){
43955                 if(!this.isExpanded()){
43956                     this.onTriggerClick();
43957                 }else{
43958                     this.inKeyMode = true;
43959                     this.selectNext();
43960                 }
43961             },
43962
43963             "enter" : function(e){
43964                 this.collapse();
43965                 
43966                 if(this.fireEvent("specialkey", this, e)){
43967                     this.onViewClick(false);
43968                 }
43969                 
43970                 return true;
43971             },
43972
43973             "esc" : function(e){
43974                 this.collapse();
43975             },
43976
43977             "tab" : function(e){
43978                 this.collapse();
43979                 
43980                 if(this.fireEvent("specialkey", this, e)){
43981                     this.onViewClick(false);
43982                 }
43983                 
43984                 return true;
43985             },
43986
43987             scope : this,
43988
43989             doRelay : function(foo, bar, hname){
43990                 if(hname == 'down' || this.scope.isExpanded()){
43991                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43992                 }
43993                 return true;
43994             },
43995
43996             forceKeyDown: true
43997         });
43998         
43999         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44000         
44001     },
44002     
44003     initNumberEvent : function(e)
44004     {
44005         this.inputEl().on("keydown" , this.fireKey,  this);
44006         this.inputEl().on("focus", this.onFocus,  this);
44007         this.inputEl().on("blur", this.onBlur,  this);
44008         
44009         this.inputEl().relayEvent('keyup', this);
44010         
44011         if(this.indicator){
44012             this.indicator.addClass('invisible');
44013         }
44014  
44015         this.originalValue = this.getValue();
44016         
44017         if(this.validationEvent == 'keyup'){
44018             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44019             this.inputEl().on('keyup', this.filterValidation, this);
44020         }
44021         else if(this.validationEvent !== false){
44022             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44023         }
44024         
44025         if(this.selectOnFocus){
44026             this.on("focus", this.preFocus, this);
44027             
44028         }
44029         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44030             this.inputEl().on("keypress", this.filterKeys, this);
44031         } else {
44032             this.inputEl().relayEvent('keypress', this);
44033         }
44034         
44035         var allowed = "0123456789";
44036         
44037         if(this.allowDecimals){
44038             allowed += this.decimalSeparator;
44039         }
44040         
44041         if(this.allowNegative){
44042             allowed += "-";
44043         }
44044         
44045         if(this.thousandsDelimiter) {
44046             allowed += ",";
44047         }
44048         
44049         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44050         
44051         var keyPress = function(e){
44052             
44053             var k = e.getKey();
44054             
44055             var c = e.getCharCode();
44056             
44057             if(
44058                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44059                     allowed.indexOf(String.fromCharCode(c)) === -1
44060             ){
44061                 e.stopEvent();
44062                 return;
44063             }
44064             
44065             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44066                 return;
44067             }
44068             
44069             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44070                 e.stopEvent();
44071             }
44072         };
44073         
44074         this.inputEl().on("keypress", keyPress, this);
44075         
44076     },
44077     
44078     onTriggerClick : function(e)
44079     {   
44080         if(this.disabled){
44081             return;
44082         }
44083         
44084         this.page = 0;
44085         this.loadNext = false;
44086         
44087         if(this.isExpanded()){
44088             this.collapse();
44089             return;
44090         }
44091         
44092         this.hasFocus = true;
44093         
44094         if(this.triggerAction == 'all') {
44095             this.doQuery(this.allQuery, true);
44096             return;
44097         }
44098         
44099         this.doQuery(this.getRawValue());
44100     },
44101     
44102     getCurrency : function()
44103     {   
44104         var v = this.currencyEl().getValue();
44105         
44106         return v;
44107     },
44108     
44109     restrictHeight : function()
44110     {
44111         this.list.alignTo(this.currencyEl(), this.listAlign);
44112         this.list.alignTo(this.currencyEl(), this.listAlign);
44113     },
44114     
44115     onViewClick : function(view, doFocus, el, e)
44116     {
44117         var index = this.view.getSelectedIndexes()[0];
44118         
44119         var r = this.store.getAt(index);
44120         
44121         if(r){
44122             this.onSelect(r, index);
44123         }
44124     },
44125     
44126     onSelect : function(record, index){
44127         
44128         if(this.fireEvent('beforeselect', this, record, index) !== false){
44129         
44130             this.setFromCurrencyData(index > -1 ? record.data : false);
44131             
44132             this.collapse();
44133             
44134             this.fireEvent('select', this, record, index);
44135         }
44136     },
44137     
44138     setFromCurrencyData : function(o)
44139     {
44140         var currency = '';
44141         
44142         this.lastCurrency = o;
44143         
44144         if (this.currencyField) {
44145             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44146         } else {
44147             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44148         }
44149         
44150         this.lastSelectionText = currency;
44151         
44152         //setting default currency
44153         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44154             this.setCurrency(this.defaultCurrency);
44155             return;
44156         }
44157         
44158         this.setCurrency(currency);
44159     },
44160     
44161     setFromData : function(o)
44162     {
44163         var c = {};
44164         
44165         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44166         
44167         this.setFromCurrencyData(c);
44168         
44169         var value = '';
44170         
44171         if (this.name) {
44172             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44173         } else {
44174             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44175         }
44176         
44177         this.setValue(value);
44178         
44179     },
44180     
44181     setCurrency : function(v)
44182     {   
44183         this.currencyValue = v;
44184         
44185         if(this.rendered){
44186             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44187             this.validate();
44188         }
44189     },
44190     
44191     setValue : function(v)
44192     {
44193         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44194         
44195         this.value = v;
44196         
44197         if(this.rendered){
44198             
44199             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44200             
44201             this.inputEl().dom.value = (v == '') ? '' :
44202                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44203             
44204             if(!this.allowZero && v === '0') {
44205                 this.hiddenEl().dom.value = '';
44206                 this.inputEl().dom.value = '';
44207             }
44208             
44209             this.validate();
44210         }
44211     },
44212     
44213     getRawValue : function()
44214     {
44215         var v = this.inputEl().getValue();
44216         
44217         return v;
44218     },
44219     
44220     getValue : function()
44221     {
44222         return this.fixPrecision(this.parseValue(this.getRawValue()));
44223     },
44224     
44225     parseValue : function(value)
44226     {
44227         if(this.thousandsDelimiter) {
44228             value += "";
44229             r = new RegExp(",", "g");
44230             value = value.replace(r, "");
44231         }
44232         
44233         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44234         return isNaN(value) ? '' : value;
44235         
44236     },
44237     
44238     fixPrecision : function(value)
44239     {
44240         if(this.thousandsDelimiter) {
44241             value += "";
44242             r = new RegExp(",", "g");
44243             value = value.replace(r, "");
44244         }
44245         
44246         var nan = isNaN(value);
44247         
44248         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44249             return nan ? '' : value;
44250         }
44251         return parseFloat(value).toFixed(this.decimalPrecision);
44252     },
44253     
44254     decimalPrecisionFcn : function(v)
44255     {
44256         return Math.floor(v);
44257     },
44258     
44259     validateValue : function(value)
44260     {
44261         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44262             return false;
44263         }
44264         
44265         var num = this.parseValue(value);
44266         
44267         if(isNaN(num)){
44268             this.markInvalid(String.format(this.nanText, value));
44269             return false;
44270         }
44271         
44272         if(num < this.minValue){
44273             this.markInvalid(String.format(this.minText, this.minValue));
44274             return false;
44275         }
44276         
44277         if(num > this.maxValue){
44278             this.markInvalid(String.format(this.maxText, this.maxValue));
44279             return false;
44280         }
44281         
44282         return true;
44283     },
44284     
44285     validate : function()
44286     {
44287         if(this.disabled || this.allowBlank){
44288             this.markValid();
44289             return true;
44290         }
44291         
44292         var currency = this.getCurrency();
44293         
44294         if(this.validateValue(this.getRawValue()) && currency.length){
44295             this.markValid();
44296             return true;
44297         }
44298         
44299         this.markInvalid();
44300         return false;
44301     },
44302     
44303     getName: function()
44304     {
44305         return this.name;
44306     },
44307     
44308     beforeBlur : function()
44309     {
44310         if(!this.castInt){
44311             return;
44312         }
44313         
44314         var v = this.parseValue(this.getRawValue());
44315         
44316         if(v || v == 0){
44317             this.setValue(v);
44318         }
44319     },
44320     
44321     onBlur : function()
44322     {
44323         this.beforeBlur();
44324         
44325         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44326             //this.el.removeClass(this.focusClass);
44327         }
44328         
44329         this.hasFocus = false;
44330         
44331         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44332             this.validate();
44333         }
44334         
44335         var v = this.getValue();
44336         
44337         if(String(v) !== String(this.startValue)){
44338             this.fireEvent('change', this, v, this.startValue);
44339         }
44340         
44341         this.fireEvent("blur", this);
44342     },
44343     
44344     inputEl : function()
44345     {
44346         return this.el.select('.roo-money-amount-input', true).first();
44347     },
44348     
44349     currencyEl : function()
44350     {
44351         return this.el.select('.roo-money-currency-input', true).first();
44352     },
44353     
44354     hiddenEl : function()
44355     {
44356         return this.el.select('input.hidden-number-input',true).first();
44357     }
44358     
44359 });/**
44360  * @class Roo.bootstrap.BezierSignature
44361  * @extends Roo.bootstrap.Component
44362  * Bootstrap BezierSignature class
44363  * This script refer to:
44364  *    Title: Signature Pad
44365  *    Author: szimek
44366  *    Availability: https://github.com/szimek/signature_pad
44367  *
44368  * @constructor
44369  * Create a new BezierSignature
44370  * @param {Object} config The config object
44371  */
44372
44373 Roo.bootstrap.BezierSignature = function(config){
44374     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44375     this.addEvents({
44376         "resize" : true
44377     });
44378 };
44379
44380 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44381 {
44382      
44383     curve_data: [],
44384     
44385     is_empty: true,
44386     
44387     mouse_btn_down: true,
44388     
44389     /**
44390      * @cfg {int} canvas height
44391      */
44392     canvas_height: '200px',
44393     
44394     /**
44395      * @cfg {float|function} Radius of a single dot.
44396      */ 
44397     dot_size: false,
44398     
44399     /**
44400      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44401      */
44402     min_width: 0.5,
44403     
44404     /**
44405      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44406      */
44407     max_width: 2.5,
44408     
44409     /**
44410      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44411      */
44412     throttle: 16,
44413     
44414     /**
44415      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44416      */
44417     min_distance: 5,
44418     
44419     /**
44420      * @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.
44421      */
44422     bg_color: 'rgba(0, 0, 0, 0)',
44423     
44424     /**
44425      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44426      */
44427     dot_color: 'black',
44428     
44429     /**
44430      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44431      */ 
44432     velocity_filter_weight: 0.7,
44433     
44434     /**
44435      * @cfg {function} Callback when stroke begin. 
44436      */
44437     onBegin: false,
44438     
44439     /**
44440      * @cfg {function} Callback when stroke end.
44441      */
44442     onEnd: false,
44443     
44444     getAutoCreate : function()
44445     {
44446         var cls = 'roo-signature column';
44447         
44448         if(this.cls){
44449             cls += ' ' + this.cls;
44450         }
44451         
44452         var col_sizes = [
44453             'lg',
44454             'md',
44455             'sm',
44456             'xs'
44457         ];
44458         
44459         for(var i = 0; i < col_sizes.length; i++) {
44460             if(this[col_sizes[i]]) {
44461                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44462             }
44463         }
44464         
44465         var cfg = {
44466             tag: 'div',
44467             cls: cls,
44468             cn: [
44469                 {
44470                     tag: 'div',
44471                     cls: 'roo-signature-body',
44472                     cn: [
44473                         {
44474                             tag: 'canvas',
44475                             cls: 'roo-signature-body-canvas',
44476                             height: this.canvas_height,
44477                             width: this.canvas_width
44478                         }
44479                     ]
44480                 },
44481                 {
44482                     tag: 'input',
44483                     type: 'file',
44484                     style: 'display: none'
44485                 }
44486             ]
44487         };
44488         
44489         return cfg;
44490     },
44491     
44492     initEvents: function() 
44493     {
44494         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44495         
44496         var canvas = this.canvasEl();
44497         
44498         // mouse && touch event swapping...
44499         canvas.dom.style.touchAction = 'none';
44500         canvas.dom.style.msTouchAction = 'none';
44501         
44502         this.mouse_btn_down = false;
44503         canvas.on('mousedown', this._handleMouseDown, this);
44504         canvas.on('mousemove', this._handleMouseMove, this);
44505         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44506         
44507         if (window.PointerEvent) {
44508             canvas.on('pointerdown', this._handleMouseDown, this);
44509             canvas.on('pointermove', this._handleMouseMove, this);
44510             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44511         }
44512         
44513         if ('ontouchstart' in window) {
44514             canvas.on('touchstart', this._handleTouchStart, this);
44515             canvas.on('touchmove', this._handleTouchMove, this);
44516             canvas.on('touchend', this._handleTouchEnd, this);
44517         }
44518         
44519         Roo.EventManager.onWindowResize(this.resize, this, true);
44520         
44521         // file input event
44522         this.fileEl().on('change', this.uploadImage, this);
44523         
44524         this.clear();
44525         
44526         this.resize();
44527     },
44528     
44529     resize: function(){
44530         
44531         var canvas = this.canvasEl().dom;
44532         var ctx = this.canvasElCtx();
44533         var img_data = false;
44534         
44535         if(canvas.width > 0) {
44536             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44537         }
44538         // setting canvas width will clean img data
44539         canvas.width = 0;
44540         
44541         var style = window.getComputedStyle ? 
44542             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44543             
44544         var padding_left = parseInt(style.paddingLeft) || 0;
44545         var padding_right = parseInt(style.paddingRight) || 0;
44546         
44547         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44548         
44549         if(img_data) {
44550             ctx.putImageData(img_data, 0, 0);
44551         }
44552     },
44553     
44554     _handleMouseDown: function(e)
44555     {
44556         if (e.browserEvent.which === 1) {
44557             this.mouse_btn_down = true;
44558             this.strokeBegin(e);
44559         }
44560     },
44561     
44562     _handleMouseMove: function (e)
44563     {
44564         if (this.mouse_btn_down) {
44565             this.strokeMoveUpdate(e);
44566         }
44567     },
44568     
44569     _handleMouseUp: function (e)
44570     {
44571         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44572             this.mouse_btn_down = false;
44573             this.strokeEnd(e);
44574         }
44575     },
44576     
44577     _handleTouchStart: function (e) {
44578         
44579         e.preventDefault();
44580         if (e.browserEvent.targetTouches.length === 1) {
44581             // var touch = e.browserEvent.changedTouches[0];
44582             // this.strokeBegin(touch);
44583             
44584              this.strokeBegin(e); // assume e catching the correct xy...
44585         }
44586     },
44587     
44588     _handleTouchMove: function (e) {
44589         e.preventDefault();
44590         // var touch = event.targetTouches[0];
44591         // _this._strokeMoveUpdate(touch);
44592         this.strokeMoveUpdate(e);
44593     },
44594     
44595     _handleTouchEnd: function (e) {
44596         var wasCanvasTouched = e.target === this.canvasEl().dom;
44597         if (wasCanvasTouched) {
44598             e.preventDefault();
44599             // var touch = event.changedTouches[0];
44600             // _this._strokeEnd(touch);
44601             this.strokeEnd(e);
44602         }
44603     },
44604     
44605     reset: function () {
44606         this._lastPoints = [];
44607         this._lastVelocity = 0;
44608         this._lastWidth = (this.min_width + this.max_width) / 2;
44609         this.canvasElCtx().fillStyle = this.dot_color;
44610     },
44611     
44612     strokeMoveUpdate: function(e)
44613     {
44614         this.strokeUpdate(e);
44615         
44616         if (this.throttle) {
44617             this.throttleStroke(this.strokeUpdate, this.throttle);
44618         }
44619         else {
44620             this.strokeUpdate(e);
44621         }
44622     },
44623     
44624     strokeBegin: function(e)
44625     {
44626         var newPointGroup = {
44627             color: this.dot_color,
44628             points: []
44629         };
44630         
44631         if (typeof this.onBegin === 'function') {
44632             this.onBegin(e);
44633         }
44634         
44635         this.curve_data.push(newPointGroup);
44636         this.reset();
44637         this.strokeUpdate(e);
44638     },
44639     
44640     strokeUpdate: function(e)
44641     {
44642         var rect = this.canvasEl().dom.getBoundingClientRect();
44643         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44644         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44645         var lastPoints = lastPointGroup.points;
44646         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44647         var isLastPointTooClose = lastPoint
44648             ? point.distanceTo(lastPoint) <= this.min_distance
44649             : false;
44650         var color = lastPointGroup.color;
44651         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44652             var curve = this.addPoint(point);
44653             if (!lastPoint) {
44654                 this.drawDot({color: color, point: point});
44655             }
44656             else if (curve) {
44657                 this.drawCurve({color: color, curve: curve});
44658             }
44659             lastPoints.push({
44660                 time: point.time,
44661                 x: point.x,
44662                 y: point.y
44663             });
44664         }
44665     },
44666     
44667     strokeEnd: function(e)
44668     {
44669         this.strokeUpdate(e);
44670         if (typeof this.onEnd === 'function') {
44671             this.onEnd(e);
44672         }
44673     },
44674     
44675     addPoint:  function (point) {
44676         var _lastPoints = this._lastPoints;
44677         _lastPoints.push(point);
44678         if (_lastPoints.length > 2) {
44679             if (_lastPoints.length === 3) {
44680                 _lastPoints.unshift(_lastPoints[0]);
44681             }
44682             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44683             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44684             _lastPoints.shift();
44685             return curve;
44686         }
44687         return null;
44688     },
44689     
44690     calculateCurveWidths: function (startPoint, endPoint) {
44691         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44692             (1 - this.velocity_filter_weight) * this._lastVelocity;
44693
44694         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44695         var widths = {
44696             end: newWidth,
44697             start: this._lastWidth
44698         };
44699         
44700         this._lastVelocity = velocity;
44701         this._lastWidth = newWidth;
44702         return widths;
44703     },
44704     
44705     drawDot: function (_a) {
44706         var color = _a.color, point = _a.point;
44707         var ctx = this.canvasElCtx();
44708         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44709         ctx.beginPath();
44710         this.drawCurveSegment(point.x, point.y, width);
44711         ctx.closePath();
44712         ctx.fillStyle = color;
44713         ctx.fill();
44714     },
44715     
44716     drawCurve: function (_a) {
44717         var color = _a.color, curve = _a.curve;
44718         var ctx = this.canvasElCtx();
44719         var widthDelta = curve.endWidth - curve.startWidth;
44720         var drawSteps = Math.floor(curve.length()) * 2;
44721         ctx.beginPath();
44722         ctx.fillStyle = color;
44723         for (var i = 0; i < drawSteps; i += 1) {
44724         var t = i / drawSteps;
44725         var tt = t * t;
44726         var ttt = tt * t;
44727         var u = 1 - t;
44728         var uu = u * u;
44729         var uuu = uu * u;
44730         var x = uuu * curve.startPoint.x;
44731         x += 3 * uu * t * curve.control1.x;
44732         x += 3 * u * tt * curve.control2.x;
44733         x += ttt * curve.endPoint.x;
44734         var y = uuu * curve.startPoint.y;
44735         y += 3 * uu * t * curve.control1.y;
44736         y += 3 * u * tt * curve.control2.y;
44737         y += ttt * curve.endPoint.y;
44738         var width = curve.startWidth + ttt * widthDelta;
44739         this.drawCurveSegment(x, y, width);
44740         }
44741         ctx.closePath();
44742         ctx.fill();
44743     },
44744     
44745     drawCurveSegment: function (x, y, width) {
44746         var ctx = this.canvasElCtx();
44747         ctx.moveTo(x, y);
44748         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44749         this.is_empty = false;
44750     },
44751     
44752     clear: function()
44753     {
44754         var ctx = this.canvasElCtx();
44755         var canvas = this.canvasEl().dom;
44756         ctx.fillStyle = this.bg_color;
44757         ctx.clearRect(0, 0, canvas.width, canvas.height);
44758         ctx.fillRect(0, 0, canvas.width, canvas.height);
44759         this.curve_data = [];
44760         this.reset();
44761         this.is_empty = true;
44762     },
44763     
44764     fileEl: function()
44765     {
44766         return  this.el.select('input',true).first();
44767     },
44768     
44769     canvasEl: function()
44770     {
44771         return this.el.select('canvas',true).first();
44772     },
44773     
44774     canvasElCtx: function()
44775     {
44776         return this.el.select('canvas',true).first().dom.getContext('2d');
44777     },
44778     
44779     getImage: function(type)
44780     {
44781         if(this.is_empty) {
44782             return false;
44783         }
44784         
44785         // encryption ?
44786         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44787     },
44788     
44789     drawFromImage: function(img_src)
44790     {
44791         var img = new Image();
44792         
44793         img.onload = function(){
44794             this.canvasElCtx().drawImage(img, 0, 0);
44795         }.bind(this);
44796         
44797         img.src = img_src;
44798         
44799         this.is_empty = false;
44800     },
44801     
44802     selectImage: function()
44803     {
44804         this.fileEl().dom.click();
44805     },
44806     
44807     uploadImage: function(e)
44808     {
44809         var reader = new FileReader();
44810         
44811         reader.onload = function(e){
44812             var img = new Image();
44813             img.onload = function(){
44814                 this.reset();
44815                 this.canvasElCtx().drawImage(img, 0, 0);
44816             }.bind(this);
44817             img.src = e.target.result;
44818         }.bind(this);
44819         
44820         reader.readAsDataURL(e.target.files[0]);
44821     },
44822     
44823     // Bezier Point Constructor
44824     Point: (function () {
44825         function Point(x, y, time) {
44826             this.x = x;
44827             this.y = y;
44828             this.time = time || Date.now();
44829         }
44830         Point.prototype.distanceTo = function (start) {
44831             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44832         };
44833         Point.prototype.equals = function (other) {
44834             return this.x === other.x && this.y === other.y && this.time === other.time;
44835         };
44836         Point.prototype.velocityFrom = function (start) {
44837             return this.time !== start.time
44838             ? this.distanceTo(start) / (this.time - start.time)
44839             : 0;
44840         };
44841         return Point;
44842     }()),
44843     
44844     
44845     // Bezier Constructor
44846     Bezier: (function () {
44847         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44848             this.startPoint = startPoint;
44849             this.control2 = control2;
44850             this.control1 = control1;
44851             this.endPoint = endPoint;
44852             this.startWidth = startWidth;
44853             this.endWidth = endWidth;
44854         }
44855         Bezier.fromPoints = function (points, widths, scope) {
44856             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44857             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44858             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44859         };
44860         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44861             var dx1 = s1.x - s2.x;
44862             var dy1 = s1.y - s2.y;
44863             var dx2 = s2.x - s3.x;
44864             var dy2 = s2.y - s3.y;
44865             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44866             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44867             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44868             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44869             var dxm = m1.x - m2.x;
44870             var dym = m1.y - m2.y;
44871             var k = l2 / (l1 + l2);
44872             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44873             var tx = s2.x - cm.x;
44874             var ty = s2.y - cm.y;
44875             return {
44876                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44877                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44878             };
44879         };
44880         Bezier.prototype.length = function () {
44881             var steps = 10;
44882             var length = 0;
44883             var px;
44884             var py;
44885             for (var i = 0; i <= steps; i += 1) {
44886                 var t = i / steps;
44887                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44888                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44889                 if (i > 0) {
44890                     var xdiff = cx - px;
44891                     var ydiff = cy - py;
44892                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44893                 }
44894                 px = cx;
44895                 py = cy;
44896             }
44897             return length;
44898         };
44899         Bezier.prototype.point = function (t, start, c1, c2, end) {
44900             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44901             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44902             + (3.0 * c2 * (1.0 - t) * t * t)
44903             + (end * t * t * t);
44904         };
44905         return Bezier;
44906     }()),
44907     
44908     throttleStroke: function(fn, wait) {
44909       if (wait === void 0) { wait = 250; }
44910       var previous = 0;
44911       var timeout = null;
44912       var result;
44913       var storedContext;
44914       var storedArgs;
44915       var later = function () {
44916           previous = Date.now();
44917           timeout = null;
44918           result = fn.apply(storedContext, storedArgs);
44919           if (!timeout) {
44920               storedContext = null;
44921               storedArgs = [];
44922           }
44923       };
44924       return function wrapper() {
44925           var args = [];
44926           for (var _i = 0; _i < arguments.length; _i++) {
44927               args[_i] = arguments[_i];
44928           }
44929           var now = Date.now();
44930           var remaining = wait - (now - previous);
44931           storedContext = this;
44932           storedArgs = args;
44933           if (remaining <= 0 || remaining > wait) {
44934               if (timeout) {
44935                   clearTimeout(timeout);
44936                   timeout = null;
44937               }
44938               previous = now;
44939               result = fn.apply(storedContext, storedArgs);
44940               if (!timeout) {
44941                   storedContext = null;
44942                   storedArgs = [];
44943               }
44944           }
44945           else if (!timeout) {
44946               timeout = window.setTimeout(later, remaining);
44947           }
44948           return result;
44949       };
44950   }
44951   
44952 });
44953
44954  
44955
44956